Someone left the conference room speakerphone forwarding all calls to a stranger's voicemail
CVE-2026-41947 is an Insecure Direct Object Reference (IDOR) in Dify's trace configuration endpoints (/console/api/apps/<app_id>/trace-config). Versions prior to 1.14.2 accept authenticated requests to GET, POST, PATCH, or DELETE trace configurations for *any* app_id without verifying that the caller belongs to the same tenant as the target application. An attacker who obtains a victim app's app_id — trivially extracted from the JWT returned when accessing the app as a client — can bind their own LLM trace provider (LangSmith, Opik, Langfuse, etc.) to the victim app. From that moment on, every inbound prompt and outbound model response is silently copied to the attacker's observability backend. Dify ≤ 1.14.1 is affected; the fix landed in commit 55d05fe via PR #35793. The platform powers 1M+ deployed applications and is used by enterprises including Volvo, Maersk, Panasonic, and Thermo Fisher.
The vendor rates this CRITICAL 9.1 (CVSS 3.1) and 9.3 (CVSS 4.0), anchored on PR:N — no privileges required. That score is *defensible for Dify Cloud*, where unauthenticated free self-registration means anyone can create an editor account in seconds. But it overstates reality for self-hosted deployments, where the attacker must already hold an editor-role credential inside the instance. Even on Cloud, the impact is bounded to data confidentiality and trace-config integrity within Dify — there is no code execution, no lateral movement to infrastructure, and no supply-chain pivot. The CVSS vector's I:H captures the ability to overwrite trace configs, but it is not arbitrary write across the system. noisgate downgrades from CRITICAL to HIGH 8.2: the attack is devastatingly simple on Cloud but the blast radius is application-tier data exfiltration, not infrastructure compromise.
4 steps from start to impact.
Create attacker account on Dify Cloud
cloud.dify.ai. Self-registration is unauthenticated and unrestricted — no invitation, corporate email, or payment required. On self-hosted instances this step requires an existing editor-level credential, which is substantially higher friction.- Internet access to Dify Cloud (or existing editor credential on self-hosted)
- Self-hosted deployments require pre-existing editor credentials, blocking anonymous attackers
- Enterprise SSO-gated self-hosted instances further narrow this to insider threat only
Obtain victim application's app_id
app_id UUID. Alternatively, the attacker can enumerate app_ids if other IDOR vectors (CVE-2026-41949, CVE-2026-41950) are chained. No special tooling is needed — browser DevTools suffice.- Victim application must be published and accessible to the attacker as a client
- Attacker can parse JWT from the application's API response
- If the victim app is restricted (e.g., API-key gated or VPN-only), the attacker cannot obtain the JWT
- Internal-only apps on self-hosted instances behind network segmentation are unreachable
Call trace-config endpoints with victim's app_id
/console/api/apps/<victim_app_id>/trace-config with their own trace provider credentials (e.g., LangSmith API key and project name). The server accepts the request because ops_trace.py authenticates the caller but never validates that app_id belongs to the caller's tenant. The trace config is written to the victim app's settings.- Authenticated console session (editor role minimum)
- Knowledge of victim app_id from step 2
- None on Dify Cloud — the API call is a single HTTP POST
- Self-hosted: requires console access, which may be IP-restricted
Enable persistent exfiltration channel
curl or any HTTP client.- Attacker has an account on a supported trace provider (LangSmith, Opik, Langfuse — all have free tiers)
- Victim may notice the trace config in their app settings dashboard
- Organizations with change-management workflows on app configs would detect the addition
@get_app_model to all trace-config endpoints, returning 404 for cross-tenant requests. Dify Cloud users should confirm they are on the latest managed version. Self-hosted operators should upgrade per the noisgate remediation SLA for HIGH (≤ 180 days), but given the trivial exploitability, target 30 days. Docker Compose deployments: docker compose pull && docker compose up -d.GET /console/api/apps/<app_id>/trace-config for each app). Any trace provider you do not recognize — especially newly added LangSmith, Opik, or Langfuse configs — is a strong indicator of compromise. Remove unauthorized configs. Deploy within the noisgate mitigation SLA for HIGH (≤ 30 days), ideally within 72 hours./console/*) behind a VPN, zero-trust proxy, or IP allowlist. This breaks step 1 of the attack path for external attackers on self-hosted instances. Does not help Dify Cloud users. Deploy within 30 days per the noisgate mitigation SLA./console/api/apps/*/trace-config, alert on any modification. This converts the persistent wiretap into a detectable event. Not a preventive control — purely detective.- WAF rules on the trace-config path — the requests are legitimate authenticated API calls with valid JSON bodies; no signature or payload anomaly for a WAF to match.
- Network segmentation of the Dify application tier — the IDOR is in the *console API*, not the public app-serving layer; segmenting the app frontend does not protect the console.
- Rotating LLM API keys — this vulnerability is in Dify's console layer, not in the LLM provider integration; rotating OpenAI/Anthropic keys does not affect the trace-config IDOR.
- Disabling tracing entirely — while this would prevent the attack, it removes a critical observability capability; patching is strictly preferable.
The supporting signals.
| In-the-Wild Exploitation | No confirmed exploitation. Not listed on CISA KEV. No GreyNoise or Shadowserver tags observed. Zafran Security disclosed responsibly; no reports of active campaigns. |
|---|---|
| Proof-of-Concept | No public PoC code. The vulnerability was reported via the huntr bounty program by Zafran Security. No repos on GitHub named CVE-2026-41947. pocindex.io shows no indexed PoC from ExploitDB, Nuclei, or Metasploit. However, exploitation requires only a single curl POST — weaponization is trivial for anyone who reads the Zafran blog post. |
| EPSS Score | 0.00605 (0.45%) — 36.1st percentile. Low predicted exploitation probability, consistent with no observed in-the-wild activity. |
| CISA KEV Status | Not listed as of 2026-09-27. |
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N — Network-reachable, low complexity, no privileges (on Cloud), no user interaction. High confidentiality and integrity impact, no availability impact. The PR:N is accurate for Cloud (free registration) but overstates self-hosted risk. |
| CVSS 4.0 Score | 9.3 — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N |
| Affected Versions | Dify < 1.14.2 (all versions through 1.14.1). Both Dify Cloud and self-hosted community/enterprise editions. |
| Fixed Version | Dify 1.14.2 — released with tenant-scoping enforcement via @get_app_model decorator. Fix commit: 55d05fe. PR #35793. |
| Scanning / Exposure | No dedicated Nuclei template, Nessus plugin, or Qualys QID exists for this CVE. Shodan/Censys/FOFA queries for exposed Dify instances (http.title:"Dify") can identify attack surface, but the IDOR itself is not externally detectable without authentication. Dify has 138K+ GitHub stars and 1M+ deployed apps. |
| Researcher / Disclosure | Ido Shani and Gal Zaban of Zafran Security. Published as part of the "DifyTap" research cluster (4 CVEs) on 2026-06-22. Reported via huntr. GHSA: GHSA-48xc-wmw8-3jr3. |
Sources.
- Zafran Security — DifyTap Research Blog
- The Hacker News — DifyTap Coverage
- VulnCheck Advisory — Dify Trace Config Authorization Bypass
- GitHub PR #35793 — Tenant-Scoping Fix
- GitHub GHSA-48xc-wmw8-3jr3
- Dify Release v1.14.2 — Security Fixes
- CyberPress — DifyTap Cross-Tenant Flaws
- Strix AI — CVE-2026-41947 Detail
Why this verdict
- Dify Cloud friction is near-zero: Free self-registration, JWT-exposed app_ids, and single-POST exploitation make this trivially weaponizable on
cloud.dify.ai. No public PoC is needed — the Zafran blog provides a complete recipe. This anchors the floor. - Self-hosted friction is materially higher: Editor credentials are required, console access may be IP-restricted or SSO-gated, and internal-only apps are unreachable from the internet. Roughly half of enterprise Dify deployments are self-hosted with access controls that break step 1.
- Impact is bounded to application-tier data exfiltration: The attacker gains a persistent wiretap on AI conversations (C:H) and can overwrite trace configs (I:H), but there is no code execution, no privilege escalation, no lateral movement to underlying infrastructure. The blast radius is Dify-internal, not fleet-scale.
- Role multiplier: Dify occupies the *application tier / AI platform* role. (a) Low-value: dev sandboxes and prototyping — impact is minimal. (b) Typical: team AI app builder with internal data — impact is sensitive data exfiltration, bounded to the application. (c) High-value: production AI applications processing regulated data (PII, financial, healthcare) for enterprises like Volvo, Maersk, Panasonic — the wiretap could expose regulated data streams, triggering compliance obligations (GDPR, HIPAA). However, Dify is NOT a canonical high-value infrastructure component (not identity, hypervisor, PAM, backup, CA, or network edge). The worst-case blast radius is *tenant-level data breach*, not domain takeover or fleet compromise. This sets a HIGH floor, not CRITICAL.
- No exploitation evidence compounds the downgrade: Zero KEV entries, no GreyNoise/Shadowserver tags, EPSS at 36th percentile. The attack surface is real but untouched so far.
Why not higher?
CRITICAL would require either infrastructure-level impact (RCE, privilege escalation, lateral movement) or the affected component being a canonical high-value role (identity provider, hypervisor, domain controller). Dify is an application-tier AI platform — even in the worst case, compromise yields data exfiltration within the Dify boundary, not fleet-wide or domain-wide control. The I:H in the CVSS vector represents trace-config tampering, not arbitrary system writes. The attack cannot pivot beyond Dify.
Why not lower?
MEDIUM would understate the risk for Dify Cloud deployments, where the full chain (register → extract app_id → POST trace config → wiretap) completes in minutes with zero friction. The persistent, silent nature of the exfiltration — capturing *every future conversation* — elevates this above a typical IDOR. Enterprises running production AI apps with sensitive data face real regulatory exposure if this is exploited. The 1M+ app install base and named enterprise customers (Volvo, Maersk, Panasonic) ensure the vulnerable population is not trivially small.
Crowdsourced verification payload.
Run this script on the host running Dify (or any machine with curl access to the Dify API). It checks the Dify instance version via the /console/api/setup or version endpoint. Usage: chmod +x check_cve_2026_41947.sh && ./check_cve_2026_41947.sh https://your-dify-instance.example.com. No authentication required for the version check.
#!/usr/bin/env bash
# check_cve_2026_41947.sh — Detect CVE-2026-41947 (Dify trace-config IDOR)
# Usage: ./check_cve_2026_41947.sh <DIFY_BASE_URL>
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <DIFY_BASE_URL>"
echo "Example: $0 https://cloud.dify.ai"
exit 2
fi
BASE_URL="${1%/}"
FIXED_MAJOR=1
FIXED_MINOR=14
FIXED_PATCH=2
# Try multiple version-discovery endpoints
VERSION=""
for endpoint in "/console/api/version" "/api/version" "/version"; do
RESP=$(curl -sf --max-time 10 "${BASE_URL}${endpoint}" 2>/dev/null || true)
if [[ -n "$RESP" ]]; then
VERSION=$(echo "$RESP" | grep -oP '"version"\s*:\s*"\K[0-9]+\.[0-9]+\.[0-9]+' 2>/dev/null || true)
[[ -n "$VERSION" ]] && break
fi
done
# Fallback: check HTML page for version string
if [[ -z "$VERSION" ]]; then
RESP=$(curl -sf --max-time 10 "${BASE_URL}/" 2>/dev/null || true)
VERSION=$(echo "$RESP" | grep -oP 'dify[/ ]v?\K[0-9]+\.[0-9]+\.[0-9]+' 2>/dev/null | head -1 || true)
fi
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — could not determine Dify version at ${BASE_URL}"
echo "Manual check: verify Dify version >= 1.14.2"
exit 2
fi
echo "Detected Dify version: ${VERSION}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
MAJOR=${MAJOR:-0}; MINOR=${MINOR:-0}; PATCH=${PATCH:-0}
if (( MAJOR > FIXED_MAJOR )) || \
(( MAJOR == FIXED_MAJOR && MINOR > FIXED_MINOR )) || \
(( MAJOR == FIXED_MAJOR && MINOR == FIXED_MINOR && PATCH >= FIXED_PATCH )); then
echo "PATCHED — Dify ${VERSION} >= 1.14.2 (CVE-2026-41947 fixed)"
exit 0
else
echo "VULNERABLE — Dify ${VERSION} < 1.14.2 (CVE-2026-41947 applies)"
echo "Action: Upgrade to Dify >= 1.14.2 and audit all app trace configurations."
exit 1
fi