吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 607|回复: 4
上一主题 下一主题
收起左侧

[求助] 一道CTF的PWN题,求助大佬解答

[复制链接]
跳转到指定楼层
楼主
bob1994 发表于 2026-5-27 13:46 回帖奖励
200吾爱币
本帖最后由 bob1994 于 2026-5-27 17:20 编辑

一道CTF的PWN题,求助解答:

最佳答案

查看完整内容

这是一道 UAF + 双重释放(double free) 的堆题。 程序结构 菜单式 Note Manager,笔记指针数组在 .bss 的 0x4040C0,共 20 个槽。每个笔记是一个 malloc(0x10) 的元数据结构: struct note { void *content_ptr; // +0 size_t size; // +8 }; ┌───────────────────┬───────────────────────────────────────────── ...

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
囚徒灬 发表于 2026-5-27 13:46
本帖最后由 囚徒灬 于 2026-5-27 17:02 编辑

这是一道 UAF + 双重释放(double free) 的堆题。

  程序结构

  菜单式 Note Manager,笔记指针数组在 .bss 的 0x4040C0,共 20 个槽。每个笔记是一个 malloc(0x10) 的元数据结构:

  struct note { void *content_ptr;  // +0
                size_t size;        // +8 };

  ┌───────────────────┬────────────────────────────────────────────────────────────┐
  │       功能        │                            行为                            │
  ├───────────────────┼────────────────────────────────────────────────────────────┤
  │ Create (0x401334) │ 槽必须为空;size 限 1..0x80;分配元数据+内容,读入数据        │
  ├───────────────────┼────────────────────────────────────────────────────────────┤
  │ Edit (0x4015AF)   │ 按 note->size 读入到 note->content_ptr(任意写的雏形)       │
  ├───────────────────┼────────────────────────────────────────────────────────────┤
  │ Delete (0x4016C2) │ free(content_ptr),然后只把 size=0                          │
  ├───────────────────┼────────────────────────────────────────────────────────────┤
  │ Show (0x4017C0)   │ 按 note->size 把 content_ptr 内容 write 出来(任意读的雏形) │
  └───────────────────┴────────────────────────────────────────────────────────────┘

  核心漏洞(delete 0x4016C2)

  释放时三件事都没做:
  1. 没把 content_ptr 置 NULL → 悬空指针
  2. 没释放元数据结构、没清空数组槽 → 同一 index 可被反复 delete → double free
  3. 只把 size 清零(让 edit/show 对已删笔记暂时失效,但双重释放仍可用)

  保护机制

  - No PIE(固定地址 0x40xxxx)
  - Partial RELRO:.got.plt(0x404000–0x404068)可写 → GOT 可改写
  - 有 Canary(__stack_chk_fail),但栈不是攻击面
  - GLIBC 2.34+(tcache 带 safe-linking + key 检测)
  - free@Got = 0x404018

  利用思路

  利用「元数据 0x20、内容尺寸可调」让 double-free 的 chunk 同时是某个活跃笔记的内容(可 edit),实现 tcache 投毒:

  1. 构造重叠:create 一个内容在 0x50 bin 的笔记(如 size=0x48)→ delete → 再 create 同尺寸,使新笔记的内容复用刚释放的
  chunk B(元数据 0x20 走另一个 bin,先被取走);此时旧笔记悬空指针仍指向 B。
  2. double free:再次 delete 旧 index,把仍在使用中的 B 放回 tcache[0x50]。现在 B 既在 freelist
  又是活跃笔记的可编辑内容。
  3. 泄露堆地址:show 该活跃笔记,读出 B 的 fd = B>>12(safe-linking 还原堆基址)。
  4. 投毒:edit 该笔记,把 fd 改成 (B>>12) ^ target,两次 malloc 后拿到 target 处的 chunk。
  5. 伪造元数据 → 任意读:把 chunk 指到 notes 数组/伪造 metadata,令 content_ptr=puts@got, size=8,show 泄露 libc → 求
  system。
  6. 任意写 → getshell:edit 把 free@got(0x404018) 改成 system;再建一个内容为 "/bin/sh\0" 的笔记,delete 它 →
  free("/bin/sh") 即 system("/bin/sh")。

opus 跑了一下

  运行说明

  - 这是 Linux ELF + pwntools,需在 Linux 上跑(本机是 Windows,我没法直接执行验证)。
  - 本地:python3 exp.py(用本机 libc,脚本走 exe.libc 自动解析偏移)。
  - 远程:目录里没附带 libc,需把题目给的 libc 放成 ./libc.so.6,再 python3 exp.py REMOTE HOST=ip PORT=port。如果远程 libc
  版本不同,system/偏移会错,需用泄露的 puts 去 libc-database 匹配版本。

exp.rar.txt (2.06 KB, 下载次数: 1)

heap - Simple Note Manager 分析报告

目标文件:E:\ai\cm\pwn\heap(x86-64 ELF,动态链接)
利用脚本:exp.py
日期:2026-05-27


一、结论

漏洞类型:Use-After-Free + Double Free(堆)。

delete 函数在释放笔记内容后:

  1. 不清空 content_ptr → 悬空指针;
  2. 不释放 metadata 结构、不清空 数组槽 → 同一 index 可被重复 deletedouble free;
  3. 仅把 size = 0(让已删笔记的 edit/show 暂时失效,但 double free 仍可用)。

可由此构造 tcache 投毒,改写 .bss 中的笔记指针数组,获得任意读写,最终改写 free@gotsystem,以 free("/bin/sh") 拿到 shell。


二、程序结构(证据)

笔记指针数组位于 .bss0x4040C0,共 20 槽(0x4040C0 + 8*i,i<0x14)。
每条笔记是 malloc(0x10) 的 metadata:

struct note { void *content_ptr;  // +0
              size_t size;        // +8 };
功能 地址 行为
Create 0x401334 槽必须为空且 idx<0x14;1<=size<=0x80;分配 metadata(0x10)+内容(size),read 读入
Edit 0x4015AF note->size 读入到 note->content_ptr(任意写雏形)
Delete 0x4016C2 free(content_ptr),然后 size=0
Show 0x4017C0 note->size writenote->content_ptr(任意读雏形)
Menu 0x4012BB 打印菜单,末尾 printf("> ")
Init 0x401256 setvbuf 关缓冲

Delete 关键反编译(漏洞点)

// sub_4016C2
if ( (unsigned int)v1 < 0x14 && *((_QWORD *)&unk_4040C0 + v1) ) {
    free(**((void ***)&unk_4040C0 + v1));        // free(content_ptr)
    *(_QWORD *)(*((_QWORD *)&unk_4040C0 + v1) + 8LL) = 0;   // size = 0
    // 缺失: content_ptr 未清零、metadata 未释放、数组槽未清空
}

三、保护机制(证据)

来自 IDA 段信息与 GOT 检查:

  • No PIE:固定基址 0x400000,代码/数据地址 0x40xxxx
  • Partial RELRO:.got.plt(0x4040000x404068)段权限 RWGOT 可写
  • Canary:存在 __stack_chk_fail,但栈不是攻击面。
  • NX:数据段不可执行(常规)。
  • GLIBC 2.34+:.rodataGLIBC_2.34 版本符号 → tcache 启用 safe-linking + double-free key 检测,且无 __free_hook/__malloc_hook

关键地址:

符号 地址
notes 数组 0x4040C0
free@got 0x404018
puts@got 0x404020

(已通过 .free PLT stub bnd jmp cs:off_404018 验证 free@got=0x404018。)


四、利用链

整体:UAF/double free → tcache 投毒 → 改写 notes 数组 → 任意读写 → 改 free@got=system → getshell。

  1. 构造可控悬空 chunk:用 size=0x48(内容落 0x50 bin,独立于 metadata 的 0x20 bin)。让 double-free 的 chunk CA 同时是某活跃笔记的内容(可 edit/show)。
  2. 堆地址泄露:CA 单独入空 tcache 时 fd = CA>>12(safe-linking key)。show 读出 → 得 heap_key
  3. count=2 投毒:把 tcache[0x50] 凑成 [CA, C4](CA 在表头,count=2),editCA.fd = heap_key ^ 0x4040C0
    > 注:tcache 取块需 counts>0,故必须先把计数凑到 2,否则第二次分配不会走 tcache。
  4. 改写 notes 数组:两次分配,第二次返回 0x4040C0,把整段写成伪造 slot + metadata:
    • slot0 -> 0x4040E0 = {content_ptr=puts@got, size=8}(任意读)
    • slot1 -> 0x4040F0 = {content_ptr=free@got, size=8}(任意写)
  5. leak libc:show(0) 打印 puts@got 处 8 字节 = libc puts 地址 → 求 libc 基址与 system
  6. 改 GOT:edit(1) 写 8 字节到 free@gotsystem
  7. getshell:create 内容 "/bin/sh\0",delete 它 → free(ptr) 实为 system("/bin/sh")

详见 exp.py,各步骤已对应注释。


五、待确认 / 研究方向

  1. 远程 libc 版本:目录内未附带 libc。脚本中 tcache 桶分流与 system 偏移依赖具体 glibc。
    • 远程若给 libc → 放为 ./libc.so.6,python3 exp.py REMOTE HOST=.. PORT=..
    • 未给 → 先只跑到第 5 步泄露 puts,用 libc-database 按低 3 字节匹配版本。
  2. glibc 版本差异:
    • 2.34/2.35 行为如脚本所设;若为更高版本(2.39+),tcache key/计数检查、_int_free 校验略有差异,double-free 序列可能需微调。
    • global_max_fast/tcache bins 被改,0x50 桶假设需复核。
  3. 备选利用路径(若 GOT 改写在目标环境受限):
    • FSOP:伪造 _IO_FILE(stdout/stderr)+ _IO_2_1_stdout_ vtable(2.34+ 用 _IO_wfile_jumps / house of apple 系列)。
    • 任意写打 __exit_funcs / tls_dtor_list,或 _dl_fini 相关结构。
    • 由于 No PIE,也可考虑直接劫持其他频繁调用的 GOT(printf/puts)配合可控参数。
  4. 稳定性:脚本对 read 不追加换行、内容按 size 对齐填充已处理;实测时关注菜单回显是否完全匹配 > / Index: / Size: / Content: / New content:

附:复现命令

# 本地(需 Linux + pwntools)
python3 exp.py

# 远程(放入题目 libc)
python3 exp.py REMOTE HOST=<ip> PORT=<port>
3#
Maxhaha 发表于 2026-5-27 13:53
4#
 楼主| bob1994 发表于 2026-5-27 14:07 |楼主
5#
 楼主| bob1994 发表于 2026-5-27 17:15 |楼主
囚徒灬 发表于 2026-5-27 16:50
这是一道 UAF + 双重释放(double free) 的堆题。

  程序结构

感谢大佬
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-13 22:35

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表