ssti例会练习
1.[NISACTF 2022]midlevel

打开题目可以看到是一个获得ip的界面,又看到最下面有个Smarty,因此可以确定是php的Smarty模板,可以ssti,但看了看没有可以注入的地方,这时候想到Smarty模板可以在XFF里注入,所以我们在这里用XFF。
看,这里变49了,这就代表着可以注入。
怎么注入呢?
Smarty模板可以注入的方式有几种:
- {php}{/php}
- {literal}
<script language="php"></script> - {if}{/if}
- {}直接执行命令
先用{$smarty.version}看版本

是smarty3.1.30,像这样{php}{/php}就用不了了,因为只能在smarty2和smartyBC(2-3的一个过渡版本)使用。
又因为php版本是7.3.11,所以<script language="php"></script>用不了,那现在就两种方法,试试{if}{/if}

有回显,可以。再试试{}

也可以,那直接cat /flag了
flag在此NSSCTF{e07f7a5e-6acf-4968-8e68-91c326bf5088}
2.[单身杯]ezzz_ssti

已经很明显了,可以看看{{7*7}}
更明显了,可以ssti
果然可以,在这里我用的是jinja2模板自带的函数,就直接有了globals的属性,比起从基类找感觉更方便。继续
嗯?坏了,有长度限制。改用config.update
Flask 框架中存在 config 全局对象,用来保存配置信息。
config 对象实质上是一个字典的子类,可以像字典一样操作。
因此要更新字典,我们可以使用 Python 中字典的 update() 方法。
用 update() 方法 + 关键字参数更新字典
1 | |
所以我们就可以用这种方式在config全局变量中分段保存payload,以绕过长度限制。
所以payload为
1 | |
一步一步把每段payload传上去最后得到flag
ctfshow{223641b5-a099-4816-8ac2-475de5185ae4}
3.[VNCTF 2025]学生姓名登记系统

这是一个和上面一样的ssti.{{7*7}}有这个
但想继续用上面的方法不行了
所以只能从基类开始 {{''.__class__.__base__}}
坏了,有长度限制,数了一下,最多23个字符,上一道题的config.update就用不了了,太长了。
所以换一种方法,因为这道题可以多行解析,所以用海象表达式
构建pyload
1 | |

这样就有了flag
有需要注意的几个点,这道题过滤了open、eval、import等,可以['op'+'en']绕过
还有就是,__subclasses__()[]这部分需要爆破,可以写python脚本,也可以手动二分法找到自己要用的类。
flag为VNCTF{THE_AOOO-IlN3_6ottL3-fR@M3wORk-l5_W0Nd3rFUL226}