Sitemap
DevSecOps & AI

The place where DevSecOps & AI unite.

Secure Coding Part 6 : Command Injection — Attack (Python) — os.system

3 min readJul 31, 2025

--

Lets see in action how a command injection payload lands on a server and how it gets executed :

✅Legit Flow :

Press enter or click to view image in full size
  1. The user enters a safe input like 127.0.0.1 in the frontend.
  2. The frontend sends this input via an API call to the middleware.
  3. The middleware constructs a system command:
ping 127.0.0.1

4. The server executes the ping command and returns the result safely to the frontend.

✅Malicious Flow :

Press enter or click to view image in full size
  1. The attacker enters malicious input:
127.0.0.1; ls -la

2. The middleware constructs:

ping 127.0.0.1; ls -la

3. The server blindly executes this command.

4. The attacker gets a directory listing of the server (proof of RCE).

Get Siddiquimohammad’s stories in your inbox

Join Medium for free to get updates from this writer.

5. Sensitive files like config.php and .htaccess become exposed.

✅The Culprit : Vulnerable backend code

import os
from flask import Flask, request

app = Flask(__name__)

@app.route('/ping', methods=['GET'])
def ping_host():
# 1️⃣ ❌ Take user input directly (no validation)
host = request.args.get('host')

# 2️⃣ ❌ Construct the command with string concatenation (DANGEROUS)
command = "ping -c 4" + host

# 3️⃣ ❌ Execute the command using os.popen (vulnerable to injection)
output = os.popen(command).read()

# 4️⃣ Check if '4 received' appears in ping output (basic check)
if "4 received" in output or "4 packets received" in output:
return "Reachable"
else:
return "Unreachable"

if __name__ == "__main__":
app.run(debug=True)

Why is this code vulnerable?

host = request.args.get('host') → Takes unsanitized user input.

command = "ping -c 4 " + host → Concatenates input directly into a system command.

os.popen(command).read() → Executes the entire string on the OS shell.

Impact: An attacker can send 127.0.0.1; ls -la → the system runs ls -la, exposing files.

✅Fix : Secure version

import subprocess
from flask import Flask, request
import re

app = Flask(__name__)

@app.route('/ping', methods=['GET'])
def ping_host():
# 1️⃣ Get user input
host = request.args.get('host')

# 2️⃣ ✅ Validate input (only digits and dots allowed)
if not re.match(r'^[0-9\.]+$', host):
return "Invalid input!", 400

# 3️⃣ ✅ Run the ping command safely
result = subprocess.run(
["ping", "-c", "1", host], # only 1 ping to make it faster
capture_output=True,
text=True
)

# 4️⃣ ✅ Check return code and respond with simple message
if result.returncode == 0:
return "Reachable"
else:
return "Unreachable"

if __name__ == "__main__":
app.run(debug=True)

What makes this code secure?

==>> Input Validation :

if not re.match(r'^[0-9\.]+$', host):     return "Invalid input!", 400
  • Only allows digits and dots (127.0.0.1) — no ;, |, &&.

==>> No Shell Involvement :

subprocess.run(["ping", "-c", "4", host], ...)
  • Uses subprocess.run with a list (not a string) — prevents shell injection.
  • Example: Instead of building ping 127.0.0.1; ls, it safely runs the binary ping with arguments.

✅Quick Summary :

Press enter or click to view image in full size

--

--

Siddiquimohammad
Siddiquimohammad

Written by Siddiquimohammad

Cybersecurity strategist | Offense meets defense | Cloud & AI security, DevSecOps, Red Team tactics & Blue Team resilience in one security brain