
Dimostra la scrittura arbitraria di file CVE-2026-18953 nel tool get_resource di un server MCP abusando del path traversal di savePath; include validatori vulnerabili e corretti vendored per la verifica.
Scrittura arbitraria di file in awslabs.aws-transform-mcp-server (server MCP
AWS Transform) tramite il parametro savePath dello strumento get_resource.
| CVE | CVE-2026-18953 |
| CWE | CWE-22 — Improper Limitation of a Pathname to a Restricted Directory |
| Versioni interessate | awslabs.aws-transform-mcp-server 0.1.0 – 0.1.4 |
| Corretto in | 0.1.5 |
| CVSS v3.1 | 8.6 HIGH — AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H |
| CVSS v4.0 | 6.3 MEDIUM — AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:H |
| Avviso | GHSA-66mr-jr63-2jgw |
| Bollettino | AWS Security Bulletin 2026-075 |
| Segnalatore | Drew Raines (divulgazione coordinata) |
| Pubblicato | 2026-08-05 |
get_resource(resource="artifact" | "asset", ...) scarica un file da un
URL S3 pre-firmato e, quando il chiamante passa savePath / fileName, lo salva
su disco locale tramite:
tools/get_resource.py -> tool_utils.download_s3_content()
-> file_validation.validate_write_path()
In <= 0.1.4, validate_write_path() si limita a:
save_path con os.path.realpath(os.path.expanduser(...)).~/.aws, ~/.ssh, ~/.gnupg,
~/.docker, ~/.aws-transform-mcp, /etc/shadow, /etc/passwd).file_name tramite os.path.basename().Non vincola mai la directory risolta ad alcuna directory di base/lavoro, e
BLOCKED_FILENAMES (.bashrc, .zshrc, authorized_keys, id_rsa, …) viene
applicato solo in lettura, non in scrittura. Quindi qualsiasi client MCP di
questo server — incluso un agente che ha subito un'iniezione di prompt indiretta
tramite contenuti non fidati di job/attività/messaggi recuperati attraverso
questo stesso strumento — può impostare savePath su un percorso assoluto, una
traversata ../.. o un nome di dotfile sensibile, e il server vi scrive byte
influenzati dall'attaccante. Si tratta di una primitiva di scrittura file al di
fuori della directory in cui l'operatore ritiene che i download siano confinati;
l'avviso osserva che "potrebbe portare all'esecuzione di codice locale" (ad
esempio sovrascrivendo un file di avvio della shell).
0.1.5 risolve il problema aggiungendo una directory di base esplicitamente
consentita (_ALLOWED_WRITE_BASE, ricavata da $AWS_TRANSFORM_MCP_WRITE_DIR o
dalla CWD del server all'avvio) in cui ogni percorso di scrittura risolto deve
rientrare, e applicando inoltre BLOCKED_FILENAMES sul percorso di scrittura
finale risolto.
Diff completo della causa principale: vendor/0.1.4-vulnerable/file_validation.py
vs. vendor/0.1.5-fixed/file_validation.py
(entrambi prelevati verbatim da PyPI / GitHub, Apache-2.0).
Un client MCP (Q Developer, Kiro, Claude, ecc. collegato a questo server) effettua una chiamata a uno strumento come la seguente:
{
"name": "get_resource",
"arguments": {
"resource": "artifact",
"workspaceId": "ws-...",
"jobId": "job-...",
"artifactId": "art-...",
"savePath": "/Users/victim/Library/LaunchAgents",
"fileName": "com.evil.persist.plist"
}
}
oppure, da una directory sandbox relativa:
{ "savePath": "../../../../../../Users/victim/.bashrc", "fileName": "x" }
Poiché lo strumento descrive le risposte resource="task" come elementi che
l'agente dovrebbe leggere e su cui agire, e i contenuti di resource="messages"
provengono da dati di chat/job, un collaboratore del workspace (o una fonte
upstream compromessa di job/messaggi) non ha bisogno di convincere un essere
umano a digitare tutto questo — è sufficiente indirizzare un agente già connesso
a chiamare get_resource con un savePath ostile. Non sono richieste
credenziali AWS reali per dimostrare la vulnerabilità stessa, poiché il bug
risiede interamente nella gestione locale dei percorsi, prima/dopo il recupero
dall'S3.
python3 poc.py
Nessuna dipendenza di terze parti, nessun account AWS, nessun accesso di rete ad AWS. Lo script:
file_validation.py proveniente sia dalla
0.1.4 che dalla 0.1.5+ (vedi vendor/).download_s3_content() con lo stesso identico flusso di controllo
dell'upstream tool_utils.py (solo httpx sostituito con urllib della stdlib
—zero dipendenze, stessa logica) e lo pilota esattamente come farebbe
get_resource.savePath/fileName — escape con percorso
assoluto, traversata relativa ../.. e un nome di dotfile bloccato solo in
fase di lettura — contro il validatore vulnerabile, poi contro quello corretto.Tutto avviene all'interno di una directory temporanea usa-e-getta creata tramite
mkdtemp(); il tuo vero $HOME non viene mai toccato. Output di esempio:
=== 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): ...
Aggiornare a awslabs.aws-transform-mcp-server >= 0.1.5. Non esiste alcuna
soluzione alternativa lato server per le versioni precedenti; l'avviso
raccomanda l'aggiornamento. Gli operatori che non possono aggiornare
immediatamente dovrebbero eseguire il server con la CWD impostata su una
directory dedicata e vuota, e considerare compromesso qualsiasi file a cui il
server possa scrivere.
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