
Démontre une vulnérabilité de traversée de chemin dans un script d'exemple officiel d'eml-parser, permettant l'écriture arbitraire de fichiers via des noms de pièces jointes malveillants, avec PoC et correctif.
Le script d'exemple officiel examples/recursively_extract_attachments.py contient une vulnérabilité de traversée de chemin qui permet une écriture arbitraire de fichiers en dehors du répertoire de sortie prévu. Les noms de fichiers des pièces jointes extraits des e-mails analysés sont directement utilisés pour construire les chemins de fichiers de sortie sans aucune assainissement, permettant à un nom de fichier contrôlé par l'attaquant de s'échapper du répertoire cible.
Fichier : examples/recursively_extract_attachments.py
Lignes : 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']))
La valeur est contrôlée par l'attaquant via des en-têtes de pièces jointes d'e-mails falsifiés :
a['filename']Content-Disposition: attachment; filename="../outside/pwned.txt"
Aucune normalisation de chemin ni validation des limites n'est effectuée avant l'écriture.
.eml malveillant :Content-Disposition: attachment; filename="../outside/pwned.txt"
python recursively_extract_attachments.py -p ./emails -o ./safe
./safe/pwned.txt./outside/pwned.txt ← écrit en dehors du répertoire prévuVérifié sur Kali Linux avec eml-parser installé via pip dans un environnement virtuel.
Cette vulnérabilité est limitée au script d'exemple uniquement et n'affecte pas la bibliothèque principale eml-parser. Cependant, comme le script fait partie du dépôt officiel et est susceptible d'être adapté pour une utilisation en production, un attaquant fournissant un e-mail falsifié pourrait réaliser une écriture arbitraire de fichiers dans le contexte d'exécution.
Les scénarios d'attaque potentiels incluent :
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']))