Cross-Site Request Forgery (CSRF) is a vulnerability where an attacker takes advantage of a logged-in user’s authenticated state to execute malicious application requests and change the user’s application state in harmful ways.
Because the attacker can’t see the result of any attack (unlike an XSS attack), CSRF is typically less about exfiltrating information (stealing data) and more about exploiting the app’s capabilities to perform unwanted actions. A classic example is making the user of a mobile payment system unwittingly send money to the wrong person.
There is often a strong social engineering aspect involved: phishing and other techniques are used to trick a user into clicking on a link that will kick off the malicious request and act as the CSRF attack vector.
The Mechanics of CSRF
CSRF is often possible because authentication credentials or cookies meant for one part of an application mistakenly allow access to another.
Imagine you are logged into a payment app like PayPal. An attacker sends you a link in a chat session. The link executes code that leverages the authentication cookie already present in your browser to make an (authenticated) request—such as one sending money to the attacker.
The danger of CSRF is a direct consequence of the actions you are allowed to take as a logged-in user of the application. Unlike XSS, the threat isn’t that you’ll send sensitive information to the attacker for later impersonation; the danger is the immediate, unauthorized action (e.g., money transfer, password change, etc.).
Many modern frameworks (Spring, Joomla, and Django) have their own solutions for preventing CSRF. These usually involve tying a cookie’s authentication ability to a specific in-app action (often by using a synchronizer token). But, despite CSRF’s status as a “solved problem,” it persists as a recurring bug in the annual OWASP Top-10 surveys. Like SQL Injection (SQLi), CSRF is a simple-but-damaging vulnerability that endures largely because of the tension in software development between security and the pressure for rapid productivity (speed of development).
Technical Requirements
For investigating and proving CSRF vulnerabilities, the following tools are used:
- Burp Suite and Burp Proxy
- Chrome (version 66.0.3359.139 is noted) for everyday web browsing and proxying.
- Python 3.6.5 and the standard macOS version of shell (
sh) for scripting and automation.
Building and Using CSRF PoCs
A CSRF Proof of Concept (PoC) is a short HTML snippet that, when executed by a user, will take advantage of a weak CSRF defense and change the application state in unexpected or unwanted ways, thus validating the vulnerability.
Creating a CSRF PoC Code Snippet
To create a CSRF PoC, you first need to observe the legitimate request made by the vulnerable application’s form.
- Intercept the Request: Navigate to the form (e.g., on a deliberately-vulnerable app like
webscantest.com). Ensure your browser is connected to the Burp Proxy and turn the Intercept feature on within the Proxy tab. - Submit Data: Fill in the form values, for example, for “William Private Mandella Mandella.”
- Capture the Request: Click Submit. The form will hang as the Burp Proxy intercepts (and holds onto) the form’s HTTP POST request.
From the intercepted request, you can deduce all the necessary parts for the CSRF PoC, including the data-encoding type (enctype), HTTP verb (method), URL value for the action attribute, and form-field names.
The PoC code is essentially a reverse-engineered expression of the submission in HTML.
Example PoC Snippet (Basic Form Imitation):
This code successfully imitates the original form. However, in a real, malicious CSRF attack, the attacker would alter the values for their own purposes—such as switching financial routing numbers, changing account passwords, or altering some other piece of critical information.
Example of Altered PoC (Promoting Mandella to Major):
Hiding the Malicious Input
If the attacker’s intent is to deceive the target into unwittingly making a submission, showing the malicious input field is counter-productive. A common technique is to use a hidden input field for the malicious value while presenting a harmless decoy field to the user.
Example with Hidden Input:
In this snippet, the other-nick input field is given the expected label, while the real nick input field is hidden and contains the secret value.
Report Note: When creating a CSRF PoC for a bug-report submission, ensure you do not actually change or modify sensitive information (like passwords or real transaction amounts). A small, illustrative alteration is sufficient to prove the possible impact of the bug.
Validating Your CSRF PoC
Using the PoC snippet is simple:
- Open the PoC as a local file in your browser.
- Submit the form you’ve coded.
If the PoC is successful, you will be redirected to a success screen (or see the change reflected in the application), indicating that the POST request generated from your local form has been accepted by the target application, despite originating from a different source (cross-site). Crucially, the hidden field’s value will be the one accepted by the application.
Impact: This ability to change a user’s application state by altering their form data is serious. Even altering a username can be a clever way of stealing an account if the victim can’t use their email to resolve authentication issues because the username (or a related identifier) was changed.
Creating Your CSRF PoC Programmatically
Rather than manually constructing a PoC by “eyeballing” the intercepted HTTP request in Burp, a script can be used to take the necessary information as input and painlessly format it into a CSRF PoC.
Python PoC Generator Script (csrf_poc_generator.py)
A short Python script can use the Beautiful Soup library (which can be used for both parsing and creating markup) to build the HTML elements.
Initial Data Structure:
#!/usr/bin/env python3
# ... imports ...
def generate_poc():
method="POST"
encoding_type="application/x-www-form-urlencoded"
action="http://webscantest.com/crosstraining/aboutyou.php"
fields = [
{
"type":"text",
"name":"fname",
"label":"fname",
"value":"William"
},
# ... other fields ...
]
# ... logic to build HTML ...
This structure uses strings for basic form tag attributes and a fields list of dictionaries containing all information needed for form fields.
The advantage of using Beautiful Soup (instantiating an HTML document, finding elements, and using append() to insert child elements) is that it simplifies the process over plain string manipulation, especially when iterating and nesting multiple children.
Logic to Build Markup (Simplified):
# ... inside generate_poc() ...
content = BeautifulSoup("", "html.parser")
html_tag = content.find("html")
form_tag = content.new_tag("form", action=action, method=method, enctype=encoding_type)
html_tag.append(form_tag)
for field in fields:
label_tag = content.new_tag('label')
label_tag.string = field['label']
# Note: 'name' is a reserved keyword in Beautiful Soup, requiring a string-based attribute definition
field_tag = content.new_tag("input", type=field['type'], value=field['value'])
field_tag['name'] = field['name'] # Using string-based definition for 'name'
form_tag.append(label_tag)
form_tag.append(field_tag)
submit_tag = content.new_tag("input", type="submit", value=action)
form_tag.append(submit_tag)
return content.prettify()
The final script includes error handling using if __name__ == "__main__": and accepts the data as command-line arguments.
This meta-programming approach successfully generates code based on simple data points to prove the vulnerability. The initial variables could be populated by command-line arguments, data pulled from a site, or a simple application form, making it a robust starting point for testing.
CSRF – An End-to-End Example
The example focuses on a form that accepts a user’s “favorite color.” The goal is to detect the vulnerability, forge a request, and create a report.
Programmatic Generation with Command-Line Arguments
The csrf_poc_generator.py script is refactored to take the critical information from the command line, leveraging Python’s sys and ast (Abstract Syntax Tree) packages to safely parse text representations of Python lists.
Refactored Script Logic for Input:
# ... inside if __name__ == "__main__":
method=sys.argv[1]
encoding_type=sys.argv[2]
action=sys.argv[3]
fields = ast.literal_eval(sys.argv[4])
print(generate_poc(method, encoding_type, action, fields))
This allows the critical information to be passed in a single command, such as:
python code/csrf_poc_generator.py "POST" "application/x-www-form-urlencoded" "http://webscantest.com/csrf/csrfpost.php" "[{ 'type':'text', 'name':'property', 'label':'color', 'value':'Cyan'}]"
The Malicious Payload
To prove the vulnerability’s impact, hidden-field “chicanery” is added to the PoC snippet:
When a user submits the form, they see “Cyan” as their favorite color (the decoy). However, the hidden field with the name="property" (the field the web app is actually expecting) submits the malicious value “Peasoup.” Since the user is logged in and authenticated, the server accepts this as a legitimate request. The success message confirms that the PoC successfully forced the change, proving the vulnerability.
Gathering and Formatting the Final Report
| Detail | Value | Description |
| CATEGORY | CSRF POST-based attack | The type of vulnerability. |
| TIME | 2018-07-22 17:27 (17:27) UTC | The approximate time of discovery/validation. |
| URL | http://webscantest.com/csrf/csrfpost.php | The vulnerable URL (the target of the POST action). |
| PAYLOAD | Peasoup | The data alteration that proved the vulnerability. |
| METHODOLOGY | Vulnerability detected with generated CSRF PoC included in reproduction instructions. | How the vulnerability was found. |
| INSTRUCTIONS TO REPRODUCE | Open the CSRF PoC HTML in a browser and submit the form. | Simple, manual steps to validate. |
| ATTACK SCENARIO | The vulnerability gives the attacker the opportunity to change a piece of the user’s account information (or execute any action) if the user unwittingly submits the attacker’s forged form. | Explains the impact on the user and the business. |
Summary
This article covered the fundamentals of Cross-Site Request Forgery (CSRF), explained how a user’s authenticated state is exploited, and demonstrated how to create and validate a CSRF PoC both manually and programmatically using Python and Beautiful Soup. Successfully documenting the vulnerability for a bug-report submission, including the attack scenario, is a crucial final step.
Review Questions
1. What is CSRF?
Cross-Site Request Forgery (CSRF, pronounced “sea-surf”) is an attack that tricks an authenticated user into unknowingly submitting a malicious request to a web application they are currently logged into.
- The Core Idea: It exploits the trust a web application has in the user’s browser. Since the user is authenticated, the application assumes any request coming from their browser is intentional and legitimate.
- The Flow:
- The user logs into a trusted site (e.g., their bank), which sets a session cookie in their browser.
- The user then visits a malicious site (e.g., via a phishing email or a compromised ad).
- The malicious site contains hidden code that automatically sends a request to the trusted site (e.g., a request to transfer money).
- The user’s browser, still holding the valid session cookie, sends this request to the trusted site, which processes it because it comes from an authenticated user.
2. What’s one possible attack scenario for a malicious actor who discovers a CSRF vulnerability?
Scenario: Unauthorized Fund Transfer in a Banking Application
- The Vulnerability: The bank’s “transfer funds” endpoint (
POST /transfer) does not have CSRF protection. It only checks for a valid session cookie. - The Setup: The attacker, Mallory, discovers this and creates a malicious webpage.
- The Attack:
- The victim, Alice, logs into her bank account.
- Later, Alice checks her email and clicks a link from “a friend,” which takes her to Mallory’s malicious page.
- The page loads and, using a hidden HTML form, automatically sends a
POSTrequest tohttps://alices-bank.com/transfer. - The request instructs the bank to transfer $5,000 from Alice’s account to Mallory’s account.
- Because Alice’s browser sends her valid session cookie with the request, the bank processes it as if Alice had intentionally submitted it.
3. What’s the typical structure of a CSRF PoC?
A Proof-of-Concept (PoC) is a simple HTML file that replicates the malicious request. Its typical structure is:
Key Elements:
- Form
action: The exact URL of the vulnerable endpoint. - Form
method: The HTTP method (GET, POST) used by the endpoint. - Hidden
inputfields: Pre-populated with the malicious parameters (e.g., new email, new password, transfer amount). - Auto-submit Script: A JavaScript that submits the form without any user interaction.
4. How do you use a CSRF PoC to validate a vulnerability?
You test it in a controlled environment to prove the action can be performed without the victim’s knowledge.
- Log In: Open a browser and log into the vulnerable application (e.g.,
vulnerable-site.com). - Open the PoC: In the same browser, open the HTML file you created. You don’t need to host it on a web server; opening it as a local file is sufficient for testing.
- Observe the Result: The form will auto-submit.
- Verify the Change: Go back to the application and check if the state-changing action occurred (e.g., check if your email was changed to
attacker@evil.com, or if a funds transfer was initiated).
If the action was successful, you have validated the CSRF vulnerability.
5. What’s the advantage of using Beautiful Soup to generate HTML, as opposed to raw string manipulation?
- Beautiful Soup (or similar HTML builders): They treat the HTML as a structured document tree. This automatically handles proper escaping of characters. For example, if a value contains a quote (
"), Beautiful Soup will correctly escape it to"to prevent breaking the HTML syntax. - Raw String Manipulation (e.g., f-strings): You are simply concatenating strings. If a user-input value contains a character that has special meaning in HTML (like
<,>, or"), it can break the HTML structure, invalidate your PoC, or even introduce an XSS vulnerability into your own test page.
In short: Beautiful Soup is more robust and safer because it prevents syntax errors and unintended side effects in your generated HTML.
6. What type of CSRF attack did the end-to-end example engage in?
Based on the previous questions and the typical PoC structure, the end-to-end example most likely engaged in a state-changing CSRF attack, specifically a POST-based CSRF attack. The goal was to make an unauthorized, state-changing request (like changing an email address or password) by automatically submitting a hidden HTML form.
7. How would malicious CSRF markup differ from educational PoCs? How would it be similar?
Similarities:
- Core Technique: Both use the same fundamental HTML elements: a
with hiddenfields and an auto-submit. - Goal: The goal is identical—to forge a request from a victim’s browser.
Differences (Malicious vs. Educational):
| Aspect | Educational PoC | Malicious PoC |
|---|---|---|
| Overtness | Obvious. It might even have a message like “CSRF PoC Test”. The field values are clearly malicious (e.g., attacker@evil.com). | Stealthy. Designed to be completely invisible and silent. The user should have no indication anything happened. |
| Delivery | Run locally by the tester. | Hosted on a public web server, often embedded in a phishing page or a malicious ad. |
| User Interaction | Often requires none, but sometimes a “Submit” button is left visible for manual testing. | Absolutely none. It will always use style="display:none" and an auto-submit script. |
| Evasion | No effort to evade detection. | May use obfuscated JavaScript, split the request across multiple frames, or use other techniques to bypass simple WAF/CSRF token checks. |
| Context | Standalone file. | Embedded within a believable decoy page (e.g., a funny meme, a fake login portal) to lure the victim. |
Further Reading
You can find out more about some of the topics discussed in this article at:
- Additional CSRF test vulnerabilities:
http://webscantest.com/csrf/