CVE-2026-29780
Path traversal in eml_parser example script allows arbitrary file write.
- CVSS 5.5
- CWE-22
- Input Validation and Sanitization
- Local
eml_parser serves as a python module for parsing eml files and returning various information found in the e-mail as well as computed information. Prior to version 2.0.1, the official example script examples/recursively_extract_attachments.py contains a path traversal vulnerability that allows arbitrary file write outside the intended output directory. Attachment filenames extracted from parsed emails are directly used to construct output file paths without any sanitization, allowing an attacker-controlled filename to escape the target directory. This issue has been patched in version 2.0.1.
- CWE
- CWE-22
- CVSS base score
- 5.5
- Published
- 2026-03-07
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- eml_parser
- Fixed by upgrading
- Yes
Solution
Upgrade to `eml_parser` version 2.0.1 or later.
Vulnerable code sample
import argparse
import os
import sys
try:
from eml_parser import eml_parser
from eml_parser.eml_parser import EmlParser as ep
except ImportError:
# try to import from parent directory
# assuming that this script is in examples folder
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from eml_parser import eml_parser
from eml_parser.eml_parser import EmlParser as ep
h = ep(include_raw_body=True, include_attachment_data=True)
def is_eml(f):
return f.endswith('.eml')
def walk_for_emls(path):
if not os.path.isdir(path):
yield path
return
for dirpath, dirnames, filenames in os.walk(path):
for f in filenames:
if is_eml(f):
yield os.path.join(dirpath, f)
def main():
parser = argparse.ArgumentParser(description='Recursively extract attachments from .eml files.')
parser.add_argument('input_path', help='Path to an .eml file or directory of .eml files.')
parser.add_argument('output_dir', help='Path to directory where attachments will be extracted.')
args = parser.parse_args()
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
for f_path in walk_for_emls(args.input_path):
with open(f_path, 'rb') as f:
eml = h.decode_email_bytes(f.read())
if 'attachment' in eml:
for attachment in eml['attachment']:
filename = attachment['filename']
output_path = os.path.join(args.output_dir, filename)
print('Writing attachment to %s' % output_path)
with open(output_path, 'wb') as f:
f.write(attachment['data'])
if __name__ == '__main__':
main()Patched code sample
import os
def save_attachment_securely(attachment, output_dir):
"""
Saves a file from an email attachment dictionary to a specified
output directory, implementing the security fix for path traversal.
This function represents the patched logic found in eml_parser version 2.0.1,
which fixes the vulnerability by sanitizing the attachment filename.
"""
if 'filename' in attachment and 'raw' in attachment:
# --- START OF VULNERABILITY FIX ---
# The vulnerable code would directly use the filename from the attachment:
# filename = attachment['filename']
# The fix is to use os.path.basename() to strip any directory
# information from the filename. This prevents path traversal
# attacks (e.g., a filename like '../../../../etc/passwd').
sanitized_filename = os.path.basename(attachment['filename'])
# An additional check to ensure the filename is not empty after
# sanitization (e.g., if the original filename was './' or '../').
if not sanitized_filename:
print(f"Skipping attachment with invalid filename: {attachment['filename']}")
return
# --- END OF VULNERABILITY FIX ---
# Construct the final path using the now-safe, sanitized filename.
filepath = os.path.join(output_dir, sanitized_filename)
# Create the output directory if it does not exist.
os.makedirs(output_dir, exist_ok=True)
# Write the attachment content to the file.
print(f"Safely writing file to: {filepath}")
with open(filepath, 'wb') as f:
f.write(attachment['raw'])Payload
From: attacker@example.com
To: victim@example.com
Subject: Malicious Attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="boundary-string"
--boundary-string
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
This is the body of the email.
--boundary-string
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="../../../../../../../../../tmp/pwned.txt"
Content-Transfer-Encoding: 7bit
This file was written outside the target directory.
--boundary-string--
Cite this entry
@misc{vaitp:cve202629780,
title = {{Path traversal in eml_parser example script allows arbitrary file write.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-29780},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-29780/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
