
يُظهر ثغرة اجتياز المسار في سكربت مثال رسمي لمحلل eml-parser، مما يسمح بكتابة ملفات عشوائية عبر أسماء مرفقات مُصممة بعناية، مع تضمين إثبات المفهوم (PoC) والإصلاح.
يحتوي السكربت المثال الرسمي examples/recursively_extract_attachments.py على ثغرة اجتياز المسار (Path Traversal) تسمح بكتابة ملفات عشوائية خارج دليل الإخراج المقصود. يتم استخدام أسماء ملفات المرفقات المستخرجة من رسائل البريد الإلكتروني المُحلَّلة مباشرةً لبناء مسارات ملفات الإخراج دون أي تنقية، مما يسمح لاسم ملف يتحكم فيه المهاجم بالهروب من الدليل المستهدف.
الملف: examples/recursively_extract_attachments.py
الأسطر: 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']))
القيمة a['filename'] يتحكم فيها المهاجم عبر ترويسات مرفقات البريد الإلكتروني المُصمَّمة بعناية:
Content-Disposition: attachment; filename="../outside/pwned.txt"
لا يتم إجراء أي تطبيع للمسار أو التحقق من الحدود قبل الكتابة.
.eml خبيث:Content-Disposition: attachment; filename="../outside/pwned.txt"
python recursively_extract_attachments.py -p ./emails -o ./safe
./safe/pwned.txt./outside/pwned.txt ← يُكتب خارج الدليل المقصودتم التحقق على Kali Linux مع تثبيت eml-parser عبر pip في بيئة افتراضية.
تقتصر هذه الثغرة على السكربت المثال فقط ولا تؤثر على مكتبة eml-parser الأساسية. ومع ذلك، نظرًا لأن السكربت جزء من المستودع الرسمي ومن المرجح أن يتم تكييفه للاستخدام في الإنتاج، يمكن للمهاجم الذي يقدم بريدًا إلكترونيًا مُصمَّمًا بعناية تحقيق كتابة ملفات عشوائية ضمن سياق التنفيذ.
تشمل سيناريوهات الهجوم المحتملة:
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']))