Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-64640 — Reproducer for CVE-2026-64640 — Apache Polaris Iceberg REST register/register-view vends storage credentials and reads an attacker-chosen metadata location before validating allowedLocations (confused-deputy cross-tenant read). Affected ≤ 1.6.0, fixed in 1.7.0. | Kitploit
Herramientas/GitHubGitHub/oscerd/cve-2026-64640
ReconnaissanceVulnerability AnalysisExploitationWeb Application ExploitationData ExfiltrationPenetration TestingCloud SecurityAPI Security
GitHuboscerd/cve-2026-64640

CVE-2026-64640

Reproducer for CVE-2026-64640 — Apache Polaris Iceberg REST register/register-view vends storage credentials and reads an attacker-chosen metadata location before validating allowedLocations (confused-deputy cross-tenant read). Affected ≤ 1.6.0, fixed in 1.7.0.

hace 14 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio

CVE-2026-64640 — Apache Polaris: emisión de credenciales antes de la validación de ubicación en register

Un reproductor autónomo de un solo comando para CVE-2026-64640: los endpoints register de Iceberg REST en Apache Polaris generan credenciales de almacenamiento en la nube para una ruta proporcionada por el llamante y leen esa ruta en el lado del servidor antes de verificarla contra los allowedLocations del catálogo.

Una entidad (principal) cuyo único privilegio es crear tablas en su propio catálogo puede hacer que Polaris use las credenciales de almacenamiento del catálogo para leer objetos que el catálogo nunca tuvo permitido tocar — el prefijo de otro tenant, otro bucket, cualquier cosa a la que el principal de almacenamiento pueda acceder.

CVECVE-2026-64640
Componentepolaris-runtime-service — IcebergCatalog / LocalIcebergCatalog: registerTable, registerView
EndpointsPOST /api/catalog/v1/{prefix}/namespaces/{namespace}/register
POST /api/catalog/v1/{prefix}/namespaces/{namespace}/register-view (1.6.0+)
CWECWE-441 (subordinado confundido), CWE-639 (bypass de autorización mediante clave controlada por el usuario), CWE-918 (SSRF)
GravedadAlta — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N (8.1)
Afectadas≤ 1.6.0 (verificado en 1.3.0-incubating, 1.4.0, 1.4.1, 1.5.0, 1.6.0)
Corregida en1.7.0 — 1.6.0 corrige solo la ruta de tablas y reintroduce el fallo en la nueva ruta de vistas
Privilegio requeridoun principal autenticado con TABLE_CREATE / CATALOG_MANAGE_CONTENT en cualquier catálogo — sin administrador

1.6.0 no es una corrección. Cierra register (incidentalmente, dentro de un PR de funcionalidad) y entrega el nuevo endpoint register-view con el mismo error de orden. El reproductor demuestra ambas partes. Actualiza a 1.7.0.

Ejecutarlo

Requisitos: Docker con el plugin Compose v2, curl, python3, bash. Nada más: el entorno se construye a partir de imágenes publicadas y se elimina al final.

root@kitploit:~
./exploit.sh                 # predeterminado: apache/polaris:1.4.1  -> reproduce vía register
./exploit.sh --tag 1.6.0     # parcialmente corregido               -> reproduce vía register-view
./exploit.sh --tag 1.7.0     # corregido de forma integral          -> rechaza limpiamente en ambos
./scripts/version-matrix.sh  # todas las versiones, lado a lado

El código de salida 0 significa que la vulnerabilidad se reprodujo, 1 que no se reprodujo, 2 que el entorno no logró levantarse. Cada petición y respuesta se escribe en evidence/<timestamp>-polaris-<tag>/.

Cómo se ve el entorno

root@kitploit:~
  catalog  tenant_a_catalog        allowedLocations = [ s3://bucket123 ]
  actor    low_priv_user           CATALOG_MANAGE_CONTENT on that catalog, nothing else
  target   s3://tenant-b-private   another tenant's bucket:
                                     no allowedLocations entry, no grant, no relation
                                     to the attacker's catalog — but reachable by the
                                     credentials that back it

Esa última línea es la parte realista. Los operadores acotan un catálogo con allowedLocations; el rol IAM subyacente casi siempre es más amplio que un solo prefijo. allowedLocations es el muro. Este error lo rodea.

Qué demuestra

Prueba 0 — el muro es real. Crear una tabla con una ubicación explícita en s3://tenant-b-private se rechaza con 403 ForbiddenException, antes de que se lea nada. Polaris sabe perfectamente que la ubicación está fuera de los límites.

Prueba 1 — register la lee de todos modos. El mismo principal apunta register a s3://tenant-b-private/sales/metadata/00007-tenant-b-sales.metadata.json. La respuesta también es un 403, pero cita s3://tenant-b-private/warehouse/CANARY-64640-4f1c9e2a-tenant-b-sales, una cadena que existe solo dentro del cuerpo de ese objeto:

root@kitploit:~
{"error":{"message":"Invalid locations '[s3://tenant-b-private/warehouse/sales/data,
s3://tenant-b-private/warehouse/CANARY-64640-4f1c9e2a-tenant-b-sales]' for identifier
'tenant_a_ns.pwn_canary': s3://tenant-b-private/warehouse/sales/data is not in the list
of allowed locations: [s3://bucket123/tenant_a_ns]","type":"ForbiddenException","code":403}}

La petición nunca menciona warehouse/. Polaris solo pudo producir esas cadenas obteniendo y analizando el objeto con las credenciales del catálogo. El 403 es Polaris detectando la violación un paso después de la lectura que se suponía que debía evitar.

Prueba 2 — lo que devuelve. Dos campos distintos extraídos del documento víctima se devuelven al llamante: la location declarada de la tabla y su propiedad write.data.path.

Prueba 3 — un oráculo de enumeración de almacenamiento. La misma llamada responde de forma distinta para cada estado de un objetivo fuera de allowedLocations:

Cuatro respuestas distinguibles significan cuatro datos sobre el almacenamiento que el llamante no tiene derecho a conocer. En AWS real, la sonda de existencia de bucket alcanza el espacio de nombres global de buckets de S3, usando las credenciales del catálogo.

En una compilación corregida, cada fila de esa tabla es el mismo 403 que menciona solo la ruta solicitada, y no vuelve nada del interior del objeto; el script detecta esa firma e informa NOT VULNERABLE — pre-validation observed.

Prueba 4 — el mismo fallo en register-view. Polaris 1.6.0 añadió POST .../namespaces/{ns}/register-view, que llega a registerView, el cual carga el FileIO y analiza el documento del llamante sin validación previa — exactamente lo que registerTable acababa de dejar de hacer. En 1.6.0 el canario de vista devuelve:

root@kitploit:~
{"error":{"message":"Invalid locations '[s3://tenant-b-private/warehouse/CANARY-64640-VIEW-8d3b7a15-tenant-b]'
for identifier 'tenant_a_ns.pwn_view': … is not in the list of allowed locations:
[s3://bucket123/tenant_a_ns]","type":"ForbiddenException","code":403}}

Así que un despliegue 1.6.0 sigue expuesto, a través de un endpoint diferente, a la misma primitiva. En 1.4.1 y anteriores el endpoint no existe y el script lo indica.

Causa raíz

IcebergCatalog.registerTable (LocalIcebergCatalog desde 1.6.0), antes de la corrección — registerView tiene la misma forma:

root@kitploit:~
String locationDir = metadataFileLocation.substring(0, lastSlashIndex);   // attacker-controlled
...
FileIO fileIO =
    loadFileIOForTableLike(
        identifier,
        Set.of(locationDir),                                              // credentials minted here
        resolvedParent,
        new HashMap<>(tableDefaultProperties),
        Set.of(PolarisStorageActions.READ, PolarisStorageActions.LIST));

InputFile metadataFile = fileIO.newInputFile(metadataFileLocation);       // server-side GET
TableMetadata metadata = TableMetadataParser.read(metadataFile);          // server-side parse
ops.commit(null, metadata);                                               // allowedLocations checked HERE

La cadena de emisión (loadFileIOForTableLike → StorageAccessConfigProvider.getStorageAccessConfig → *StorageIntegration.getSubscopedCreds) no realiza ninguna comprobación propia de allowedLocations, así que la única aplicación de la política es la del momento del commit — y para entonces la lectura privilegiada ya ha ocurrido y su resultado está en la respuesta.

Las rutas hermanas en el mismo archivo hacen el orden correcto: la creación de vistas y sendNotificationForTableLike ambas validan antes de cargar el FileIO. registerTable era la inconsistente. Recorrido completo en docs/ANALYSIS.md.

La corrección, en dos partes

Ruta de tablas — commit 1dd5feeb (2026-06-02, publicado por primera vez en 1.6.0) añadió una línea en el lugar correcto:

root@kitploit:~
validateLocationForTableLike(identifier, metadataFileLocation, resolvedParent);

FileIO fileIO = loadFileIOForTableLike(identifier, Set.of(locationDir), ...);

Llegó dentro de un PR de funcionalidad («add RegisterTable overwrite support»), no como corrección de seguridad, así que 1.6.0 distribuyó la corrección sin ningún aviso que la nombrara.

Ruta de vistas — commit 7e822f23 («Validate locations when registering tables and views», #5114), 2026-07-20, publicado por primera vez en 1.7.0, añadió la misma protección a registerView y consolidó las comprobaciones posteriores al análisis para ambas rutas.

1.7.0 también incluye 85a0c292 (#4860, «Fix native catalog credential vending skipping allowedLocations re-validation»), que cierra la brecha relacionada de defensa en profundidad: la propia ruta de credenciales ahora revalida en lugar de confiar en cada llamante. Esa es la mitad estructural del problema, y es otra razón por la que 1.7.0, y no 1.6.0, es la versión en la que hay que estar.

La contención se verificó contra las etiquetas de lanzamiento: 1dd5feeb está en 1.6.0 y 1.7.0; 7e822f23 y 85a0c292 solo están en 1.7.0.

Para quien esté anclado a una línea anterior, ambos cambios se proporcionan como parches: 0001 para la ruta de tablas ≤ 1.5.0, 0002 para la ruta de vistas 1.6.0.

La guía para operadores — ruta de actualización, mitigaciones y cómo buscar explotación en registros existentes — está en docs/REMEDIATION.md.

Matriz de versiones verificadas

Producida con ./scripts/version-matrix.sh; cada fila es un arranque completo de la pila y un intento de explotación en vivo. Ver docs/AFFECTED-VERSIONS.md.

1.4.1 importa: es la versión que corrigió CVE-2026-42809, el mismo error de validar después de emitir en la ruta stage-create. Esa corrección se limitó al endpoint del informe, y el patrón se repitió entonces dos veces: register mantuvo el comportamiento hasta 1.6.0, y el nuevo register-view de 1.6.0 nació con él.

Alcance y honestidad sobre el impacto

Lo que este reproductor demuestra, y nada más:

  • Polaris realiza una lectura en el lado del servidor con credenciales emitidas de una ubicación arbitraria elegida por el atacante, fuera del límite de almacenamiento declarado del catálogo — a través de register en ≤ 1.5.0 y a través de register-view en 1.6.0.
  • Los campos con forma de ubicación extraídos de ese objeto se devuelven al llamante.
  • La existencia de objetos, la existencia de buckets y el tipo de objeto son observables en todo lo que el principal de almacenamiento del catálogo puede alcanzar.

Lo que no demuestra: la recuperación masiva del contenido completo de un objeto fuera de alcance a través de la API. Dos comprobaciones posteriores (la ubicación de metadatos debe estar bajo la ubicación de la tabla, y la ubicación analizada debe estar en allowedLocations) impiden que el registro se complete, así que el llamante obtiene un oráculo más fragmentos de metadatos, no el documento completo. Con una anulación del endpoint de S3 configurada en el catálogo, la misma primitiva es una falsificación de petición en el lado del servidor contra el host configurado.

Seguridad

Todo se ejecuta localmente: un proyecto Docker Compose en puertos de loopback, una instancia S3 desechable (RustFS) y archivos sintéticos de «víctima» que este repositorio planta por sí mismo. No se contacta ningún host externo, ninguna credencial sale de la máquina, y docker compose down -v se ejecuta al salir a menos que pases --keep. Los objetos de victim-data/ son fabricados; no hay datos reales en ninguna parte de aquí.

Úsalo en sistemas que sean tuyos o para los que estés autorizado a hacer pruebas.

Crédito y divulgación

Encontrado durante una revisión de la corrección de CVE-2026-42809, notificado al equipo de seguridad de la Apache Software Foundation a través del proceso descrito en el SECURITY.md del proyecto, y registrado como CVE-2026-64640.

Licencia

Apache License 2.0 — ver LICENSE. El entorno Compose se deriva del quickstart de Apache Polaris; ver NOTICE.

Descargar herramienta
sonda (todas fuera de allowedLocations)respuesta de 1.4.1
metadatos Iceberg válidos, existe403 ForbiddenException + ubicaciones analizadas devueltas
clave inexistente400 NotFoundException — Location does not exist: …
bucket inexistente400 NoSuchBucketException — error crudo del SDK de S3
existe pero no son metadatos Iceberg503 RuntimeIOException — Failed to read file: …
Versiónregister (tabla)register-viewVeredicto
1.3.0-incubatingvulnerableendpoint ausentevulnerable
1.4.0vulnerableendpoint ausentevulnerable
1.4.1vulnerableendpoint ausentevulnerable
1.5.0vulnerableendpoint ausentevulnerable
1.6.0validado antes de emitirvulnerablevulnerable
1.7.0validado antes de emitirvalidado antes de emitircorregida