- Critical Thinking - Bug Bounty Podcast
- Posts
- [HackerNotes Ep.108] How to Hack Salesforce, ServiceNow, and Other SaaS Products With Aaron Costello
[HackerNotes Ep.108] How to Hack Salesforce, ServiceNow, and Other SaaS Products With Aaron Costello
Justin and Joseph bring on Aaron Costello to discuss SaaS security and misconfigurations as a bug class. He also gives some in-depth examples on research he's performed on Salesforce, ServiceNow, and Power Pages.
Hacker TLDR;
Hacking SaaS Platforms: Chaining traditional bug classes with SaaS misconfiguration can lead to big impact. Aaron detailed a pretty nice chain to hit a SaaS support engineer:
Misconfigured file upload to bucket → Upload malicious AppCache manifest → Raise support engineer request → Cookie bomb & DoS support engineers browser → AppCache payload kicks in → URL harvest from support engineer.
SaaS Misconfigurations as a Bug Class: Misconfigurations scale across thousands of instances-find one, and you could have hundreds of targets.
Think N-day research: Many orgs fail to apply recommended configurations & patches.
The SaaS Methodology: Aaron has dropped a tonne of personal research and research at AppOmni. There are no shortcuts to becoming an expert on a SaaS platform, but Aarons's research is a great starting point:
ServiceNow: https://www.enumerated.ie/index/servicenow-data-exposure
Widgets & UX Data Brokers: Looking for available widgets and API endpoints that can be abused to return data from various perspectives (non-authenticated, guest, low privilege, support requests) can provide an initial foothold into a lot of these sensitive API calls. Once found, these can be abused to leak data on a given object in ServiceNow if misconfigured with weak access controls.
Guest User Access Mis-configs: More often than not guest users will be treated as normal authenticated users. Giving any guest user access extra attention might mean you can hit a lot of APIs you aren’t supposed to.
SalesForce: www.enumerated.ie/index/SalesForce & https://go.appomni.com/hubfs/Collateral/AppOmni_Labs_White_Paper_Apex_Security.pdf
Map the Aura API: Use client-side enumeration to find objects & accessible Apex classes. You can think of Aura as the front end and Apex as the server-side logic.
Check if "noSharing" is used: Check Apex for calls with the
noSharing
keyword. This can bypass access controls inadvertently and lead to misconfigurations.Exploit Community User context: Sign up as a low-privileged user and see what objects are exposed. This might allow you to run Apex using an exposed execute anonymous API, which may also let you access named credentials.
Check for brute-forceable Named Credentials: Named credentials allow credentials to be stored and used within Apex. Each credential is given a label and can be set to
principal
mode - meaning credentials are shared and accessed all users specified. Bruteforcing credential labels can allow you access to the credential.
Microsoft Power Pages: https://appomni.com/ao-labs/microsoft-power-pages-data-exposure-reviewed/
Sign up as a portal user & export the schema: this can give a solid default wordlist to work from across all Power Pages deployments.
Look for exposed OData endpoints: Calls and references to endpoints with the naming convention (
/_api/[objname]
) will allow you to enumerate object names and calls associated with the target.Test different web roles: Self-signup is common on these SaaS apps and can unlock misconfigured and sensitive API calls. If a signup is available, register and view the calls to the OData endpoints.
Check domain patterns: Many Power Pages follow the naming convention
[something].powerappsportals.com

The White House has recently banned the use of Kaspersky products across the United States, citing concerns over potential data access risks and allegations of incorporating "backdoors" in their software.
In response, ThreatLocker®, a prominent provider of Zero Trust cybersecurity solutions, has introduced a complimentary system cyber health report. This tool offers businesses insights into their IT environments, highlighting vulnerabilities associated with foreign software. This proactive measure assists organizations in assessing and securing their software ecosystems, ensuring compliance and preemptively addressing security threats. While acknowledging national security concerns raised by foreign software, ThreatLocker® emphasizes the broader scope of vulnerabilities, exemplified by incidents like the 2020 SolarWinds attack. The ThreatLocker® cyber health report comprehensively details all applications in use, their countries of origin, and potential data access implications, promoting informed decision-making and robust security protocols.
Learn More About the ThreatLocker® Cyber Health Report Here: https://www.criticalthinkingpodcast.io/chr
Hacking SaaS Platforms
If you aren’t familiar with Aaron, he’s the Chief of SaaS Security Research over at AppOmni. Luckily for us, he’s popped just about every major SaaS (with some exceptions) platform there is, and had an absolute treasure trove of tips to share on the pod.
One of his notable bugs combined traditional XSS techniques with a nice SaaS misconfiguration in SalesForce.
The chain started with a file upload to an external bucket provider. Why this initially stood out to Aaron is because SalesForce uses its own file upload facility, and in this case, it was using a custom bucket to upload to.
Notably, the bucket wasn’t performing any form of type validation, which meant any file type could be uploaded, including types that allowed XSS. It turned out it was possible to overwrite another user’s file and overwrite arbitrary contents in the bucket which had some impact, but due to identifiers being used, the attack complexity was high.
Now this is where it gets cool. As the file upload was on a different origin than the main app, any kind of traditional file upload attack or XSS’s impact was greatly reduced. This is where an AppCache manifest comes in handy.
An AppCache manifest helps an application serve content - even if the network drops unexpectedly. You list what files should be cached, how to handle files that aren’t cached, and potential fallback pages. This makes sure users don’t stare at an error screen if they happen to go offline while browsing.
Using this exact behaviour, Aaron uploaded an AppCache with his payload specified as the fallback for all URLs. The payload exfil’d all URLs that were redirecting to it straight to his webserver.
Now, if you specify your payload as a fallback, you need a means of ensuring the fallback actually kicks in. Combine this with cookie bombing, and you have a means of ensuring it kicks in. Cookie bombing allows you to fill the user’s browser with enough cookies to DoS their session, which in this context would kick the fallback (payload) in.
So, Aarons’ attack chain from here:
Uploads a manifest, when a support engineer goes to view the file it loads the manifest, and DoS’s the browser. The support engineer would then view another case (as a support engineer would likely do), and when they view another case, Aarons's fallback will kick in from the app manifest. This then allows Aaron to harvest the location being redirected from (another user’s file) which means the full path to the bucket and signing key is sent across.
To summarise the attack chain in its simplest form: Misconfigured file upload → upload a malicious AppCache manifest → raise support engineer request with the file → cookie bomb and DoS the browser → harvest the URL of the next visited file.
Pretty cool huh?
This was a really clever move. Instead of targeting another end user through the file upload feature, Aaron went after the SaaS application’s support engineer - someone with a higher level of access. Ensuring you understand the app’s threat model and pivoting the attack toward a privileged user, it significantly amplified its impact.
As we always say: understand your target’s threat model!
SaaS Misconfigurations as a Bug Class
SaaS misconfiguration as a bug class can be quite fruitful from a bug bounty perspective. Instead of a standard custom-built app where you may only find 1 or 2 instances of a certain type of bug, SaaS misconfiguration can apply to many hundreds/thousands of active instances that are deployed.
You can think of it as N-day type research, whereby a vendor might fix something and provide a patch or suggest a new config to mitigate something, but the chances are not every customer has applied the patch or config.
Equally, the amount of flexibility these SaaS platforms have to supply for their broad customer base is insane. Every customer will have slightly different use cases that have to be supported, so that means a tonne of configurations and custom integrations that can go wrong and ultimately, give us more attack surface.
If you do decide to look at hunting some of these SaaS platforms on targets, from Aarons's experience, there’s usually initial pushback when they're trying to understand the bug and how it actually became vulnerable in the first place. After contacting the vendor for support, the vendor usually confirms it is in fact a misconfiguration which then results in the validation of the bug.
Equally, if some bugs are dependent on a default configuration, it can become a grey area for some targets. It’s not all bad though, as the attack surface is HUGE.
Let’s jump in!
The SaaS Methodology
When approaching platforms to hunt on, Aaron doesn’t start by looking straight for bugs. He instead becomes familiar with the API, the classes, the calls, the layout and the structure... essentially becoming an expert on the technology, and then starts to drill in and look for bugs.
Luckily, there’s quite a bit of research he’s published on each of these SaaS platforms that he has become an expert on. Let’s take a look:
ServiceNow
One example of this mastery is with some of his ServiceNow bugs. Some of these bugs allowed an attacker to provide misconfigured ServiceNow instances a table name within a request, which allowed an unauthenticated remote attacker to directly read from tables of their choice and disclose all of the contents... insane stuff!
To get to that point, Aaron went through the database schema and read tables one by one until he had an understanding of each table’s purpose and how it was interacted with and used.
I don’t know what ServiceNow’s schema looks like but I bet it’s huge and incredibly difficult to understand. This is the sort of insight you can only gain by spending a serious amount of hours, peeling back a target to a level no one else has.
With the release of this research, ServiceNow stepped in and saved their customers by mass deploying a config change which is absolutely INSANE.
Aaron notes some high-signal areas in ServiceNow if you want to start some research of your own:
Widgets & UX Data Brokers: Looking for available widgets and API endpoints that can be abused to return data from various perspectives (non-authenticated, guest, low privilege, support requests) can provide an initial foothold into a lot of these sensitive API calls. Once found, these can be abused to leak data on a given object in ServiceNow if misconfigured with weak access controls.
Guest User Access Mis-configs: More often than not, guest users will be treated as normal authenticated users. Giving any guest user access extra attention might mean you can hit a lot of APIs you aren’t supposed to.
If you want to get more of an idea of the methodology and approach, you can check out the full research here: https://www.enumerated.ie/index/servicenow-data-exposure
SalesForce
This research was a banger - dropped in 2020 but still prevalent everywhere. It focused on SalesForce’s Lightning aura functionality and essentially resulted in a tonne of default and custom configurations that allowed massed data to be leaked, IDOR style.
If you aren’t familiar with SalesForce, there are components that tie together in order to provide the customisability that SalesForce offers. You have Apex components and Aura components. You can think of Aura as the front end and Apex as the server-side logic.
Aura components are front-end components, i.e. a button or form, and aurora components are paired with an apex class that is aura-enabled. The aura-enabled Apex provides the business logic (serverside logic) to the functionality of that Aura component.
When apex is aura enabled, it means it is accessible to the aura API. If you look at the payloads in the article, they are being sent to the aura API as this is the API that is accessible to the accounts context.
If you want an easy way to remember custom v standard configurations: Aura enabled apex == custom API endpoints.
Now what’s interesting, it’s possible to accidentally develop apex in such a way that it doesn’t actually adhere to access controls at all. One of the ways this can happen is via the call to the noSharing
keyword, and it essentially runs the call in a completely different context. More can be found on this specific edge case here: https://developer.SalesForce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_keywords_sharing.htm
Using the payloads in the original research, you can look for objects that are publicly accessible and subsequently find ones you can read data from. From here, you can enumerate object names and properties, and then iterate over these with a separate call to read these too.
The blog details the exact approach on how to use these calls in conjunction, which we highly recommend reading, but a full list of these calls taken from the blog, can be found below:
SelectableListDataProviderController/ACTION$getItems - Returns pageSize amount of records from all fields in a specific object.
entityNameOrId - Object name
{"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.lists.selectableListDataProvider.SelectableListDataProviderController/ACTION$getItems","callingDescriptor":"UNKNOWN","params":{"entityNameOrId":"MARKER","layoutType":"FULL","pageSize":100,"currentPage":0,"useTimeout":false,"getCount":false,"enableRowActions":false}}]}
HostConfigController/ACTION$getConfigData - Returns all currently available objects
{"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.hostConfig.HostConfigController/ACTION$getConfigData","callingDescriptor":"UNKNOWN","params":{}}]}
ProfileMenuController/ACTION$getProfileMenuResponse - Returns minor current user details
{"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.self.service.components.profileMenu.ProfileMenuController/ACTION$getProfileMenuResponse","callingDescriptor":"UNKNOWN","params":{}}]}
ScopedResultsDataProviderController/ACTION$getLookupItems - Returns pageSize amount of records for a particular object that includes a specific term in a row.
scope - Object name
term - Search term, minimum 4 characters
additionalFields - Any other fields in the object that you wish to be returned in the record
Definition - /auraCmpDef?aura.app=markup://siteforce:communityApp&_au=<VALUE>&_def=markup://forceSearch:resultsGridLVMDataManager
Payload - {"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.search.components.forcesearch.scopedresultsdataprovider.ScopedResultsDataProviderController/ACTION$getLookupItems","callingDescriptor":"UNKNOWN","params":{"scope":"User","term":"script","pageSize":10,"currentPage":1,"enableRowActions":false,"additionalFields":[],"useADS":false}}]}
DetailController/ACTION$getRecord - Returns the data in a given record ID
recordId - ID of the record to query
{"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.detail.DetailController/ACTION$getRecord","callingDescriptor":"UNKNOWN","params":{"recordId":"<RECORD_ID>","record":null,"inContextOfComponent":"","mode":"VIEW","layoutType":"FULL","defaultFieldValues":null,"navigationLocation":"LIST_VIEW_ROW"}}]}
ApexActionController/ACTION$execute - Alternative way to call an apex class method
classname - Name of apex class
method - Method being called
params - Parameter and value pairs taken by method
{"actions":[{"id":"123;a","descriptor":"aura://ApexActionController/ACTION$execute","callingDescriptor":"UNKNOWN","params":{"namespace":
At the time the research was published, you were able to access data specified in custom settings but that has since changed.
Named credentials
Some other common misconfiguration in SalesForce lies within its credential management. Named credentials in SalesForce is a method in which SalesForce configurations can use to integrate with an external platforms.
Once you save credentials you can call them directly from from Apex. This means that developers can call external systems in an authenticated context without having to embed credentials directly in the apex code.
When it comes to the implementation of named credentials, there’s two ways to set this up:
Principle: Every user performing the call uses the same set of credentials
Per user: each user has their own set of credentials they use
Now, as you can imagine, most are configured on a principle basis, where everyone has the same set of credentials. Why is this important?
In a misconfigured SalesForce Community, if you can sign up as a Community user, you might gain the ability to run Apex code using an exposed "execute anonymous" API (which normally shouldn’t be accessible to Community users). If named credentials are set up with a single principal (i.e., everyone shares the same credential), you could potentially brute force the label (or name) of that credential and then invoke it to access resources intended only for higher-privileged users.
Crazy, right?
If you’re looking to dig on a target yourself, here are some key areas to watch out for:
Objects (Tables) via the Aura API
Check which objects you can access directly through Aura endpoints.
Exposed Apex Classes
Enumerate client-side and see which classes you can actually call, and what level of access they grant.
Community User Context
Sign up as a Community user to become “semi-authenticated,” then iterate over objects mentioned in articles and see what permissions you have under that context.
Privilege Escalation via Unauthenticated Apex
Take what you learned from enumerating objects as an authenticated user and see if you can use that knowledge on an unauthenticated Apex endpoint for more critical impact.
SOQL Injection
Watch for possible query injection flaws. However, these typically allow read operations only and are often restricted to the objects included in the query.
Endpoints to search for that would suggest you’re talking to a SalesForce site:
/aura
/s/
/sfsites
Aaron and AppOmni also released an entire whitepaper on Apex Security. You can check it out below: https://go.appomni.com/hubfs/Collateral/AppOmni_Labs_White_Paper_Apex_Security.pdf?utm_campaign=Network
Microsoft Power Pages
This research was no different than the last, but this time on Microsoft Power Pages. If you aren’t familiar with Power Pages, Microsoft Power Pages is a low-code/no-code platform for creating sites that can easily integrate into existing MS services.
Aarons's research concluded that it can be widely misconfigured in ways that inadvertently expose data, and from his research identified information on healthcare services that exposed 1 million + records of users.
If you want to look at Power Pages yourself, unfortunately, you can’t just blindly enumerate all custom table names in Power Pages. What you can do, however, is sign up as a regular portal user, and then export the schema.
That export includes a handy wordlist of all the default object names (and in some misconfigured instances, partial access to additional tables). This can be a goldmine for understanding potential data structures.
It’s worth noting that Power Pages has a lot more customisation on the object names than the other platforms mentioned. Although you can get a good wordlist for default object names, there’s no solid way of getting a generic list of all object names, as customers configure their own object names during setup.
To enumerate Power Pages sites, most follow a predictable domain pattern - often something like [something].powerappsportals.com
. Once discovered, you should analyze how the portal is configured, which OData endpoints are exposed, and what default web roles or permissions are inadvertently granted to the public or to newly registered users.
From there, it’s a case of mapping out accessible schema information from the user’s context you have access to. To do so, keep an eye out for calls and references to /_api/[objname]
endpoints, as this is where all the calls to the objects happen.
Aaron does an excellent job at laying out the process of enumerating, identifying, and exploiting the configurations in Power Pages in the research below. Be sure to check it out:
Aaron noted some bills can rack up to $20k A MONTH just for using some of these platforms. He suggested a few bug bounty hunters grouping together on a license and wrecking it for a while. Anyone wanna chip in and give it a shot? 😅
Some of the other big SaaS companies he suggested looking at include: SAP, Adobe and some other SalesForce components.
If you want to know where you can find Aaron and his research, his socials and research can be found below:
And that’s a wrap. Not sure about ya’ll but I certainly learned a lot from this week’s pod!
As always, keep hacking!