
Demuestra una vulnerabilidad de path traversal en un script de ejemplo oficial de eml-parser, que permite la escritura arbitraria de archivos mediante nombres de archivo adjunto manipulados, con PoC y corrección.
El script de ejemplo oficial examples/recursively_extract_attachments.py contiene una vulnerabilidad de path traversal que permite la escritura arbitraria de archivos fuera del directorio de salida previsto. Los nombres de archivo adjunto extraídos de los correos analizados se utilizan directamente para construir las rutas de archivo de salida sin ninguna sanitización, lo que permite que un nombre de archivo controlado por el atacante escape del directorio de destino.
Archivo: examples/recursively_extract_attachments.py
Líneas: 61–64
for a in m['attachment']:
out_filepath = out_path / a['filename'] # No sanitization
print(f'\tWriting attachment: {out_filepath}')
with out_filepath.open('wb') as a_out:
a_out.write(base64.b64decode(a['raw']))
El valor a['filename'] está controlado por el atacante mediante cabeceras de adjunto de correo manipuladas:
Content-Disposition: attachment; filename="../outside/pwned.txt"
No se realiza ninguna normalización de rutas ni validación de límites antes de escribir.
.eml malicioso:Content-Disposition: attachment; filename="../outside/pwned.txt"
python recursively_extract_attachments.py -p ./emails -o ./safe
./safe/pwned.txt./outside/pwned.txt ← escrito fuera del directorio previstoVerificado en Kali Linux con eml-parser instalado mediante pip en un entorno virtual.
Esta vulnerabilidad se limita únicamente al script de ejemplo y no afecta a la librería principal eml-parser. Sin embargo, dado que el script forma parte del repositorio oficial y es probable que se adapte para uso en producción, un atacante que proporcione un correo manipulado podría lograr la escritura arbitraria de archivos dentro del contexto de ejecución.
Los posibles escenarios de ataque incluyen:
filename="../../etc/cron.d/backdoor"filename="../../var/www/html/shell.php"filename="../../home/user/.ssh/authorized_keys"import os.path
for a in m['attachment']:
filename = os.path.basename(a['filename'])
out_filepath = out_path / filename
if not out_filepath.resolve().is_relative_to(out_path.resolve()):
print(f'[!] Skipping suspicious filename: {a["filename"]}')
continue
print(f'\tWriting attachment: {out_filepath}')
with out_filepath.open('wb') as a_out:
a_out.write(base64.b64decode(a['raw']))