VAITP Dataset

← Back to the dataset

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.

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 ::