Fork me on GitHub

Underdist 3.0

Vulnhub's newest VM Underdist was a great challenge for me. What follows is my walkthrough from start to root, enjoy!

First Look

Nmap scan report for Underdist (
Host is up (0.015s latency).
Not shown: 65533 closed ports
22/tcp open  ssh     OpenSSH 6.0p1 Debian 4+deb7u2 (protocol 2.0)
25/tcp open  smtp    Postfix smtpd
80/tcp open  http    Apache httpd 2.2.22 ((Debian))
MAC Address: 08:00:27:07:BD:56 (Cadmus Computer Systems)
Service Info: Host:  Underdist; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Taking a look at what was available via HTTP revealed some snazzy ASCII art but otherwise nothing remarkable. Naturally, I checked the page source which revealed an HTML comment pointing to a PHP script with some base64 encoded URL parameter (Figure 1). Following the link, I was taken to another page with an ASCII art 404. A quick decode of the base64 encoded URL parameter revealed the text 'ascii1.txt'. While I was doing this, I had dirb running in the background. The results revealed a folder structure (Figure 2), within which was the file 'ascii1.txt'! This meant that there was a potential LFI vulnerability available to me, as the URL parameter was equal to the base64 encoded value of 'ascii1.txt', which was found in /ascii/letras/ascii1.txt. After a bit of prodding, I was able to successfully exploit the LFI vulnerability by base64 encoding the string ../../../../etc/passwd and using it as the URL parameter for the PHP script (Figure 3).

<!--<a href="v.php?a=YXNjaWkxLnR4dA==">foo</a>-->

Figure 2: Possible LFI vulnerability

Figure 3: Successful LFI exploitation to read /etc/passwd

Finding a Way In

Having /etc/passwd, my first thing to check was weak user passwords. Users cuervo and underside both looked particularly interesting, however when attempting SSH logins to both accounts, I was denied due to a publickey error. This meant that I should probably be on the lookout for a private key somewhere on the file system.

Now with the ability to read files on the target system, I set out to identify and access any files I could to find a way in. Initially I had thought that I might be able to attempt a HTTP log poisoning vector. To accomplish this I would have to first locate and be able to read the Apache error log. After reading the Apache config files on the target I attempted to read the log files, but I was unable to do so. The next couple hours I poured over what files I did have access to to try and think of another vector.

After some time, I concluded that I might be able to leverage the open Postfix service to send an email to a user and then utilize the LFI vulnerability to render arbitrary PHP code by reading the contents of the email. Knowing that email files are kept in /var/mail, I had to then determine valid user accounts. For this, I fired up metasploit's smtp_enum auxiliary scanning script and pointed it at my target. After some time, I got my results (Figure 4)! After many attempts, I determined that I could send an email via Postfix to user www-data and then read it by pointing the PHP script to ../../../../var/mail/www-data. All other attempts failed most likely because I only had access to the file system via LFI as the default web user on the system. From here, I created a simple Python script to send a reverse php meterpreter payload as an email and have it read and executed when I viewed it via the PHP script (Figure 5). I then successfully had limited user access to the target (Figure 6)!

Figure 4: SMTP user enumeration

Figure 5: Python script to send meterpreter payload

Figure 6: Successful shell on target


I had limited capabilities with my PHP meterpreter session, but I had a little more freedom to navigate and view files on the target. I discovered a folder and a file whose contents contained a RSA private key - likely the very key I needed to gain SSH access to the target (Figure 7). Sure enough, passing the private key to SSH as user cuervo, I had indeed elevated my privileges to a user on the target (Figure 8)!

Figure 7: RSA private key discovered

Figure 8: SSH access as user cuervo

(Jose) Cuervo

There wasn't anything remarkable in cuervo's home directory, so I set out to see what else I could find. In underdist's home directory I found two files of interest (Figure 9). Aptly named, was a script run via cron. This script read a file ips.txt and each entry was used as raw, unsanitized input and passed directly to Popen, a module that executes commands on the system (Figure 10). The best part about these files were that their permissions were not set properly which then allowed me to edit ips.txt, thus allowing me to inject arbitrary code into the cron script and get code execution as user underdist. I created a reverse shell payload with metasploit, uploaded it to the target and added this line to ips.txt:;/tmp/rs. In no time, the cron job was kicked off, and my reverse shell was opened, allowing me to escalate my access on the target to user underdist (Figure 11).

Figure 9: Lax permissions to userdist cron scripts


import os

def ips():
    return f

def save(d):
    f=open("/tmp/logs", "a+")

def command(c):
    p=os.popen('ping -c 1 -w 1 %s|grep received|cut -d " " -f 4' % (c), "r")

def verify():
    save("- - - - - - - - - - - - - - - - - - - - - - - - -\n")
    for ip in ips():
        ip=ip.replace("\n", "")
        if command(ip)=="1\n":
            save("Host %s Up\n" % (ip))
            save("Host %s Down\n" % (ip))


Figure 11: Shell via cron script

Is There an Echo? (echo, echo, echo...)

Now that I had full user access to user underdist I was able to explore the rest of his home directory. I discovered a hidden folder .bin which contained a lovely binary file echo which was owned by root and had the setuid bit set (Figure 12). This meant that this was very likely my way to root!

I loaded up gdb (and also setup peda!) and commenced fuzzing the binary to see if I could use a buffer overflow attack and control code execution. I was indeed able to elicit a buffer overflow (Figure 13), so I had my work cut out for me! I used metasploit's pattern_create and pattern_offset scripts to identify where in my buffer I had control over EIP. Using peda-gdb really made the exploit development process much easier. I was able to make quick work of searching for a jmp esp instruction (there was none!).

Figure 12: Hidden folder with setuid root-owned binary

Figure 13: Buffer overflow EIP control

After some dinner and plenty of head scratching, I determined what I needed to do. If I could point EIP to the location in memory of my A's (the beginning portion of my string buffer), I could replace my 'A's with NOP instructions and get a NOP sled to some shellcode and drop into a shell. For me, this was easier said than done. I repeatedly failed at finding a suitable memory address to jump to. The theory seemed correct, I just couldn't get it. Through trial and error I finally found a memory address that got me to my NOP sled.

The next task was finding a suitable shellcode. peda does a fantastic job of streamlining this step! Builtin to peda is the ability to search's shellcode repository. I pulled up a small 23 byte shellcode, adjusted my buffer of NOPs, added my shellcode and my working memory address and ran it through gdb one last time - success!! (Figure 14)

Figure 14: Working buffer overflow exploit in gdb

Pulling a Rabbit from My Hat

All that was left for me to do was to close gdb and run my little buffer overflow string against the binary. As expected, my exploit was a success (Figure 15)! The exploit against the setuid binary gave me euid=root! From here, I navigated to /root and captured my flag (Figure 16)!!

Figure 15: Successful buffer overflow

Figure 16: Flag