
Демонстрирует уязвимость обхода пути в официальном примере скрипта eml-parser, позволяющую произвольную запись файлов через специально сформированные имена вложений, с PoC и исправлением.
Официальный пример скрипта examples/recursively_extract_attachments.py содержит уязвимость обхода пути, которая позволяет произвольную запись файлов за пределами целевой выходной директории. Имена вложений, извлеченные из разобранных писем, напрямую используются для формирования путей к выходным файлам без какой-либо санитизации, что позволяет имени файла, контролируемому атакующим, выйти за пределы целевой директории.
Файл: 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']))