2026 SUCTF Pwn 方向 WriteUp:堆溢出、内核 Page Cache 篡改与 V8 引擎利用
字数 6437
更新时间 2026-04-02 13:46:46

2026 SUCTF Pwn方向WriteUp教学文档

目录

  1. 比赛与题目概述
  2. 2.41heap(SU_minivfs)
  3. core(SU_Chronos_Ring)
  4. IOT
  5. 老版本J2V8学习(SU_BOX)
  6. webpwn(evbuffer)

1. 比赛与题目概述

本教学文档基于2026年SUCTF(SUCTF 2026)Pwn方向的官方WriteUp撰写。该比赛Pwn方向包含多个高难度题目,涉及用户态和内核态的多种漏洞利用技术,包括堆利用、内核Page Cache篡改、V8引擎利用以及新型通信架构的漏洞利用。WriteUp提供了详细的逆向分析、漏洞原理、利用链构建和最终利用脚本(exp)的解释。

2. 2.41heap(SU_minivfs)

2.1 题目背景与环境

  • 题目名称: SU_minivfs
  • 核心漏洞: 堆溢出(Off-by-null)
  • 环境: glibc 2.41,启用seccomp,程序有正常退出路径。
  • 程序功能: 模拟一个带“鉴权”的简易文件系统,支持touchrmcatwritestatls等命令,分别对应堆内存的mallocfreereadwrite等操作。

2.2 逆向分析与关键点

  1. 鉴权绕过:

    • 所有敏感操作都需要一个auth参数。
    • 逆向分析发现,程序对文件路径进行FNV-1a哈希和混淆后,仅与用户输入的auth进行简单比较 (v7[1] == mixer_result)。
    • 由于哈希值可预测,因此“鉴权”并非真正的安全机制,可被轻易绕过。WriteUp中通过本地计算正确的auth值来自动化所有命令。
  2. 漏洞定位:

    • cat命令: 直接按记录的长度输出堆块内容,成为一个稳定的堆信息泄漏原语,可用于泄露libc和堆地址。
    • write命令: 当写入数据大小恰好等于堆块容量(cap)时,补零操作(\0)会写到堆块边界之外,形成一个Off-by-null漏洞。这可以污染下一个堆块的size字段的PREV_INUSE位。

2.3 利用链构建(House of Einherjar -> FSOP)

利用思路围绕Off-by-null展开,目标是最终通过FSOP(File Stream Oriented Programming)劫持控制流。

  1. 堆布局与泄漏阶段:

    • 首先创建一系列文件(堆块),利用cat命令泄漏出libc基址和堆地址,为后续利用做准备。
  2. 利用阶段 - 堆重叠构造:

    • 创建两个大小为0x4f8的堆块(对应/aj/ap),使其最终在内存中大小为0x500
    • /aj写满数据,触发Off-by-null,将/ap堆块的PREV_INUSE位清零。
    • /aj的用户数据区预先布置一个伪造的堆块(fake chunk)。
    • free(“/ap”)时,glibc会因PREV_INUSE=0而尝试向前合并,从而将我们的伪造堆块合并进来,造成堆块重叠。这相当于获得了use-after-free (UAF) 能力。
  3. 利用阶段 - 控制流劫持:

    • 利用堆重叠后的读写能力,进行largebin attack等操作,最终目标是将_IO_list_all全局指针改写为指向我们伪造的FILE结构体链。
    • 伪造的FILE结构体链放置在可控的堆内存中(例如/ar/ak文件对应的堆块):
      • FILE结构体本身
      • 伪造的_wide_data
      • 伪造的虚函数表(vtable)
      • ROP链(或Shellcode)及路径字符串
    • 通过setcontext gadget进行栈迁移,最终执行ORW(Open-Read-Write)链来读取flag
  4. 最终利用与Flag获取:

    • 首次ORW读取./flag可能得到假flag(障眼法)。
    • 修改利用脚本,先通过getdents系统调用(或类似方法)列出目录,找到真正的flag文件路径,再进行读取。

2.4 关键技术点总结

  • 漏洞挖掘: 关注边界条件,write在写满时的行为是突破口。
  • 利用原语: Off-by-null -> House of Einherjar -> 堆重叠 -> UAF。
  • 劫持手法: 结合largebin attack与FSOP,利用程序正常退出时的_IO_flush_all_lockp触发。
  • 绕过技巧: 识别并绕过虚假的鉴权机制。

3. core(SU_Chronos_Ring)

3.1 题目背景

  • 题目类型: 内核驱动题。
  • 核心漏洞: 通过驱动接口篡改文件的Page Cache。
  • 目标: 普通用户权限下,通过利用驱动漏洞,让root进程执行被篡改的脚本,从而读取高权限的/flag文件。

3.2 逆向分析与接口梳理

驱动通过/dev/chronos_ring设备文件提供一系列ioctl命令:

ioctl 命令码 作用 关键点
0x1001 创建 ring buffer 分配 0x1000 大小的内核缓冲区
0x1002 鉴权 通过后设置 ctx->flags
0x1003 pin 用户页 pin_user_pages_fast
0x1004 绑定文件页 fget + read_cache_page
0x1005 创建 view 为 buffer 创建一个视图(view)
0x1007 向 buffer 写数据 向 ring buffer 中 memcpy 数据
0x1008 把 buffer 内容拷到 view 本题核心原语
0x1009 读状态 调试辅助
0x100a 销毁 buffer 清理

核心利用链
0x1004 (绑定文件页) -> 0x1005 (创建view) -> 0x1008 (buffer内容拷贝到view)。

  • 0x1004 将指定文件(如/tmp/job)的某一页(page cache)关联到驱动上下文。
  • 0x1005 为该缓冲区创建一个视图(view),作为数据写入的目标。
  • 0x1008 将ring buffer中的数据拷贝到上一步创建的view中,实质上是直接修改了文件在内存中的page cache。拷贝后会调用set_page_dirty标记页面为脏。

3.3 漏洞利用流程

  1. 鉴权绕过:

    • 0x1002命令的鉴权逻辑使用了kfree函数地址的高位经过变换后的值作为种子的一部分。
    • 由于kfree地址高位相对固定,熵较低,可以暴力枚举可能的种子值来通过鉴权。
  2. 目标文件锁定:

    • 驱动对0x1004绑定的文件名有校验,只允许操作/tmp/job文件。
    • 题目提供了一个root守护进程,每3秒以root权限执行一次/tmp/job
  3. 完整攻击链:

    1. 0x1001 创建缓冲区。
    2. 0x1002 暴力枚举通过鉴权。
    3. 0x1007 向缓冲区写入恶意Shell脚本(如#!/bin/sh\ncat /flag > /tmp/flag\nchmod 666 /tmp/flag)。
    4. 0x1003 pin一个用户页(为view链做准备)。
    5. 0x1004 绑定/tmp/job文件的第0页。
    6. 0x1005 为缓冲区创建view。
    7. 0x1008 执行拷贝,将缓冲区中的Shell脚本写入/tmp/job文件的page cache。
    8. 等待root进程执行被篡改的/tmp/job
    9. 普通用户读取/flag(或由脚本写入的/tmp/flag)。

3.4 关键技术点总结

  • 漏洞本质: 滥用驱动提供的“将缓冲区数据写入文件缓存页”的原语,实现对只读文件的篡改
  • 利用场景: 结合系统的特权进程(root helper)定期执行特定脚本的机制,将权限提升转化为文件篡改。
  • 内核利用特点: 本题目无需复杂的内核ROP,关键在于理解驱动接口的组合语义和内核Page Cache机制。

4. IOT

WriteUp中提及此题目将单独撰写复现文章,因此链接内容未提供详细信息。

5. 老版本J2V8学习(SU_BOX)

5.1 题目背景

  • 题目类型: JavaScript引擎漏洞利用(V8)。
  • 环境: Java应用程序调用老版本的J2V8库(V8 9.3.345.11,对应Chrome 93)。
  • 目标: 在受限的J2V8环境中实现任意代码执行。

5.2 环境搭建与调试

  • 题目提供了Java App和J2V8库(libj2v8-linux-x86_64.so)。
  • 为了辅助利用,WriteUp修改了Java App源码,增加了强大的调试原语:
    • log(x): 打印输出。
    • addr(obj): 返回V8对象的原生地址。
    • read64(addr) / read32(addr): 任意地址读。
    • dump(addr, size): 内存dump。
    • write64(addr, value): 任意地址写(关键原语)。
    • jgc(): 手动触发Java GC。

5.3 漏洞利用思路(CVE-2021-38003相关)

由于是旧版本V8,参考了历史漏洞的利用思路,但需适配J2V8环境。

  1. 制造“TheHole”:

    • 利用JSON.stringify()一个特殊构造的异常对象,得到一个可触发后续Map状态破坏的“令牌”。
  2. 破坏Map并制造OOB(Out-Of-Bounds)访问:

    • 利用上一步得到的对象进行操作,破坏某个Map的状态,进而污染一个数组的对象头,使其获得越界读写的能力。
    • 在远程环境下,这个OOB窗口可能较小且不稳定,需精确控制。
  3. 定位可执行内存:

    • 创建一个WebAssembly.Instance对象。WASM实例会分配一块具有RWX(可读、可写、可执行) 权限的内存页用于存放编译后的代码。
    • 使用获得的OOB能力,读取wasm实例对象内部的指针,泄露出这块RWX内存的地址。
  4. 劫持ArrayBuffer/DataView:

    • 创建一个ArrayBuffer和一个DataView
    • 利用OOB能力,修改ArrayBufferbacking store指针(在对象偏移+0x28处)和DataViewdata pointer(在对象偏移+0x30处),将它们都指向泄露出的RWX内存页。
  5. 写入并执行Shellcode:

    • 通过被篡改的DataViewsetUint8等方法,将Shellcode直接写入RWX内存页。
    • 调用WASM实例的函数,执行Shellcode。

5.4 关键技术点总结

  • 环境差异: J2V8与标准d8环境在稳定性、对象布局、可用gadget上存在差异,不能直接套用公开PoC。
  • 利用链拆分: 采用分步策略:一个OOB窗口用于读地址(如RWX),另一个用于写指针(如backing store)。
  • 稳定利用: 远程环境脆弱,对象创建顺序和布局需要精细调整。

6. webpwn(evbuffer)

6.1 题目背景与架构

  • 程序功能: 一个同时监听TCP和UDP端口的服务。
  • 漏洞类型: 基于libevent库的漏洞利用,涉及全局变量溢出和回调函数劫持。
  • 保护机制: 全保护开启(ASLR, PIE, NX等)。

6.2 漏洞分析

  1. 信息泄漏:

    • TCP连接: 在处理TCP连接时,程序在回复数据包中会包含对端的地址信息,其中hostname字段未初始化,残留了栈上的数据,可能包含libc和PIE地址。
    • UDP通信: 通过发送特定报文,可以触发类似的未初始化数据读取,泄漏PIE基址。
  2. 关键溢出漏洞:

    • 在处理UDP数据包的某个函数中,存在一个memcpy,其目标指针最终指向一个全局变量g_udp_ctx附近。
    • 对输入IP地址的校验存在缺陷,可通过\x00截断绕过长度检查。
    • 因此,通过发送精心构造的长UDP数据包,可以溢出覆盖g_udp_ctx结构体中的关键字段,特别是其内部指向bufferevent结构体的指针。

6.3 利用链构建(伪造libevent结构体)

  1. 劫持指针:

    • 利用UDP溢出漏洞,覆盖g_udp_ctx中存储的bufferevent指针,使其指向我们可控内存区域(如.bss段)伪造的bufferevent结构。
  2. 理解libevent触发路径:

    • 后续代码会调用bufferevent_get_output(bev)
    • 该函数内部会访问bev->output(一个evbuffer指针)。
    • 因此,我们只需在伪造的bufferevent中,将output字段指向另一个我们伪造的evbuffer结构。
  3. 利用evbuffer_add_reference的清理回调:

    • 在伪造的evbuffer中,设置其last指针指向一个伪造的evbuffer_chain链表。
    • evbuffer_chain结构体包含一个cleanup回调函数指针和cleanup_arg参数。
    • evbuffer_add_reference函数在插入新数据前,会清理尾部空的chain,即调用chain->cleanup(chain->buffer, chain->buffer_len, chain->cleanup_arg)
    • 通过连续伪造多个evbuffer_chain,可以构造一个连续的函数调用链
  4. 构造ORW链:

    • 将伪造的cleanup函数设置为openreadwrite等库函数地址。
    • 精心设置cleanup_arg作为参数,构造以下调用序列:
      1. open(“flag”, 0, 0) -> 返回文件描述符fd
      2. read(fd, buf, 0x40) -> 读取flag到缓冲区。
      3. write(conn_fd, buf, 0x40) -> 将flag写回TCP连接。
      4. (可选)继续读取flag剩余部分。

6.4 利用步骤

  1. 通过TCP连接泄漏libc地址。
  2. 通过UDP泄漏PIE地址。
  3. 在.bss段等可写区域布置伪造的buffereventevbufferevbuffer_chain结构体链。
  4. 发送恶意UDP包,溢出覆盖g_udp_ctx->bev指针指向伪造的bufferevent
  5. 触发相关逻辑(如发送特定TCP请求),使程序执行bufferevent_get_output,进而走入我们伪造的结构体链。
  6. 通过伪造的cleanup回调链执行ORW,将flag通过现有的TCP连接发送回来。

6.5 关键技术点总结

  • 漏洞组合: 未初始化数据泄漏 + 全局缓冲区溢出。
  • 利用框架: 针对特定版本libevent的内部机制进行结构体伪造。
  • 回调链构造: 利用evbuffer_chaincleanup机制,将数据结构的释放转化为可控的函数调用链,避免了在NX开启下直接执行Shellcode的困难。
  • 稳定利用: 利用已有的TCP连接作为输入和输出通道,实现稳定的信息交互和flag回传。
相似文章
相似文章
 全屏