Manual CSRF Testing: A Deep Dive for Penetration Testers
In this installment of the Learn Pentesting series, we’ll dive into the technical nuances of testing for Cross-Site Request Forgery (CSRF) vulnerabilities manually. CSRF remains a critical vulnerability in web applications, and understanding how to identify and exploit it is essential for penetration testers and security professionals.
1. Understanding CSRF Vulnerabilities
CSRF is an attack that tricks an authenticated user into submitting a request to a web application in which they’re currently logged in. By leveraging the user’s active session, an attacker can force unwanted actions—ranging from changing account details to initiating financial transactions—without the user’s explicit consent.
Key Characteristics:
- State-Changing Requests: CSRF targets actions that modify server state (e.g., form submissions, account updates, transactions).
- Reliance on User Authentication: The attack assumes that the user’s browser automatically includes authentication tokens (cookies, session IDs, etc.) in the request.
- Lack of Anti-CSRF Protections: Many vulnerabilities occur because the application does not verify if the request is intentionally issued by the user (e.g., missing CSRF tokens, same-site cookies not set).
2. Identifying CSRF Vulnerabilities During Penetration Testing
Before you attempt exploitation, thorough reconnaissance is necessary. The following steps outline a systematic approach:
A. Request Analysis
- Identify State-Changing Endpoints: Use your web proxy (e.g., Burp Suite) to capture HTTP requests that modify server state.
- Examine HTTP Methods: Focus on POST, PUT, DELETE, or any non-idempotent requests.
- Check for Anti-CSRF Tokens: Look for hidden form fields, custom headers, or other token implementations in the requests. A missing or predictable token is a red flag.
B. Testing Without Tokens
- Replay Requests in Isolation: Using tools like Burp Suite Repeater, remove or modify the anti-CSRF token value and resend the request.
- Observe Responses: A successful state change (or a different error message) upon tampering indicates a potential vulnerability.
C. Browser Testing
- Craft a Malicious HTML Page: Create a page that automatically submits a forged request when visited by a user. This helps in verifying if the CSRF protection can be bypassed.
3. Manual Testing Techniques and Examples
A. Intercepting and Modifying Requests
When using an intercepting proxy:
- Capture a Legitimate Request: For instance, intercept a POST request that changes a user’s email address.
- Analyze the Request Body: Look for hidden fields such as
csrf_token
or similar. - Tamper the Request: Remove or change the token value and resend it.
- Evaluate the Response: A successful change, despite token tampering, confirms the vulnerability.
B. Crafting a Malicious HTML Form
One common method is to create an HTML page that automatically submits a forged request. Consider this example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF Test</title>
</head>
<body>
<!-- The form action points to the vulnerable endpoint -->
<form id="csrfForm" action="http://vulnerableapp.com/changeEmail" method="POST">
<!-- Parameters expected by the vulnerable endpoint -->
<input type="hidden" name="email" value="[email protected]">
<!-- If a token exists, try removing it or replacing it with a fixed value -->
</form>
<script>
// Auto-submit the form to simulate a CSRF attack
document.getElementById("csrfForm").submit();
</script>
</body>
</html>
Explanation:
- Action URL: Replace
http://vulnerableapp.com/changeEmail
with the actual endpoint you are testing. - Form Fields: Include all necessary parameters expected by the endpoint. Omitting or tampering with tokens is the key to testing vulnerability.
- Auto-Submission: The JavaScript snippet submits the form as soon as the page loads, simulating an automatic CSRF exploit.
C. Using Python for CSRF Testing
Python’s requests
library can also simulate CSRF attacks. Here’s a simple example:
import requests
# URL of the vulnerable endpoint
url = "http://vulnerableapp.com/changeEmail"
# Data payload without a valid CSRF token
payload = {
"email": "[email protected]"
# Notice: No CSRF token is included here, or it could be set to a fixed value.
}
# Optionally, you might need to include session cookies if authentication is required.
session = requests.Session()
# For example, set the session cookie if you have it:
# session.cookies.set('sessionid', 'your_valid_session_cookie_here')
# Send the POST request simulating a CSRF attack
response = session.post(url, data=payload)
# Output response details for verification
print("Status Code:", response.status_code)
print("Response Body:", response.text)
Explanation:
- Session Handling: The script uses a session to manage cookies. In real-world scenarios, obtaining a valid session (or simulating one) is crucial.
- Payload Construction: The payload intentionally lacks a proper CSRF token. If the server accepts this request and changes the email, it indicates a vulnerability.
- Verification: By printing out the status code and response body, you can check if the change was successful.
4. Advanced Considerations
A. Bypassing Token Checks
Even when CSRF tokens are present, weaknesses can exist:
- Predictable Tokens: If the tokens are not truly random, you might be able to predict or reuse a token from a different session.
- Token Reuse: Some implementations use the same token across sessions or actions. Testing for token reuse is a vital part of manual testing.
B. Verifying Impact
Always validate that the request truly altered the state:
- Check the Application’s Behavior: Follow up the request by navigating the application or querying the database (if permitted) to confirm the change.
- Logs and Alerts: In a testing environment, review server logs to see if the request bypassed expected security controls.
C. Real-World Testing Example
Consider an online banking application where a money transfer endpoint is protected by a CSRF token. During testing:
- Capture the Transfer Request: Identify parameters such as
recipient_account
,amount
, andcsrf_token
. - Remove/Alter the Token: Use your intercepting proxy to remove or change the
csrf_token
value. - Replay the Request: Send the modified request. If the transfer occurs, the CSRF protection is inadequate.
- Document Findings: Record the endpoints, request details, and behavior observed for further remediation.
5. Conclusion
Manual testing for CSRF vulnerabilities requires both a keen understanding of the underlying mechanics and a methodical approach to verify the presence (or absence) of effective anti-CSRF measures. By leveraging techniques such as intercepting proxy manipulation, crafting malicious HTML forms, and simulating attacks with Python scripts, penetration testers can uncover critical flaws that automated tools might miss.
Key Takeaways:
- Understanding the Mechanism: Know how CSRF exploits user trust and session management.
- Thorough Testing: Analyze requests carefully and verify the necessity of anti-CSRF tokens.
- Practical Exploitation: Use both browser-based and scripted methods to validate vulnerabilities.
- Documentation: Meticulously document each step for a comprehensive report.
This guide serves as a reference for penetration testers aiming to master manual CSRF testing. In subsequent posts, we’ll explore more complex scenarios and mitigation strategies for various web vulnerabilities.
For further reading and code examples, consider exploring resources like the OWASP CSRF Prevention Cheat Sheet and advanced Burp Suite tutorials.
Happy testing, and stay secure!