SSTI(Server-Side Template Injection)服务端模板注入在CTF中并不是一个新颖的考点了,之前略微学习过,但是最近的大小比赛比如说安洵杯,祥云杯,太湖杯,南邮CTF,上海大学生安全竞赛等等比赛都频频出现,而且赛后看到师傅们各种眼花缭乱的payload,无法知晓其中的原理,促使我写了这篇文章来总结各种bypass SSTI的方法。
本篇文章从Flask的模板引擎Jinja2入手,CTF中大多数也都是使用这种模板引擎
官方文档对于模板的语法介绍如下
这里我们逐条来看
主要用来声明变量,也可以用于条件语句和循环语句。
用于将表达式打印到模板输出,比如我们一般在里面输入,,或者是字符串,调用对象的方法,都会渲染出结果
我们通常会用简单测试页面是否存在SSTI
表示未包含在模板输出中的注释
有和相同的效果
这里的模板注入主要用到的是和
用于返回对象所属的类
以字符串的形式返回一个类所继承的类
以米组的形式返回一个类所继承的类
返回解析方法调用的顺序,按照子类到父类到父父类的顺序返回所有类
获取类的所有子类
所有自带带类都包含方法,常用他当跳板来调用
会以字典类型返回当前位置的全部模块,方法和全局变量,用于配合使用
存在模板注入漏洞原因有二,一是存在用户输入变量可控,二是了使用不固定的模板,这里简单给出一个存在SSTI的代码如下
我们简单输入一个,返回了1,说明存在模板注入
而如果存在SSTI的话,我们就可以利用上面的魔术方法去构造可以读文件或者直接getshell的漏洞
如何拒绝这种漏洞呢,其实很简单只需要使用固定的模板即可,正确的代码应该如下
可以看到原封不动的输出了
这里从零开始介绍如何去构造SSTI漏洞的payload,可以用上面存在SSTI漏洞的做实验
目的:使用来获取内置类所对应的类
可以通过使用,,,等来获取
目的:拿到基类
用拿到基类
用拿到基类
用或者拿到基类
用拿到子类列表
在子类列表中找到可以getshell的类
在上述的第四步中,如何快速的寻找利用类呢
我们一般来说是先知晓一些可以getshell的类,然后再去跑这些类的索引,然后这里先讲述如何去跑索引,再详写可以getshell的类
这里先给出一个在本地遍历的脚本,原理是先遍历所有子类,然后再遍历子类的方法的所引用的东西,来搜索是否调用了我们所需要的方法,这里以为例子
我们运行这个脚本
可以发现基类的第128个子类名为的这个类有popen方法
先调用它的方法进行初始化类
再调用可以获取到方法内以字典的形式返回的方法、属性等值
然后就可以调用其中的popen来执行命令
但是上面的方法仅限于在本地寻找,因为在做CTF题目的时候,我们无法在题目环境中运行这个,这里用hhhm师傅的一个脚本直接去寻找子类
我们首先把所有的子类列举出来
然后把子类列表放进下面脚本中的a中,然后寻找这个类
又或者用如下的脚本去跑
tips:后面的各种方法都是利用这种思路寻找到可以getshell类的位置
在上面的例子中就是用的这个方法,payload如下
把上面脚本中的search变量换成
可以看到有5个类下是包含的,随便用一个即可
payload如下
因为python3和python2两个版本下有差别,这里把python2单独拿出来说
tips:python2的类型不直接从属于属于基类,所以要用两次
本方法只能适用于python2,因为在python3中类已经被移除了
可以使用dir查看file对象中的内置方法
然后直接调用里面的方法即可,payload如下
读文件
本方法只能用于python2,因为在python3中会报错,猜测应该是python3中被移除了还是啥的,如果不对请师傅们指出
我们把上面的脚本中的search变量赋值为,去寻找含有的类
后面如法炮制,payload如下
这里介绍python2和python3两个版本通用的方法
这种方法是比较常用的,因为他两种python版本都适用
首先是一个包含了大量内置函数的一个模块,我们平时用python的时候之所以可以直接使用一些函数比如,,就是因为这类模块在Python启动时为我们导入了,可以使用来查看调用方法的列表,然后可以发现下有,等的函数,因此可以利用此来执行命令。
把上面脚本search变量赋值为,然后找到第140个类含有他,而且这里的话比较多的类都含有,比如常用的还有等等,这也可能是为什么这种方法比较多人用的原因之一吧
再调用等函数和方法即可,payload如下
又或者用如下两种方式,用模板来跑循环
读取文件payload
然后这里再提一个比较少人提到的点
类在在内部定义了,然后模块包含有,也就是说如果可以找到类,则可以不使用,payload如下
总而言之,原理都是先找到含有的类,然后再进一步利用
我们可以用寻找这个类,可以直接RCE,payload如下
一开始我以为这种方法只能用于python2,因为我在本地实验的时候python3中无法找到直接含有os的类,但后来发现python3其实也是能够用的,主要是环境里面有这个那个类才行
我们把上面的脚本中的search变量赋值为os,去寻找含有os的类
后面如法炮制,payload如下
我们有时候可以使用flask的内置函数比如说,,甚至是内置的对象来查询配置信息或者是构造payload
我们通常会用查询配置信息,如果题目有设置类似,就可以直接访问或者获得flag
jinja2中存在对象
查询一些配置信息
构造ssti的payload
查询配置信息
构造ssti的payload
查询配置信息
构造ssti的payload
CTF中一般考的就是怎么绕过SSTI,我们学会如何去构造payload之后,还要学习如何去绕过一些过滤,然后下面由于环境的不同,payload中类的位置也是就那个数字可能会和文章中不一样,需要自己动手测一下
过滤了
在python中,可用以下表示法可用于访问对象的属性
也就是说我们可以通过,,来绕过点
使用访问字典的方式来访问函数或者类等,下面两行是等价的
以此,我们可以构造payload如下
使用原生JinJa2的函数,以下两行是等价的
以此,我们可以构造payload如下
这种方法有时候由于环境问题不一定可行,会报错,所以优先使用以上两种
过滤了和
flask中存在着内置对象可以得到请求的信息,可以用5种不同的方式来请求信息,我们可以利用他来传递参数绕过
payload如下
方式,利用传递参数
方式,利用传递参数
方式,利用传递参数
剩下两种方法也差不多,这里就不赘述了
这里先爆破,获取中含有chr的类索引
然后就可以用chr来绕过传参时所需要的引号,然后需要用chr来构造需要的字符
这里我写了个脚本可以快速构造想要的ascii字符
最后payload如下
过滤了
使用十六进制编码绕过,编码后为,编码后为
payload如下
这里甚至可以全十六进制绕过,顺便把关键字也一起绕过,这里先给出个python脚本方便转换
随便构造个payload如下
在上面的过滤引号已经介绍过了,这里不再赘述
首先要看关键字是如何被过滤的
如果是替换为空,可以尝试双写绕过,或者使用黑名单逻辑漏洞错误绕过,即使用黑名单最后一个关键字替换绕过
如果直接ban了,就可以使用字符串拼接的方式等方法进行绕过,常用方法如下
这里以过滤class为例子,用中括号括起来然后里面用引号连接,可以用号或者不用
随便写个payload如下
或者可以使用join来进行拼接
看到有师傅甚至用管道符加上方法来拼接的骚操作,也就是我们平时说的格式化字符串,其中的被替换
绕过,payload如下
绕过,但这种方法经过测试只能在python2下使用,payload如下
过滤init,可以用或替代
过滤config,我们通常会用获取当前设置,如果被过滤了可以使用以下的payload绕过
过滤了和
在python里面可以使用以下方法访问数组米素
也就是说可以使用和替代中括号,取列表的第n位
payload如下
调用魔术方法本来是不用中括号的,但是如果过滤了关键字,要进行拼接的话就不可避免要用到中括号,像这里如果同时过滤了class和中括号
可用绕过
或者可以配合一起使用
payload如下
这种同样是绕过关键字的方法之一
过滤了和
用替代了,使用判断语句进行dns外带数据
然后在ceye平台接收数据即可
如果上面的方法不行的话,可以考虑使用盲注的方式,这里附上p0师傅的脚本
我们上面之所以要dnslog外带数据以及使用盲注,是因为用会没有回显,这里的话可以使用来做一个标记使得他有回显,比如,payload如下
这里我基于上面绕过黑名单各种方法的组合,对CTF中用到的一些方法和payload再做一个小的总结,不过其实一般来说,只要不是太偏太绕的题,上面的方法自行组合一下都够用了,下面只是作为一个拓展
这里顺便给一个不常见的方法,主要是找到的方法,第一个是参数0,第二个为要读取的文件名,payload如下
使用十六进制绕过后,payload如下
之前安恒二月赛在y1ng师傅博客看到的一个payload,原理并不难,这里使用了绕过点,绕过,payload如下
有一些题目我们不并需要去getshell,比如flag直接暴露在变量里面了,像如下这样把文件加载到flag这个变量里面了
我们就可以通过是导入主函数去读变量,payload如下
这种方法是从安洵杯2020 官方Writeup学到的,我们直奔主题看payload
这里的绕过和绕过上面已经说过了这里不赘述
然后这里的用测了一下发现是个方法
然后用他直接调用发现可以直接执行os命令,测了一下发现也可以用,又学到了一种新方法,只能说师傅们tql
回到正题,这里使用了Unicode编码绕过关键字,下面两行是等价的
知道了这两点之后,那个官方给的payload就很明朗了,解开编码后如下
然后我这里顺便给个Unicode互转的php脚本
这种方法是在太湖杯easyWeb这道题目学到的,上面所说的过滤双大括号,在一些特定的题目可以魔改,比如说这道题由于有个字符规范器可以把我们输入的文本标准化,所以可以使用这种方法
可以在Unicode字符网站寻找绕过的字符,直接在网址搜索,就会出现类似的字符,就可以找到和了,网址:https://www.compart.com/en/unicode/U+FE38
payload如下
还可以使用中文的字符魔改
payload如下
因为水平和文章篇幅有限,可能还有一些bypass方法没有提到,还有就是CTF中也不只考Jinja2这种模板,还有另外的Twig模板,smart等模板,这些就等以后有必要再更吧,最后就是有不足之处请各位师傅指出
相关实验:Flask服务端模板注入漏洞
https://p0sec.net/index.php/archives/120/
https://www.jianshu.com/p/a736e39c3510
https://www.redmango.top/article/43
https://xz.aliyun.com/t/8029
https://xz.aliyun.com/t/7746