首页
社区
课程
招聘
KCTF2026: 第十题:卯时·曦光初现
发表于: 1天前 348

KCTF2026: 第十题:卯时·曦光初现

1天前
348

Flag: flag{ac2aec65-aeb5-4760-9d90-228064e4db4b}

程序是一个小型 REPL。RTTI 残留了 interpreter 命名空间下的类名:值类
Number / Object,以及 AST 节点类 AssignLit / GetField / SetField /
BinOp。每行一条语句:

空行会重新出提示符;非法语句打印 invalid syntax!;执行失败打印
runtime error!程序没有任何“打印数值”的功能 —— 唯一反馈就是成功/报错
(这一点很关键:它逼着我们把整条利用都在 env 内部算完)。

所有值以 NaN-boxing 方式存放在一个 256 槽的全局 env[] 里:

数组内容被扁平化存进一块固定的 0x10000 字节 bump 缓冲区 arena
(初始化时 operator new[](0x10000))。每个存进去的字都会和一个启动时从
/dev/urandom 读取的 8 字节 key 做 XOR 混淆(放在 .bss 固定偏移)。一个数组
序列化后的布局:

env[](256×8)紧挨在 arena 控制块之前;arena 本身是普通堆缓冲区,其后就是进程
的其余堆区。

BinOp::execute 计算 env[dst] op env[src] 后把结果写回,既不截断到 48 位、
也不重新检查 tag
(只拒绝 0x1337操作数)。于是算术运算可以合成出任意
64 位字 —— 关键是构造出一个伪数组头 0xbeef<<48 | BIGLEN

SetField 把这个字写到某个已有元素上,该元素就变成了一个长度任意的伪嵌套
数组
。再对它取下标(GetField/SetField)就会读/写
arena_elem_base + 8 + k*8k < BIGLEN),即对 arena 及其后堆区的越界读写
(下标遍历会逐字扫过中间内存,所以可达范围是“连续映射的堆” —— 已经足够。)

其余机制(256 槽上限、0xbeef 头校验、每层下标边界、XOR key)都是缓解措施;
唯一的裂缝就是 BinOp 未掩码的结果。

每个命令 AST 对象都是带 vtable 的堆对象main() 会先调用
obj->vtable[0](env)(execute),在 execute 返回之后再调用
obj->vtable[2]()(析构,即 call [rax+0x10],此时 rdi=obj)。而一个
SetField 对象会稳定地复用在 arena+0x10430(0x30 大小的 tcache 复用;该相对
偏移与 ASLR 无关)。

全程无需把地址外带给自己:one_gadget 地址与 arena 指针都在 env[] 里算出,越界写
只负责把它们放到位。

稳定性≈100%(配合小重试循环)—— 唯一的失败来自约 0.2% 的概率:随机 key 恰好让某个
遍历到的堆字解混淆后高 16 位等于 0xbeef(被误判成嵌套头)。本地 8/8,远程一
把即中。

本地运行直接用加载器(可移植,无需 patchelf):
./ld-2.27.so --library-path . ./pwn。其中 ld-2.27.so / libstdc++.so.6 /
libgcc_s.so.1 / libm.so.6 取自官方 Ubuntu-18.04 软件包
libc6 2.27-3ubuntu1.6gcc-8 …~18.04);libc 的 build-id 与题目所给
libc-2.27.so 完全一致。

语法 节点 效果
$N=<num> AssignLit env[N] = num & 0xffffffffffff
$N=[a,b,…] AssignLit env[N] = 数组句柄(元素可嵌套)
$N=$M[i][j]… GetField env[N] = env[M] 取下标(读)
$N[i][j]…=$M SetField env[M] 写入 env[N] 的下标处(写)
$N op $M BinOp env[N] = env[N] op env[M],`op ∈ + - * / % ^

冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

上传的附件:
收藏
免费 0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回