2025羊城杯
0x00 前言:
止步46,不知道说什么,还是太菜了
0x01 PWN
malloc
不是正经堆题,但差不多。首先就是有沙箱,ban了execve和execveat
line CODE JT JF K |
虽然去了符号表,但是不难看出这里有uaf
正当我想gdb尝尝咸淡的时候发现不对劲
回到ida,查看分配堆块的逻辑
qword_5200[v0 + 512] = sub_1365(v4); |
可以通过这里找到堆块存放的位置以及堆块大小存放的位置,可以用这两个命令查看对应地址的内容
pwndbg> x/20gx $rebase(0x0000000000005200) + 528*8 |
emm,堆块存放的地方并不在堆段上(?
同时可以注意到存放第一个堆块的大小的与存放堆地址的位置离得很近,回到分配堆块的函数
if ( v3 <= 0x10 && (puts("size"), __isoc99_scanf("%u%c", &v4, &v2), v4 <= 0x70) && v4 > 0xF ) |
限制了只能申请16个堆块,第16个刚好能够覆盖掉第一个堆块的大小。
malloc(0,0x10) |
这样就可以有堆溢出,去写下一个相邻的堆块的fd,然后将堆块申请到管理堆块的这片内存,同时打印堆块的函数并没有做检测
if ( v2 <= 0x10 && qword_5200[v2 + 512] ) |
依旧UAF,可以用来泄露pie base和libc base。
malloc(0,0x10) |
还是堆的泄露手法,不过这次可以直接泄露pie base,泄露出来后通过第0个堆的堆溢出,能够修改fd,把堆块申请到管理堆块的内存上,直接就能制造一个任意地址写
payload = p64(0) * 3 + p64(0x80) + p64(pie_base + 0x61f0) |
效果大概是这样
接着就能泄露libc,同时参考heap-2.35(UAF,堆栈结合) 泄露出environ,得到栈地址,继续利用这个任意地址写往栈上写rop链,最后orw即可,这里直接搬了stack的orw(,我是先做那题的
from pwn import * |
stack
check
Arch: amd64-64-little |
首先是初始化,把main函数的地址*3 / *4丢到qword_4040上
按x找引用发现有个这样的函数,会输出刚刚的qword_4040,然后call sub_161F();
顺带一提,17行的函数就在上面这个输出函数的旁边,是一个exit
接着就是申请一个0x1000的堆块,然后一些其他的设置。继续往下看
往堆块上输入内容,可以输入0x2000字节,远超堆块大小。gdb测试发现,我们输入的地址距离下一个函数的地址是0x108
虽然开了pie,但是我们可以只写入一个字节,来劫持程序流到输出magic number的位置。这样我们就得到了这个
for ( qword_4038 = 0LL; qword_4038 <= 2; qword_4038 = rand() % 5 ) |
伪随机嘛,也就是说我们得到了main,pie base
libc1.srand(libc1.time(0)) |
接着回到了起点,这次有了pie,我们想办法泄露libc即可,我找了这段
_seccomp_load并不会重置rdi寄存器,然后我们就可以用puts来泄露libc
payload = b'\x00' * 0x108 + p64(pie_base + 0x0000000000001489) + p64(0) + p64(elf.got.puts) + p64(0) * 5 + p64(elf.plt.puts) + p64(pie_base + 0x000000000000160A) + p64(0) + p64(pie_base + 0x00000000000161F) |
依旧回到起点,这次直接布置orw的rop链即可,因为沙箱限制了read的第一个参数,所以这里使用了readv
from pwn import * |





