HTB Study Notes: OS Command Injection (CWE-78)
Command Injection is a critical vulnerability that occurs when an application passes unsafe user-supplied data (forms, cookies, HTTP headers, etc.) to a system shell. An attacker can use this to execute arbitrary operating system commands on the server, often leading to full system compromise.
1. Core Concepts
What is Injection?
Injection occurs when user-controlled input is misinterpreted as part of a command or query. It is consistently ranked as a top web security risk (OWASP Top 10).
| Injection Type | Description |
|---|---|
| OS Command | Input is executed as a system command (e.g., ping, ls). |
| Code Injection | Input is evaluated as programming code (e.g., eval() in PHP/JS). |
| SQL Injection | Input manipulates database queries. |
| XSS / HTML | Input is rendered directly in the browser, executing malicious scripts. |
Vulnerable Functions by Language
Developers often use these functions to interact with the underlying OS. If the input isn’t sanitized, they become entry points for attackers.
- PHP:
exec,system,shell_exec,passthru,popen. - Node.js:
child_process.exec,child_process.spawn. - Python:
os.system,subprocess.run(shell=True).
2. Detection & Basic Exploitation
The “Host Checker” Scenario
Imagine a web app that pings an IP address. The backend likely runs:
ping -c 1 [USER_INPUT]
If we input 127.0.0.1; whoami, the shell sees two distinct commands:
ping -c 1 127.0.0.1whoami
Common Injection Operators
These characters allow you to chain or manipulate commands:
| Operator | Description | URL Encoded |
|---|---|---|
; | Semicolon: Executes both commands sequentially. | %3b |
\n | New Line: Starts a new command line. | %0a |
& | Background: Runs the first command in the background. | %26 |
| | Pipe: Passes output of the first to the second. | %7c |
&& | AND: Runs the second only if the first succeeds. | %26%26 |
|| | OR: Runs the second only if the first fails. | %7c%7c |
` ` or $() | Sub-shell: Executes a command inside another. | %60 / %24%28 |
3. Bypassing Filters & Obfuscation
Modern applications often implement filters to block “dangerous” characters or keywords. Here is how to bypass them:
A. Bypassing Space Filters
If spaces are blocked, use alternative “white space” characters:
- Linux:
${IFS},%09(Tab),{ls,-la}(Brace expansion). - Windows:
%09,%0b.
B. Bypassing Character Blacklists
If specific characters like / or ; are blocked, you can use environment variables to reconstruct them:
- Linux:
${PATH:0:1}often yields/. - Windows:
%HOMEPATH:~0,1%often yields\.
C. Command Obfuscation
If keywords like whoami or cat are blocked, try these tricks:
- Case Manipulation (Windows):
WhOaMi(Windows is case-insensitive). - Character Insertion (Linux):
w'h'o'a'm'iorw\hoami. The shell ignores the quotes/slashes during execution. - Reversed Commands:
- Generate:
echo 'whoami' | rev->imaohw - Execute:
$(rev<<<'imaohw')
- Generate:
- Encoding (Base64):
- Encode:
echo -n 'cat /etc/passwd' | base64 - Execute:
bash<<<$(base64 -d<<<[B64_STRING])
- Encode:
4. Advanced Prevention & Modern Defense (2024-2025)
Beyond basic sanitization, modern security architecture focuses on Secure by Design principles to eliminate the attack surface entirely.
A. The “No-Shell” Policy
The most effective defense is to never invoke a system shell.
- Use Native APIs: Instead of
os.system('rm file.txt'), useos.remove('file.txt'). Native APIs do not interpret shell metacharacters (like;or&), making injection impossible. - Parameterized Execution: If you must run an external binary, pass arguments as an array rather than a single string.
- Vulnerable:
subprocess.run("ping " + user_input, shell=True) - Secure:
subprocess.run(["ping", "-c", "1", user_input], shell=False)(The input is treated as a literal argument, not a command).
- Vulnerable:
B. Modern Validation Strategies
- Strict Allow-listing: Instead of blocking “bad” characters (blacklisting), only allow “good” ones. For an IP address, use a regex like
^(\d{1,3}\.){3}\d{1,3}$. - Type Safety: Use strong typing or dedicated libraries (e.g.,
ipaddressin Python) to validate that the input is exactly what it claims to be before it ever reaches a processing function.
C. Environment Hardening
- Containerization & Sandboxing: Run vulnerable services in isolated containers (Docker/Podman) with read-only file systems. Even if a command is injected, the attacker is trapped in a restricted environment.
- Capability Restriction: Use Linux Security Modules like AppArmor or SELinux to restrict what a specific process can do (e.g., preventing the web server from ever executing
/bin/sh). - Principle of Least Privilege (PoLP): Ensure the service account has zero access to sensitive binaries like
nc,curl,wget, orpython, which are commonly used for reverse shells.
D. Automated Security Testing
- SAST/DAST: Integrate Static and Dynamic Analysis tools into the CI/CD pipeline to catch
shell=Trueoreval()calls before they reach production. - IAST (Interactive Application Security Testing): Modern “in-app” firewalls can monitor execution flow in real-time and block commands that contain suspicious metacharacters.
Pro Tip: Frontend validation is for User Experience (UX). Backend validation is for Security. Never trust data coming from the client, even if it looks sanitized in the browser.