最关键的思路
wireshark足够强,可以直接过滤出数据,然后可以用各种形式导出,可以直接导出成capcng或者给txt(可能会限制长度)也可以直接导出成json
SSIT
模板注入攻击(Server-Side Template Injection)指未经处理就将用户输入的内容作为web应用模板内容的一部分.
1. SSTI(模板注入)漏洞(入门篇) - bmjoker - 博客园
flask对应后端代码
1 | from flask import Flask,request,render_template_string,Response,session |
高危代码!: return render_template_string(f"Welcome {data}")
直接将用户输入的东西渲染为模板返回,这样也是高危
1 | data = request.args.get("name","Guest") |
本次环境中漏洞执行前提SSTI 攻击原理
- 模板引擎的工作方式:Flask 使用 Jinja2 模板引擎,它会解析模板中的特殊语法(如
{{ }}
)并执行其中的表达式。 - 用户输入直接拼接:当用户控制的输入 (
data
) 被直接插入模板字符串时,攻击者可以注入模板语法。 - 执行任意代码:通过精心构造的输入,攻击者可以访问 Python 内置对象和方法,最终实现远程代码执行。
flask中的SSIT高危代码命令执行
1 | {{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['os'].popen('cat /etc/passwd').read()}} |
第一个Payload分析
1 | {{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['os'].popen('cat /etc/passwd').read()}} |
分步解析:
''.__class__
获取空字符串的类对象,即<class 'str'>
.__mro__[1]
访问方法解析顺序(MRO),获取父类<class 'object'>
(MRO返回元组:(str, object),索引1是object).__subclasses__()
获取object的所有子类列表(约200-400个内置类)[X]
选择特定索引的子类(攻击者需要找到包含os模块的类,常见目标如<class 'os._wrap_close'>
).__init__.__globals__
访问该类的初始化方法的全局命名空间字典
(包含导入的模块和全局变量)['os']
从globals中获取os模块引用.popen('cat /etc/passwd').read()
通过os模块执行系统命令并读取结果
实际攻击示例:
如果<class 'subprocess.Popen'>
在索引123:
1 | {{''.__class__.__mro__[1].__subclasses__()[123]('cat /etc/passwd',shell=True,stdout=-1).communicate()[0]}} |
第二个Payload分析
1 | {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen('id').read()}}{%endif%}{%endfor%} |
分步解析:
().__class__
获取空元组的类<class 'tuple'>
.__base__
获取基类<class 'object'>
(与__mro__[1]
等效).__subclasses__()
获取所有子类列表- 循环遍历
{% for x in ... %}
遍历每个子类 - 条件判断
{% if "warning" in x.__name__ %}
查找类名包含”warning”的类
(如<class 'WarningMessage'>
或<class 'catch_warnings'>
) x()._module.__builtins__
实例化该类,访问其模块的builtins字典
(包含所有内置函数和模块)['__import__']('os')
动态导入os模块.popen('id').read()
执行系统命令
为什么选择warning相关类?
因为这些类通常会导入__builtins__
,且在生产环境中普遍存在,可靠性高。
关键技术点
- 对象继承链利用
通过__class__
、__mro__
、__base__
、__subclasses__()
等方法遍历Python对象继承体系 - 模块导入技巧
通过__globals__
或__builtins__
获取危险模块(os, subprocess等) - Jinja2特性
模板中的{{}}
会执行表达式,{%%}
可以控制逻辑流程 - 沙箱逃逸
绕过Jinja2的沙箱限制,访问底层Python环境
教训
分离数据包
发现很大的pacapng包的时候不要直接开始尝试解包,先看看数据包中是否有包含什么东西
1 | binwalk -e ./NetHttP.pacpng |
查看POST的传输数据
1 | http.request.method == "POST" |
选择reponse中的内容
1 | http contains "Welcome rce" |
解题过程
wireshark打开数据包,发现超级大,连加载都很难,过滤出http,找到第一个包,发现存在源码泄露和框架注入的问题
继续分析数据包发现关键的东西
1
2
3
4
5
6GET /rce?name=%7B%7Blipsum.__globals__.__builtins__.eval(%22__import__('os').popen('echo%20aWYgWyAkKGNhdCAvYXBwL3NlY3JldC9tdy9tNXxiYXNlNjQgLXcgMHwgYXdrIE5SPT0xIHwgY3V0IC1jIDEpID09ICdxJyBdOyB0aGVuIGVjaG8gInJjZSI7Zmk=%20%7C%20base64%20-d%7Cbash').read()%22)%7D%7D HTTP/1.1
Host: 192.168.111.132:8989
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive关键url
1
aWYgWyAkKGNhdCAvYXBwL3NlY3JldC9tdy9tNXxiYXNlNjQgLXcgMHwgYXdrIE5SPT0xIHwgY3V0IC1jIDEpID09ICdxJyBdOyB0aGVuIGVjaG8gInJjZSI7Zmk=
base64解码后是
1
if [ $(cat /app/secret/mw/m5|base64 -w 0| awk NR==1 | cut -c 1) == 'q' ]; then echo "rce";fi
是一个linux bash脚本
1
如果 [ $(读取 /app/secret/mw/m5 文件 | 用base64编码且不换行 | 取第一行 | 截取第一个字符) 等于 'q' ]; 那么 显示 "rce"; 结束
这是一个最关键的点,检查后面多个包都会发现这个格式,据此判断实际上就是为了获取
/app/secret/mw/m5
,也就是flag很有可能就在这个文件中
接下来的思路就应该是如何把所有的这个包找出来然后统一解base64然后来获取所有的
== 'q'
的东西,但是在这个地方我的思维出现了插曲(ctrl点击跳转插曲). 按照wp思路就是筛选出数据包之后导出,然后使用vscode的正则表达式之类的选择方法来进行数据清洗,至于最关键的思路筛选出http的回复包含
Welcome rce
的数据包1
http contains "Welcome rce"
导出筛选的数据
然后就是在vscode中正则表达式选中所有的符合的加密式子,使用以下快捷键全部选中复制
1
Alt + Enter
然后cybercooker可以全部一块解码
筛选对应的内容出来再次接base64然后发现一个这个no flag,无法解base64
想起来
tcp.stream eq 13
还有一个密钥,但是加密了,tcp.stream eq 1
有个私钥解密!!!!!!!!!!!!需要注意的是: 这里的私钥解密方式不是直接把这个rsa私钥导入wireshark然后输密码,而是应该直接解密!,因为wireshark中并没有被加密的tls数据!!!!!!!!!!!
可以通过这个方式生成一个解密的
2.pem
1
$ openssl rsa -in 1.pem -out 2.pem
可以通过私钥来获取rsa的所有数据SSL在线工具-在线RSA私钥解析-在线RSA私钥提取-SSLeye官网
其实cyberchef更简单,但是似乎需要先base64解密(对于base64解密的rsa密文)
在 CyberChef 的 RSA Decryption 操作中,
Encryption Scheme
选项决定了 RSA 解密时使用的填充方式(Padding Scheme),不同的填充方式会影响安全性和适用场景。以下是详细解析:
CyberChef 中的 RSA Encryption Scheme 选项
选项 正式名称 填充方式 特点 适用场景 PKCS#1 v1.5 RSAES-PKCS1-v1_5 固定格式填充 较旧,可能存在漏洞 兼容旧系统 OAEP (SHA-1) RSAES-OAEP (SHA-1) 随机化填充 + SHA-1 更安全,但SHA-1较弱 一般用途(逐步淘汰) OAEP (SHA-256) RSAES-OAEP (SHA-256) 随机化填充 + SHA-256 现代标准,推荐使用 安全通信、HTTPS Raw 无填充(教科书式RSA) 直接加密明文 极不安全,仅用于研究 绝对不要用于生产环
插曲
首先要说明实际上是没有意义的,因为wireshark可以将过滤的内容全部导出
考虑到存在很多的base64加密,所以我决定使用python来解析pcapng包,然后使用python直接进行base解密输出,
首先就需要解析出http跟踪流的数据,然后在进行审计
但是在这个34mb的数据包面前,会花费大量的资源和时间,此处不可行,但是总结了以下的一些操作方式
tshark + subprocess
使用python执行系统命令,对输出的调度结果直接进行解析之后处理数据
✅ 优点: tshark就是wireshark的命令行版本,速度极快,对这种大文件的处理非常友好
❌ 缺点: 目前并没有找到好的参考文档,输出的内容会全部先保存到内存中,而且需要全部输出完才能够继续让python处理,耗费极大的资源
每一次调用命令(重新筛选)都需要重新对文件进行加载(否则需要用变量存储,超级大),不是很方便
tshark无法直接返回python对象
tshark打印http跟踪流
1 | import subprocess |
⚠️注意:
- powershell似乎并不支持应用程序路径中有空格,但是cmd支持,不过python调用的环境应该是属于powershell环境,所以对于系统环境中的变量路径
C:\Program Files\Wireshark\tshark.exe
时常会报找不到文件的错误 - 直接在cmd中使用这个操作的前提还是需要放到环境变量中去
tshark_path = r'C:\Program Files\Wireshark\tshark.exe'
必须要这样子的方式直接指明带空格的应用才能找到文件
pyshark
pyshark是python版本的wireshark,可以直接进行处理流量包
性能综合之下会好很多,但是对于需要遍历打印所有的http流也需要耗费很长的时间
scapy
处理大文件完全无法使用,性能太差了