Learn Pentesting: Manual Testing for PHP Code Injection
This article is part of our “Learn Pentesting” series—a collection of in-depth technical articles for penetration testers and those working to become one. Today, we dive into one of the more dangerous vulnerabilities in PHP-based web applications: PHP code injection. In this post, we explore how to manually test for PHP code injection during web-application penetration tests, discuss common vulnerable coding patterns, and provide code examples and payloads that you can use as a reference during your assessments.
Overview
PHP code injection occurs when user-supplied input is unsafely passed to functions that execute code dynamically. This typically happens with functions such as eval()
or assert()
when they are used without proper input validation or sanitization. An attacker who successfully exploits such vulnerabilities can inject and execute arbitrary PHP code, leading to severe security breaches—including remote code execution.
In this article, we will cover:
- The fundamentals of PHP code injection.
- Step-by-step manual testing techniques.
- Code examples and payload crafting.
- Strategies for handling different injection contexts.
- Best practices and mitigation techniques.
Understanding PHP Code Injection
What Is PHP Code Injection?
PHP code injection is a vulnerability that allows an attacker to insert and execute PHP code within an application. This vulnerability most commonly arises from unsafe use of PHP functions that execute strings as code. For example:
eval()
Function: Executes a string as PHP code.assert()
Function: When passed a string argument, it acts similarly toeval()
.
Consider the following vulnerable code snippet:
<?php
// Vulnerable to PHP code injection
$code = $_GET['cmd'];
eval($code);
?>
If this code is deployed in a web application, an attacker could send a request like:
http://example.com/vulnerable.php?cmd=phpinfo();
This request would execute phpinfo()
, revealing sensitive configuration details about the server.
Why Is It Dangerous?
If an attacker can inject PHP code, they could:
- Execute arbitrary commands.
- Access sensitive files.
- Modify system configurations.
- Potentially gain complete control over the affected server.
Because the consequences can be severe, manual testing for PHP code injection is critical during a penetration test.
Manual Testing Process
1. Identifying Injection Points
Start by mapping the application and identifying all inputs where user-supplied data might be incorporated into dynamic PHP code. Common injection points include:
- Query Parameters: URL parameters such as
?cmd=
. - POST Data: Form inputs or API payloads.
- Cookies: Data stored on the client and sent to the server.
- HTTP Headers: Such as
User-Agent
orReferer
.
A careful review of error messages, unexpected outputs, or PHP warnings can reveal hints of dynamic code execution.
2. Crafting Initial Test Payloads
Begin your testing with benign payloads that help determine if user input is reaching a code execution function.
Basic Test Payload
A simple test payload is to call a harmless function such as phpinfo();
:
http://example.com/vulnerable.php?cmd=phpinfo();
If the page returns PHP configuration details, this confirms the vulnerability.
Payload with Side Effects
In cases where direct output isn’t visible, try payloads that cause observable side effects. For example:
;echo(md5("test"));
Appending this payload should output the MD5 hash of “test” (which is 098f6bcd4621d373cade4e832627b4f6
):
http://example.com/vulnerable.php?cmd=;echo(md5("test"));
The appearance of the hash in the response confirms that the injected code is being executed.
3. Evaluating Responses
Analyze the HTTP responses for clues that indicate code execution:
- Error Messages: PHP warnings or syntax errors might reveal the execution of injected code.
- Unexpected Output: Look for additional output that might result from your payload.
- Response Time: For blind injections, time delays (using commands like
sleep(5);
) can indicate code execution even if no output is shown.
4. Advanced Payload Crafting
When basic tests are filtered or do not provide direct output, you may need to use more sophisticated payloads.
Obfuscation Techniques
If the application filters specific keywords, try constructing the function name dynamically:
; $f = 'ph'.'pinfo'; $f();
This approach bypasses simple string-based filters.
Chained Commands
You might want to execute multiple commands in one payload:
; system('id'); echo(md5("check"));
This payload attempts to run the id
command on the system and then echoes the MD5 hash of “check” to confirm execution.
Handling Input Contexts
Depending on how the vulnerable input is used, you might need to adjust your payload:
- Within a String Context: If your payload is injected within quotes, escape or close the string appropriately. For example:
'); phpinfo(); //
- Structured Data (JSON, Serialized Data): If the injection point is within JSON or serialized data, consider encoding your payload (e.g., Base64) and then decoding it in the vulnerable function.
5. Verifying Code Execution
After injecting your payload, verify execution by:
- Observing Expected Output: Such as the MD5 hash or PHP configuration details.
- Checking for Side Effects: File modifications, log entries, or other system changes.
- Time-Based Verification: Using payloads like
;sleep(5);
to check if the server response is delayed, suggesting that the payload was executed.
Practical Example: Step-by-Step Testing
Let’s walk through a practical example. Suppose you are testing a parameter cmd
in a web application.
Step 1: Basic Test
Access the following URL:
http://example.com/vulnerable.php?cmd=phpinfo();
If PHP configuration details are displayed, you have confirmed that the input is being evaluated as PHP code.
Step 2: Confirm with a Side-Effect Payload
If the output isn’t immediately obvious, try:
http://example.com/vulnerable.php?cmd=;echo(md5("test"));
Look for the MD5 hash 098f6bcd4621d373cade4e832627b4f6
in the response.
Step 3: Obfuscated Injection
If your initial payloads are being filtered, use an obfuscated version:
http://example.com/vulnerable.php?cmd=;${"GLOBALS"}["f"]="ph"."pinfo";${"GLOBALS"}["f"]();
This payload constructs the phpinfo()
function name dynamically, bypassing simple keyword filters.
Step 4: Blind Injection Testing
When you suspect the injection is occurring but no output is visible, try a time-delay payload:
http://example.com/vulnerable.php?cmd=;sleep(5);
If the page takes noticeably longer (about 5 seconds) to load, it indicates that the payload was executed.
Best Practices for Manual Testing
- Start Simple: Always begin with basic payloads before moving on to more complex injections.
- Review Source Code: If available, analyze the source code to locate where user input is processed.
- Monitor Responses Carefully: Pay close attention to error messages, unexpected output, and response times.
- Bypass Filters: Use obfuscation or alternative encoding techniques to work around common input filters.
- Document Everything: Keep detailed notes on payloads used, responses received, and any errors encountered during testing.
Mitigation Techniques
Once vulnerabilities are identified, remediation is crucial. Here are some mitigation strategies:
- Avoid Dangerous Functions: Refrain from using functions like
eval()
orassert()
with user-supplied data. - Input Validation and Sanitization: Implement strict validation and sanitization routines for all inputs.
- Use Static Code Analysis: Regularly audit your code for unsafe practices.
- Follow Secure Coding Guidelines: Embrace best practices to minimize the risk of code injection vulnerabilities.
Conclusion
PHP code injection remains one of the most dangerous vulnerabilities in web applications. Through careful manual testing—starting with simple payloads, analyzing responses, and using advanced techniques when necessary—penetration testers can effectively identify and verify these vulnerabilities.
This deep dive has provided a step-by-step guide on testing for PHP code injection, complete with code examples and payloads. As you integrate these techniques into your testing methodology, remember to always obtain proper authorization before testing any system.
Stay tuned for more technical insights in our “Learn Pentesting” series. Happy testing!