网鼎杯的题目决定先复现一道,cardmaster 这道题目总体来说是逆向难度比较大,指针比较乱,漏洞利用逻辑就是 glibc2.27 版本下的 UAF 和 teache_poisioning。另一道题目是一个内核题,决定再学习一段时间内核再去复现感觉效果会更好。
PWN1-cardmaster


看下初始化的一些基本信息:
先开辟 0x28 大小的空间,地址存放在 v4。再开辟 0x20 大小的空间,地址存放在 v4[4]。
可以把 v4[4] 看做一个链表,开辟一个 0xD0 大小的空间存放在 v4[4]->v0,之后可以把 v0 看做是一个二维数组:v0[i][j],每个数组相当于是十六个字节的内存,前八个字节存放 i,后八个字节存放 j+1。
根据题意我们大概得知这是一个扑克牌的项目,4 就代表四种不同的花色,13 就代表 “3-A” 中 13 种不同牌值。

这里注意一下 __isoc99_scanf("%lld", v4 + 2);,*((_UNKNOWN **)v4 + 2)两处的不同。前者指的是 int 型的 v4,v4+2 指的就是+0x8;后者由于程序是 64 位的,默认 UNKNOWN 就是 8 个字节的指针数组,其 v4+2 指的是+0x10。(这道题目感觉就是指针给的太乱了)
之后我们会进行 malloc/realloc:不过在前面的分析当中,在第一次执行 set_info 时只会触发 malloc,在此之后在函数调用的最下方有这么几行代码:
1 | if ( v5 ) |
他们会把我们会让指针指向 v5,把*((_QWORD *)v4 + 2)移到堆上,并读入内容,这样就会修改原本*((_QWORD *)v4 + 2) 存入的字符串,在第二次进入到 set_info 函数当中时就会触发 realloc。
依据 realloc 的特性,执行 realloc(*((void **)v4 + 2), 0);就会导致 UAF,
利用(*(void (__fastcall **)(__int64))(v4 + 0x18))(v4);// get_info我们可以执行:
1 | int __fastcall sub_D31(__int64 a1) |
执行完sub_D31,原本指向*((_QWORD *)v4 + 2)的数据会被*(const char **)(a1 + 16)泄露,
那么就可以通过这个泄露出 libc_base。
最后利用 tcache_posioning 篡改 free_hook 进行 system(‘/bin/sh’);
EXP 思路:
利用 UAF 泄露 libc_base:
1 | from pwn import * |

然后微调截取关键内容即可:
1 | p.recvuntil(b"suit chara set:") |

之后利用 double_free 直接篡改 free_hook
总 EXP:
1 | from pwn import * |

说些什么吧!