← /notes

HTB Study Notes: OS Command Injection (CWE-78)

[cwes]

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 TypeDescription
OS CommandInput is executed as a system command (e.g., ping, ls).
Code InjectionInput is evaluated as programming code (e.g., eval() in PHP/JS).
SQL InjectionInput manipulates database queries.
XSS / HTMLInput 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:

  1. ping -c 1 127.0.0.1
  2. whoami

Common Injection Operators

These characters allow you to chain or manipulate commands:

OperatorDescriptionURL Encoded
;Semicolon: Executes both commands sequentially.%3b
\nNew 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:

  1. Case Manipulation (Windows): WhOaMi (Windows is case-insensitive).
  2. Character Insertion (Linux): w'h'o'a'm'i or w\hoami. The shell ignores the quotes/slashes during execution.
  3. Reversed Commands:
    • Generate: echo 'whoami' | rev -> imaohw
    • Execute: $(rev<<<'imaohw')
  4. Encoding (Base64):
    • Encode: echo -n 'cat /etc/passwd' | base64
    • Execute: bash<<<$(base64 -d<<<[B64_STRING])

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'), use os.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).

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., ipaddress in 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, or python, 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=True or eval() 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.