
Demuestra la escritura arbitraria de archivos de CVE-2026-18953 en la herramienta get_resource de un servidor MCP abusando del path traversal de savePath; incluye validadores vulnerables y corregidos (vendored) para su verificación.
Escritura arbitraria de archivos en awslabs.aws-transform-mcp-server (servidor MCP
de AWS Transform) a través del parámetro savePath de la herramienta get_resource.
| CVE | CVE-2026-18953 |
| CWE | CWE-22 — Limitación inadecuada de un nombre de ruta a un directorio restringido |
| Afectado | awslabs.aws-transform-mcp-server 0.1.0 – 0.1.4 |
| Corregido en | 0.1.5 |
| CVSS v3.1 | 8.6 ALTA — AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H |
| CVSS v4.0 | 6.3 MEDIA — AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:H |
| Aviso | GHSA-66mr-jr63-2jgw |
| Boletín | Boletín de seguridad de AWS 2026-075 |
| Reportero | Drew Raines (divulgación coordinada) |
| Publicado | 2026-08-05 |
get_resource(resource="artifact" | "asset", ...) descarga un archivo desde una
URL S3 pre-firmada y, cuando el llamador pasa savePath / fileName, lo guarda
en el disco local a través de:
tools/get_resource.py -> tool_utils.download_s3_content()
-> file_validation.validate_write_path()
En <= 0.1.4, validate_write_path() únicamente:
save_path con os.path.realpath(os.path.expanduser(...)).~/.aws, ~/.ssh,
~/.gnupg, ~/.docker, ~/.aws-transform-mcp, /etc/shadow, /etc/passwd).file_name mediante os.path.basename().Nunca confina el directorio resuelto a ningún directorio base/de trabajo, y
BLOCKED_FILENAMES (.bashrc, .zshrc, authorized_keys, id_rsa, …) solo
se aplica en lecturas, no en escrituras. Así que cualquier cliente MCP de
este servidor — incluido un agente que haya sido inyectado indirectamente por
prompt a través de contenido no confiable de trabajos/tareas/mensajes que
obtiene mediante esta misma herramienta — puede establecer savePath en una
ruta absoluta, un recorrido ../.. o un nombre de archivo de puntos sensible,
y el servidor escribe allí bytes influenciados por el atacante. Eso es una
primitiva de escritura de archivos fuera del directorio al que el operador cree
que las descargas están confinadas; el aviso señala que "podría conducir a la
ejecución local de código" (p. ej., sobrescribiendo un archivo de inicio del
shell).
0.1.5 corrige esto al añadir un directorio base explícito en lista blanca
(_ALLOWED_WRITE_BASE, tomado de $AWS_TRANSFORM_MCP_WRITE_DIR o del CWD del
servidor al inicio) bajo el cual debe caer toda ruta de escritura resuelta, y
al aplicar también BLOCKED_FILENAMES sobre la ruta de escritura final
resuelta.
Diff completo de la causa raíz: vendor/0.1.4-vulnerable/file_validation.py
vs. vendor/0.1.5-fixed/file_validation.py
(ambos extraídos textualmente de PyPI / GitHub, Apache-2.0).
Un cliente MCP (Q Developer, Kiro, Claude, etc. conectado a este servidor) emite una llamada de herramienta como:
{
"name": "get_resource",
"arguments": {
"resource": "artifact",
"workspaceId": "ws-...",
"jobId": "job-...",
"artifactId": "art-...",
"savePath": "/Users/victim/Library/LaunchAgents",
"fileName": "com.evil.persist.plist"
}
}
o, desde un directorio sandbox relativo:
{ "savePath": "../../../../../../Users/victim/.bashrc", "fileName": "x" }
Debido a que la herramienta describe las respuestas de resource="task" como
cosas que el agente debería leer y sobre las que actuar, y el contenido de
resource="messages" se origina de datos de chat/trabajo, un colaborador del
workspace (o una fuente upstream comprometida de trabajos/mensajes) no necesita
convencer a un humano de que escriba esto — basta con dirigir a un agente ya
conectado para que llame a get_resource con un savePath hostil. No se
requieren credenciales reales de AWS para demostrar la falla en sí, ya que el
bug está enteramente en el manejo local de rutas, antes/después de la obtención
de S3.
python3 poc.py
Sin dependencias de terceros, sin cuenta de AWS, sin acceso de red a AWS. El script:
file_validation.py real e inalterado de 0.1.4 y de 0.1.5+
(ver vendor/).download_s3_content() con el flujo de control exacto del
tool_utils.py upstream (httpx reemplazado por urllib de la stdlib
únicamente —cero dependencias, misma lógica—), y lo ejecuta exactamente como
lo haría get_resource.savePath/fileName — escape de ruta
absoluta, recorrido relativo ../.. y un nombre de archivo de puntos
bloqueado solo en lectura — contra el validador vulnerable y luego contra el
corregido.Todo ocurre dentro de un directorio de scratch desechable mkdtemp(); tu
$HOME real nunca se toca. Salida de ejemplo:
=== Target: file_validation.py from 0.1.4-vulnerable ===
-> Absolute path escape (no traversal needed at all)
RESULT: VULNERABLE: wrote OUTSIDE sandbox -> .../outside_sandbox/dropped_by_absolute_path.sh
-> Relative "../../.." traversal out of the sandbox dir
RESULT: VULNERABLE: wrote OUTSIDE sandbox -> .../outside_sandbox/dropped_by_traversal.sh
-> Sensitive dotfile name, written inside a decoy $HOME
RESULT: VULNERABLE: wrote OUTSIDE sandbox -> .../outside_sandbox/decoy_home/.bashrc
=== Target: file_validation.py from 0.1.5-fixed ===
-> Absolute path escape (no traversal needed at all)
RESULT: BLOCKED (raised ValueError): Write path must be within the working directory (...)
-> Relative "../../.." traversal out of the sandbox dir
RESULT: BLOCKED (raised ValueError): ...
-> Sensitive dotfile name, written inside a decoy $HOME
RESULT: BLOCKED (raised ValueError): ...
Actualiza a awslabs.aws-transform-mcp-server >= 0.1.5. No hay workaround del
lado del servidor para las versiones anteriores; el aviso recomienda actualizar.
Los operadores que no puedan actualizar de inmediato deberían ejecutar el
servidor con su CWD establecido en un directorio dedicado y vacío, y tratar
cualquier archivo al que pueda escribir como comprometido.
README.md — this file
poc.py — self-contained PoC driver
vendor/_loguru_shim.py — tiny stand-in for the `loguru` dep (test scaffolding only)
vendor/0.1.4-vulnerable/file_validation.py — real vulnerable source, from PyPI sdist
vendor/0.1.5-fixed/file_validation.py — real patched source, from github.com/awslabs/mcp@main