SharePoint - Cross Site Request Forgery (CSRF/XSRF) attacks

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker's choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.

How does the attack work?

There are numerous ways in which an end user can be tricked into loading information from or submitting information to a web application. In order to execute an attack, we must first understand how to generate a valid malicious request for our victim to execute. Let us consider the following example: Alice wishes to transfer $100 to Bob using the bank.com web application that is vulnerable to CSRF. Maria, an attacker, wants to trick Alice into sending the money to her instead. The attack will comprise the following steps:

  1. building an exploit URL or script
  2. tricking Alice into executing the action with social engineering

GET scenario

If the application was designed to primarily use GET requests to transfer parameters and execute actions, the money transfer operation might be reduced to a request like:

 GET https://bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1

Maria now decides to exploit this web application vulnerability using Alice as her victim. Maria first constructs the following exploit URL which will transfer $100,000 from Alice's account to her account. She takes the original command URL and replaces the beneficiary name with herself, raising the transfer amount significantly at the same time:

 https://bank.com/transfer.do?acct=MARIA&amount=100000

The social engineering aspect of the attack tricks Alice into loading this URL when she's logged into the bank application. This is usually done with one of the following techniques:

  • sending an unsolicited email with HTML content
  • planting an exploit URL or script on pages that are likely to be visited by the victim while they are also doing online banking

The exploit URL can be disguised as an ordinary link, encouraging the victim to click it:

 <a href="https://bank.com/transfer.do?acct=MARIA&amount=100000">View my Pictures!</a>

Or as a 0x0 fake image:

 <img src="https://bank.com/transfer.do?acct=MARIA&amount=100000" width="0" height="0" border="0">

If this image tag were included in the email, Alice wouldn't see anything. However, the browser will still submit the request to bank.com without any visual indication that the transfer has taken place.

A real life example of CSRF attack on an application using GET was a uTorrent exploit from 2008 that was used on a mass scale to download malware.

POST scenario

The only difference between GET and POST attacks is how the attack is being executed by the victim. Let's assume the bank now uses POST and the vulnerable request looks like this:

 POST https://bank.com/transfer.do HTTP/1.1

acct=BOB&amount=100

Such a request cannot be delivered using standard A or IMG tags, but can be delivered using a FORM tag:

 <form action="https://bank.com/transfer.do" method="POST">
<input type="hidden" name="acct" value="MARIA"/>
<input type="hidden" name="amount" value="100000"/>
<input type="submit" value="View my pictures"/>
</form>

This form will require the user to click on the submit button, but this can be also executed automatically using JavaScript:

 <body onload="document.forms[0].submit()">
<form...
 Other HTTP methods

Modern web application APIs frequently use other HTTP methods, such as PUT or DELETE. Let's assume the vulnerable bank uses PUT that takes a JSON block as an argument:

 PUT https://bank.com/transfer.do HTTP/1.1

{ "acct":"BOB", "amount":100 }

Such requests can be executed with JavaScript embedded into an exploit page:

 <script>
function put() {
 var x = new XMLHttpRequest();
   x.open("PUT","https://bank.com/transfer.do",true);
   x.setRequestHeader("Content-Type", "application/json"); 
    x.send(JSON.stringify({"acct":"BOB", "amount":100})); 
}
</script>
<body onload="put()">

Fortunately, this request will not be executed by modern web browsers thanks to same-origin policy restrictions. This restriction is enabled by default unless the target web site explicitly opens up cross-origin requests from the attacker's (or everyone's) origin by using CORS with the following header:

Access-Control-Allow-Origin: *

COPIED ABOVE EXAMPLE from https://www.owasp.org/index.php/Cross-Site\_Request\_Forgery\_(CSRF).

I have few customers who have complained about the app scanner complaining about some of the custom and OOB SharePoint page. So, i decided to test this thoroughly and here is what I have come up with:

When I’d looked at the GET and POST requests,

GET https://sharepoint.com/teams/NA/Shared%20Documents/Forms/AllItems.aspx HTTP/1.1

Accept: */*

Referer: https://sharepoint.com/teams/NA/Lists/Analysis/AllItems.aspx?PageView=Shared&InitialTabId=Ribbon.WebPartPage&VisibilityContext=WSSWebPartPage

Response has the below Canary value:

<input type="hidden" name=" __REQUESTDIGEST" id="__REQUESTDIGEST" value="0x0A54D6ED3EFEF916EB4F223EE3ECAE429FD9A467E32E17EC1904B111462C345CC1BD670569370B7327A7FF4090A81465A5823E9FC2948C351CB1F91499A9832C,12 Mar 2016 18:27:50 -0000" />

WebForm_InitCallback();var _spFormDigestRefreshInterval = 1440000;

I’d then uploaded a document to the SharePoint library and I could still track the same canary value there too.

POST https://sharepoint.com/teams/NA/_layouts/15/upload.aspx?IsAJAX=1&List={84CB65BE-0E08-4E81-B1BE-65852A651427}&RootFolder=%2Fteams%2FNA%2FShared%20Documents HTTP/1.1

Accept: */*

x-requestdigest: 0x0A54D6ED3EFEF916EB4F223EE3ECAE429FD9A467E32E17EC1904B111462C345CC1BD670569370B7327A7FF4090A81465A5823E9FC2948C351CB1F91499A9832C,12 Mar 2016 18:27:50 -0000

ClientSource: MultiFileUploadDialog

Content-Type: multipart/form-data; boundary=---------------------------7dffe6f15b6

Response:

HTTP/1.1 200 OK

Set-Cookie: FedAuth=77u/PD94bW**************; expires=Tue, 17-Mar-2016 14:42:41 GMT; path=/; secure; HttpOnly

X-AspNet-Version: 4.0.30319

SPRequestGuid: b316f29c-a029-0000-252d-765f0416cabd

X-RequestDigest: 0x471DB2C0D2C3CA58E87CAC38EDFA08F1A63BD03F1A1B454EB5A16886F2BF908AEBDD123F55F0A9C5BE489AF025711D7152A91E00264FB14A77595429F1F4534A,12 Mar 2016 18:28:12 -0000

X-FRAME-OPTIONS: SAMEORIGIN

X-Powered-By: ASP.NET

But the same wasn’t tracked when I loaded adminrecyclebin.aspx
When accessing the home page:
GET https://sharepoint.com/teams/Online/SitePages/Dashboard!.aspx HTTP/1.1

Response has the below canary:

<input type="hidden" name="__REQUESTDIGEST" id="__REQUESTDIGEST" value="0xF5EA17A429B1B52908BCC4E34D65E7F4E0C39CEE228CE4AA930BF132E4C9C99916627E46427D1E205634816F1E68112A93916ACCAC3222F07E1892A5F9E64BDB,12 Mar 2016 19:39:45 -0000" />

When accessed the adminrecyclebin.aspx page, I see the below traces:

GET https://sharepoint.com/teams/Online/_layouts/15/AdminRecycleBin.aspx HTTP/1.1

<input type="hidden" name="__REQUESTDIGEST" id="__REQUESTDIGEST" value="0x9585697B2B7DAD7948FE350B2E4CCAAD26F83DDE13DB2BF9A2FDC8C7274837A6E576BCF97BB575BB0234E773BD444918CC61CCB8E33C8A395A63FE4F6A0A51E6,12 Mar 2016 19:39:55 -0000" />

We could see that there is no canary value which is sent in the header but the request and response tracked via X-FRAME-OPTIONS: SAMEORIGIN

This explains on why we are seeing the CSRF issues while accessing the SharePoint OOB pages. We don't generate __REQUESTDIGEST values for some SharePoint OOB pages which have no POST actions.

The canary value is already present in a hidden  __REQUESTDIGEST form element on every page that uses the SharePoint master page. It is automatically sent with every postback.

If you do not inherit from the SharePoint master page, you must use the Microsoft.SharePoint.WebControls.FormDigest control to write the value into the page. If you are making a web service call, you must get the  __REQUESTDIGEST value and include it in the X-RequestDigest HTTP request header, as shown in the following example.

C#

var canaryValue = document.getElementById('__REQUESTDIGEST').value;request.SetRequestHeader("X-RequestDigest", canaryValue);

Ref: https://msdn.microsoft.com/en-us/library/office/gg552614(v=office.14).aspx

SharePoint uses a dynamic canary to ensure that POST requests come from the same domain as the server.

You must do two things on your custom pages and since SharePoint is ASP.NET backed, we've this implemented by default in OOB pages.

  • Send the canary with every postback or web service request.
  • Validate the canary before acting on the postback or web service request.

So, in your custom application/page which you build in top of SharePoint, ensure that you utilize <SharePoint:FormDigest runat="server"/> control in the master page or the page itself and track the POST requests by utilizing SPUtility.ValidateFormDigest(). This will ensure that the initiator is making the request through the same browser session is making the post back. If the Canary value isn't present or tapered, you would get a 401 Unauthorized error.

Dev Explanation for custom pages is in here:

https://www.codeproject.com/Articles/686881/Hack-Proof-Your-ASP-NET-Applica

https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks

If you have any further questions, please comment in here or contact Microsoft Support