CVE-2026-47078
Executive Summary
CVE-2026-47078 is a relative path traversal vulnerability in the Erlang/OTP standard library's zip module (lib/stdlib/src/zip.erl), specifically within the zip:unzip/1,2 and zip:extract/1,2 functions. The flaw exists in the zip:check_dir_level/2 validation logic, which uses an integer depth counter that can be manipulated to bypass path traversal checks entirely, allowing a crafted ZIP archive to write files outside the intended extraction directory. Any application using the Erlang OTP stdlib zip module to extract untrusted archives — including HTTP file upload handlers, firmware update mechanisms, package managers, or archive-processing microservices — is susceptible. Successful exploitation results in arbitrary file write on the host filesystem, which can escalate to remote code execution depending on where the overwritten file is placed (e.g., ~/.ssh/authorized_keys, web root scripts, cron jobs, or application config files). Patches are available for OTP 27.3.4.15, 28.5.0.4, and 29.0.4, but the fix is only claimed by a single source and is not yet independently verified.
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
Technical Analysis
Vulnerability Mechanism
The vulnerable code is located in lib/stdlib/src/zip.erl, specifically in the validation routine zip:check_dir_level/2. The core flaw is a logic error in how directory depth is tracked during ZIP entry path validation.
The validation function maintains a running integer counter that operates as follows:
- A normal path component (e.g.,
docs/,images/) increments the counter. - A
..component decrements the counter. - The check passes only when the final counter value (after the entire path has been processed) is greater than or equal to zero.
The vulnerability becomes exploitable because the counter is only checked at the end of the path string, never during intermediate processing. Consider the crafted path:
../x/y
Step-by-step counter evolution:
| Path Component | Counter Before | Operation | Counter After |
|---|---|---|---|
.. |
0 | decrement | -1 |
x |
-1 | increment | 0 |
y |
0 | increment | 1 |
The final counter value is 1 (positive), so validation passes. However, when the path is subsequently joined with the current working directory via the add_cwd function, the path resolves exactly as written: it first traverses up one directory level and then descends into x/y. The zap extractor believes it is writing inside the extraction directory (because the counter recovered to a non-negative value), but the actual filesystem write occurs one directory level above the extraction root.
Why The Check Fails
The fundamental design flaw is that zip:check_dir_level/2 treats the path as a sequence of balanced operations rather than a hierarchical file-system traversal. A path like a/../../../../b or ../x/../../y can produce intermediate negative values that later return to zero or positive, masking the traversal attempt.
More formally, the check validates:
final_depth >= 0
but should validate:
depth_at_every_step >= 0 (for all intermediate depth values)
This is a classic "check at the end only" bug — the same class of vulnerability that has affected tar extraction in multiple languages (e.g., CVE-2004-1536 for tar, various extract_archive implementations in Python and PHP).
Attack Surface
The attack surface includes any Erlang application that:
- Accepts ZIP archive uploads (file-sharing services, document management systems)
- Processes email attachments through Erlang mail servers
- Handles firmware or package updates delivered as ZIP files
- Uses
zip:unzip/2orzip:extract/2on web-served directories - Uses RabbitMQ (Erlang-based) plugin ecosystem where ZIP-based plugin installation occurs
The vulnerability is locally triggerable (requires the ability to supply a crafted archive to the vulnerable function), but because Erlang services frequently expose ZIP extraction through HTTP REST APIs, network-triggered exploitation is often possible.
Exploitation Scenarios
Scenario 1: Web-Based Arbitrary File Write via ZIP Upload
Prerequisites: Target runs an Erlang web service that accepts ZIP file uploads and calls zip:extract/2 to unzip them into a directory, e.g., uploads/.
Reconnaissance: The attacker identifies a target Erlang/OTP application (checking for
Server: Erlang/OTPHTTP headers,epmdport 4369, or RabbitMQ management UI). The attacker confirms the OTP version viaerlang:system_info(otp_release)exposed through any debug endpoint, or by fingerprinting the application response behavior.Weaponization: The attacker crafts a malicious ZIP archive using Python's
zipfilemodule or a tool likezip. The archive contains a single entry with the path:../webroot/shell.phpThe ZIP central directory contains a filename of
../webroot/shell.php(or, for byte-level precision, the path components are..,webroot,shell.php). Note: some ZIP writers normalize paths before embedding; the attacker must write the raw path directly into the ZIP headers to preserve the...The attacker verifies the ZIP is structurally valid but does not use ZIP file utilities that would normalize the path. A hex editor can be used to confirm
../webroot/shell.phpappears in both the local file header and central directory entry.Payload Delivery: The attacker POSTs the malicious ZIP to the target's file upload endpoint:
POST /api/upload HTTP/1.1 Host: target.example.com Content-Type: multipart/form-data --boundary Content-Disposition: form-data; name="file"; filename="document.zip" Content-Type: application/zip [binary ZIP payload]The server receives the file, validates content type (which passes for a readable ZIP), and invokes
zip:extract(ZipPath, [{cwd, BaseDir}]).Execution: The
zipmodule parses the entry path../webroot/shell.php,check_dir_levelcomputes final depth = 1 (passes validation), and the file is written toBaseDir/../webroot/shell.php— i.e., directly into the web root that serves PHP scripts.Post-Exploitation Impact: The attacker sends an HTTP request to
https://target.example.com/shell.phpwith the webshell payload, obtaining remote code execution on the server with the privileges of the Erlang application (which is often a dedicated Unix user but may have access to databases, message queues, or a broader trust domain).
Scenario 2: SSH Key Injection via Timed-Use Extraction
Prerequisites: A scheduled Erlang job extracts a ZIP archive from an internal source (e.g., a data pipeline) into a staging directory.
The attacker compromises a low-privilege account or MITM the ZIP download channel (e.g., via DNS spoofing of the internal server).
The attacker crafts a ZIP with multiple entries, including:
../.ssh/authorized_keysThe ZIP is extracted by
zip:extract/2to the staging dir.check_dir_levelvalidates:..(counter = -1),.ssh(counter = 0),authorized_keys(counter = 1) — final = 1 → PASS.The file is written to
StagingDir/../.ssh/authorized_keys, appending the attacker's public key to the Erlang service account's authorized keys.The attacker SSHs into the target as the service account, gaining an interactive shell.
Scenario 3: Configuration File Overwrite Leading to RCE
- Attacker uploads a malicious ZIP to an Erlang-based FTP-to-HTTP gateway that extracts archives into user-accessible directories.
- The ZIP contains
../etc/nginx/conf.d/malicious.conf. - After extraction, the attacker triggers an nginx reload (via a separate vulnerability or legitimate admin action), loading the new config that proxies attacker-controlled endpoints.
- The attacker uses this to pivot internally.
Impact Assessment
Business Impact
- Confidentiality: Direct write of files to arbitrary filesystem locations d can enable the attacker to read or modify sensitive files if the service account has read permissions on them. If the attacker is able to overwrite source code or config, they can leak secrets.
- Integrity: Arbitrary file write allows overwriting application binaries, scripts, configuration files, and package caches. The integrity of the entire host can be compromised.
- Availability: Overwriting critical system files (e.g.,
/etc/passwd, shared libraries) can cause denial of service. Untrusted ZIP extraction can also create symlink tricks combined with this traversal to delete or corrupt data. - Privilege Escalation: If the extraction directory is owned by root (e.g., the Erlang service runs as root — common in Docker setups), the attacker can write
/root/.ssh/authorized_keysor/etc/cron.d/files, achieving full root compromise.
Affected Systems
- All Erlang/OTP installations from OTP 27.1 through 27.3.4.14, OTP 28.4.x through 28.5.0.3, and OTP 29.0.x through 29.0.3.
- Every application calling
zip:unzip/2orzip:extract/2without sandboxing is impacted. - Commercial products embedding Erlang: RabbitMQ (uses
zipfor plugin install), CouchDB (attachments), ejabberd (XMPP file transfer), WhatsApp servers (Erlang backend), and numerous FinTech platforms.
CVSS Rationale
Official CVSS is N/A (no score submitted by CNA). Using engineering judgment with the EPSS score of 0.00152 (4.8% percentile — moderate probability of exploitation), this vulnerability is likely CVSS 7.5–8.8 (High) given network-triggerable attack vector, arbitrary file write, and low complexity, but official scoring is absent.
Blast Radius
- Single Host: File write outside extraction dir leads to local RCE.
- Cloud Environment: If the Erlang app runs in a pod with a service account mounted via a volume, writing to that volume can compromise other pods sharing the volume.
- CICD Pipeline: If a CI runner uses Erlang to extract artifacts, an attacker could inject binaries into the artifact cache, poisoning downstream build environments.
Detection Guidance
Sigma Rule
title: Potential Erlang ZIP Path Traversal (CVE-2026-47078)
id: 3c9f8a7b-5d7e-4f1a-9c2e-8d6a1b3e7f04
status: experimental
description: Detects ZIP extraction requests or application warnings indicating path traversal attempts through Erlang zip module.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-47078
author: ValtersIT Threat Intelligence Team
date: 2026/06/01
logsource:
category: application
product: erlang
detection:
selection_zip_error:
- event.message|contains: "zip:extract"
- event.message|contains: "zip:unzip"
- event.message|contains: "illegal archive"
selection_traversal:
- event.message|contains: "./"
- event.message|contains: "/../"
- event.message|contains: "..\\"
condition: selection_zip_error and selection_traversal
level: high
falsepositives:
- Legitimate nested directory extraction
- Malformed but non-malicious ZIP files
YARA Rule
rule CVE_2026_47078_Zip_Traversal {
meta:
author = "ValtersIT Threat Intelligence"
description = "Detects ZIP archives containing path traversal entries targeting Erlang zip module check_dir_level bypass"
reference = "CVE-2026-47078"
date = "2026-06-01"
severity = "high"
strings:
$traversal1 = "..\\x\\y" ascii wide
$traversal2 = "../x/y" ascii wide
$traversal3 = "../" ascii wide
$erlang_zip = /(?i)check_dir_level|zip:extract|zip:unzip/
condition:
uint16(0) == 0x4B50 and
2 of ($traversal*) and
$erlang_zip
}
Suricata IDS Rule
alert http any any -> $HOME_NET any (msg:"ET EXPLOIT Erlang ZIP Path Traversal Attempt (CVE-2026-47078)"; flow:to_server,established; content:"POST"; http_method; content:"multipart/form-data"; http_content_type; content:"|50 4B 03 04|"; http_client_body; content:"|2E 2E 2F|"; http_client_body; content:!"|2E 2E 2F 2E 2E|"; http_client_body; flowbits:set,et_erl_zip_traversal; classtype:attempted-admin; sid:202647078; rev:1; metadata:affected_product Erlang_OTP, attack_target Web_Server, deployment Perimeter, signature_severity Major, created_at 2026_06_01;)
Elastic Detection
sequence by host.id, process.entity_id with maxspan=1m
[ file where event.action == "write" and
file.name == "authorized_keys" or
file.name == "*.php" or
file.name == "*.conf" ]
[ file where event.action == "write" and
file.path : "*..*" and
not file.path : "*C:\\*" and
not file.path : "*\\..\\*\\*\\*" ] // normalize window paths
Splunk SPL Query
index=erlang OR index=web
| search (sourcetype="erlang_error" OR sourcetype="erlang_log")
(message="*zip:extract*" OR message="*zip:unzip*")
OR
(sourcetype="iis" OR sourcetype="access_combined")
(status=200 form_data="*multipart/form-data*")
| regex message="(?i).*(\.\.\/|\.\.\\)"
| eval malicious_path=if(match(message, "\.\.\/|\.\.\\"), "TRAVERSAL", "NORMAL")
| stats count by host, source, malicious_path
| where malicious_path="TRAVERSAL"
Wazuh OSSEC Rule
<group name="erlang_zip_traversal,">
<rule id="100078" level="10">
<decoded>general</decoded>
<match>\.\.\/|\.\.\\</match>
<match>zip:extract|zip:unzip</match>
<description>Erlang zip module path traversal attempt detected (CVE-2026-47078)</description>
<mitre>
<id>T1190</id>
</mitre>
<group>attack_attempt,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch Confidence: 2/4 — CLAIMED (single source, OSV, claims the patch exists; not independently confirmed).
Immediate Mitigations (no patch required)
- Validate ZIP entries before calling
zip:extract/2: Write a pre-filter in Erlang that iterates overzip:list_dir/1results and rejects any entry whose path component contains..or absolute path segments. This effectively blocks the attack without patching. - Use a sandboxed extraction method: Substitute
zip:unzip/2with an external, hardened tool (unzip -dwith proper flags) or a pure-Erlang re-implementation that validates paths using normalized absolute path comparison instead of the integer counter. - Restrict the service account privileges: Run the Erlang service under a dedicated low-privilege user with write access limited to the extraction directory only. Use OS-level permissions to prevent writes outside the intended area even if the vulnerability is triggered.
- WAF rules: In front of Erlang HTTP APIs, add a web application firewall rule to inspect multipart file uploads for ZIP MIME types and reject any archive containing
..in entry names. This can be done with a rule that decompresses the ZIP and inspects the central directory.
Patch / Upgrade Steps
- Upgrade to a fixed version:
- OTP 27.3.4.15 or later (for the OTP 27 series)
- OTP 28.5.0.4 or later (for OTP 28)
- OTP 29.0.4 or later (for OTP 29)
- Alternatively, upgrade
stdlibpackage to ≥ 6.2.2.4 (OTP 27), ≥ 7.3.0.1 (OTP 28), or ≥ 8.0.3 (OTP 29).
- Verify the patch: Since patch confidence is only 2/4, treat the fix as unverified. After upgrading, run a targeted regression test with a crafted ZIP containing
../x/yto confirm the extraction fails with an{error, illegal_archive}return. - Monitor OSV advisories for additional CVE-2026-47078 signals or a vendor advisory from the Erlang/OTP team.
Long-Term Hardening
- Adopt strict validation philosophy: Never use a "balance counter" to validate hierarchical paths of untrusted archives. Instead, normalize the full absolute path of each entry destination and verify it is a subpath of the intended extraction root.
- Apply defense-in-depth for ZIP/archive parsing: Use an OTP-independent validation step (e.g., Rust or Go utility invoked via port) for extremely high-security environments.
- Implement filesystem jails: On Linux, use
chroot,systemdsocket activation withRootDirectory=orPrivateTmp=, or mount a dedicated volume for extraction. On macOS, use sandbox profiles. - Harden the runtime: Ensure the Erlang service runs as a non-root, non-login user. Apply SELinux/AppArmor policies to confine the Erlang BEAM process to write only within designated directories.
- Hunt for indicators: Review historical logs for
zip:extractinvocation patterns, and manually inspect the extraction directories for files that shouldn't be there — particularly outside the designated extraction root.
References
- NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2026-47078 — Official CVE record, vulnerable software descriptions.
- OSV Advisory: https://osv.dev/vulnerability/CVE-2026-47078 — Affected OTP/stdlib versions, patch status, source claims.
- Erlang/OTP Security Advisories (mailing list): https://erlang.org/security.html — Official vendor announcements for the OTP project; monitor for the forthcoming patch advisory.
- CWE-23 Relative Path Traversal: https://cwe.mitre.org/data/definitions/23.html — Weakness class taxonomy used by this CVE.
- ATT&CK T1190 — Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/ — Common tactic mapping for upload-based path traversal exploitation.
Threat Actor Campaigns: No in-the-wild exploitation or threat-actor campaign has been publicly associated with CVE-2026-47078 at the time of writing. The EPSS score of 0.00152 (4.8 percentile) indicates that exploitation is plausible but not currently widespread. Organizations should treat this as a high-priority remediation target given the ease of exploitation and the ubiquity of Erlang-based services in messaging, finance, and middleware stacks.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.