Python沙箱逃逸与模板注入(SSTI)(二)

​ 前几天总结了python沙箱逃逸,这篇我来总结一下flask服务端模板注入(Server Site Template Injection)。我们可以看到注入绕过的方法中有很多用到了Python沙箱逃逸的方法。
​ 学习模板注入最好要先有Jinja2模板的一些知识,简单的了解一下就行。模板注入是因为模板(Template)被二次渲染(render)或者是直接渲染格式化字符串,而这里面的输入又是用户可控的,这样用户就能输入例如{{1+1}}这样的变量,被服务当做代码执行就会返回2。

寻找可用函数/变量

这里 将 我们可用的函数/变量介绍了一下

request

​ 这个对象指的是当前请求对象,request.environ对象是一个与服务器环境相关的对象字典。值得一提的是,可以通过访问request.args.get request.cookies request.headers等访问我们的请求参数,这主要是用来绕过过滤。(比如不让输入某关键字,我们就可以通过cookies传入,然后从request.cookies获取即可)

config

​ config对象是当前配置对象,是一个类字典的对象,可以用config.items()来访问。而且它还有一些独特1的方法,from_envvar, from_object, from_pyfilefrom_pyfile可以读取指定python文件编译并加载进来。

使用沙箱逃逸方法

​ 当然,这篇博客的重点还是在于和沙箱逃逸结合起来,运用沙箱逃逸的方法成功的模板注入。我们也能看到,寻找可用的函数/变量很难寻找到getshell的方式,然而沙箱逃逸却有神效。

魔法函数

按照上一篇的解释,我们可以构造以下payload:

绕过

​ 现在很多模板注入都有限制,比如限制输入某些关键字,或者干脆直接限制输入某些字符。下面总结了一些绕过的方法。

限制输入关键字

request绕过关键字

​ 毫无疑问如果要用魔法函数,那么必须就要使用_
​ jinja2模板中有很多有用的内置过滤器,可以“看看这”这里我要介绍的是attrjoin这两个过滤器。{{request|attr("get")}}就相当于{{request.get}}{{request|attr(["_"*2,"class","_"*2]|join)}}相当于{{request.__class__}} 但是我们这样似乎还是无法过滤_ ,因为还是要输入才行呀。不过,我们前面讲过,被过滤的关键字和字符我们可以从request里取出,我们可以在get、post、header、cookies里传一个值,然后用request.cookies['var']获取
​ 理论上,我们可以用这种方法绕过任何关键字过滤。

[]绕过属性关键字

​ 除了上面的方法,如果request关键字也被禁用了的话,就无法用了。但是,其实在jinja2模板的官方文档中清楚地说明了,对象的属性不但可以用.来引用,而且还可以用[]。这说明,我们可以有了如下方式的绕过(+为拼接字符串)。
{{''.__class__}} = {{''['__cl'+'ass__']}}
​ 怎么样?这样的话很多关键字过滤都可以突破,而且即使attr和join两个过滤器也被禁用时仍然可以使用。

限制输入 []

​ 当我们在字典要取值时,需要用到[],如果被过滤了,我们仍然可以用“沙箱逃逸”那篇文章的方法使用getitem方法来绕过。
{{request.cookies.getitem("hh")}}

总结

​ SSTI其实内容还是python沙箱逃逸的扩展,如果对python精通的话理解会快很多。在我查资料的时候还发现了有师傅用魔法函数构造出file的write函数写文件然后用config.from_pyfile()函数加载,等等技巧偏向于具体情景,我这里就没有总结。

参考

https://www.freebuf.com/articles/web/98619.html
https://www.freebuf.com/articles/system/97146.html
https://0day.work/jinja2-template-injection-filter-bypasses/
https://bestwing.me/awesome-python-sandbox-in-ciscn.html
https://xz.aliyun.com/t/1589/#toc-17

One thought on “Python沙箱逃逸与模板注入(SSTI)(二)

发表评论

电子邮件地址不会被公开。 必填项已用*标注