ssti入门
python ssti的学习(以flask为例)
起因
最近因为参加了newstar的新生赛,从第三周到第五周都有python ssti的题目,所以就简单的学习了一下ssti
flask入门
Flask 是一个 Python 实现的 Web 开发微框架,是一个使用Python编写的轻量级web应用框架,其WSGI工具箱采用Werkzeug,模板引擎则使用Jinja2。更多开发细节可以参考上面的flask中文文档。
ssti入门
SSTI 即服务端模板注入攻击(Server-Side Template Injection),服务端接受用户输入,将其作为 Web 应用模板的一部分,渲染编译后执行了恶意内容,导致敏感信息泄露、代码执行等。
原理
ssti服务端模板注入,ssti主要为python的一些框架 jinja2、mako、tornado、django,PHP框架smarty twig,java框架jade 、velocity等等使用了渲染函数时,由于代码不规范或信任了用户输入而导致了服务端模板注入,模板渲染其实并没有漏洞,主要是程序员对代码不规范不严谨造成了模板注入漏洞,造成模板可控。本文着重对flask模板注入进行浅析。
模板引擎
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。但是往往新的开发都会导致一些安全问题,虽然模板引擎会提供沙箱机制(相当于一个白名单,你只能用这个白名单里面的东西),但同样存在沙箱逃逸技术来绕过。
模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的视觉表现(HTML代码)这项工作的一种实现手段,而这种手段不论在前端还是后端都有应用。
通俗来说,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。
实例
1 |
|
可以看到源代码这里直接将get方法的name变量输出了,由于前端是由jinja进行模板渲染的而在jinja中由{{}}包裹的会被当做变量输出,所以当我们输入{{7*7}}时就会输出49
ssti前置知识
在学习SSTI注入之前,我们首先需要了解一些python的魔术方法和内置类
class
__class__
用于返回该对象所属的类
base
__base__
用于获取类的基类(也称父类)
mro
__mro__
返回解析方法调用的顺序。(当调用_mro_[1]或者-1时作用其实等同于_base_)
subclasses()
__subclasses__()
可以获取类的所有子类
常用过滤器
1 |
|
其实就是可以实现一些简单的功能,比如attr()过滤器可以实现代替.
,join()可以将字符串进行拼接,reverse可以将字符串反置等等
1 |
|
ssti语句构造
前面讲了这么多,终于可以开始了
首先我们需要拿到当前类,也就是使用**””._class_**
第二步拿到基类,也就是使用_base__获得,也可以使用__mro__获得(我学的时候就是因为不知道为什么一会用base一会用mro就觉得很烦躁…….)
第三步,拿到基类的子类,用__subclasses__()
,拿到子类过后我们就可以寻找可以用于执行命令或者读取文件的类,大多数利用的是os._wrap_close
这个类
接下来就可以利用os。_wrap_close
,这个类中有popen
方法,我们去调用它
首先
先调用它的__init__方法进行初始化类
然后调用globals获取到方法内以字典的形式返回的方法,属性等
到这一步就可以rce了
1 |
|
还有一个比较厉害的模块,就是__builtins__
,它里面有eval()
等函数,我们可以也利用它来进行RCE
它的payload是
1 |
|
常见的绕过
当然前面讲的只是在没有任何过滤的情况下payload才这样写,在平时做题的时候或多或少都会有一些过滤
.被过滤
当.被过滤后意味着我们就不能使用.__class了
1 |
|
我们就可以这样来绕过.
_被过滤
当_被过滤时,有以下几种方法绕过
1 |
|
绕过[]
这种情况我还没遇见过,就参考的大佬的文章
这个时候可以使用__getitem__
魔术方法,它的作用简单说就是可以把中括号转换为括号的形式,举个例子
1 |
|
绕过{{}}
有时候为了防止SSTI,可能程序员会ban掉{ {} },这个时候我们可以利用jinja2的语法,用{ %%}来进行RCE,举个例子
我们平常使用的payload
1 |
|
也可以借助for循环和if语句来执行命令
1 |
|
绕过单双引号
当单引号和双引号被ban时,我们通常采用request.args.a
,然后给a赋值这种方式来进行绕过,举个例子
1 |
|
绕过args
当使用args的方法绕过'
和"
时,可能遇见args被ban的情况,这个时候可以采用request.cookies
和request.values
,他们利用的方式大同小异,示例如下
1 |
|
注:
如果只是过滤了单双引号的其中一个,那么就可以用另外一个来代替
绕过数字
有时候可能会遇见数字0-9
被ban的情况,这个时候我们可以通过count来得到数字,举个例子
1 |
|
绕过关键字
有时候可能遇见class
、base
这种关键词被绕过的情况,我们这个时候通常使用的绕过方式是使用join拼接从而实现绕过,举个例子
关键字绕过还可以用16进制和unicode编码进行绕过