Safeguard
Vulnerability Analysis

Django str.format denial of service (CVE-2023-41164)

CVE-2023-41164 lets attackers DoS Django apps via a str.format() flaw in uri_to_iri(). Here's what's affected, severity context, and how to fix it.

Nayan Dey
Security Researcher
6 min read

In September 2023, the Django security team disclosed CVE-2023-41164, a denial-of-service vulnerability in django.utils.encoding.uri_to_iri() — the internal helper Django uses to convert percent-encoded URIs into human-readable IRIs before displaying them back to a user or logging them. The bug is a textbook algorithmic-complexity issue: a specific class of malformed percent-encoded input forces the function into a costly, repeated str.format() call path used to build error/replacement text for invalid byte sequences. Feed it a crafted string containing a large volume of these sequences and CPU time balloons well past what the input size would suggest, giving an unauthenticated attacker a way to tie up a worker process — and, at scale, degrade or take down a Django-based service — with a single HTTP request.

This is not a memory-corruption or code-execution bug. It is a resource-exhaustion flaw, but it matters a great deal for supply chain security teams because uri_to_iri() sits deep in Django's request/response plumbing, meaning the exposure isn't confined to code application developers wrote themselves — it's inherited automatically by every app built on an affected Django version.

Affected Versions and Components

CVE-2023-41164 affects the django.utils.encoding.uri_to_iri() function across all Django release lines that were supported at the time of disclosure:

  • Django 3.2 before 3.2.21
  • Django 4.1 before 4.1.11
  • Django 4.2 before 4.2.5

Django's own django.utils.http and internal URL-handling code call uri_to_iri() whenever a percent-encoded URI needs to be rendered back into a readable form — for example, when Django builds human-readable representations of request paths for technical error pages, redirect targets, or admin-facing URL display. Because this code path is triggered by ordinary request handling rather than by an obscure, rarely-used feature, any application running an unpatched Django version is potentially reachable, regardless of what the application's own code does.

It's worth flagging that Django release lines older than 3.2 (2.2, 3.0, 3.1, 4.0) were already end-of-life by the time this advisory shipped and did not receive a backported fix. Teams still running those branches — which unfortunately is common in legacy internal tooling and slow-moving enterprise codebases — remain vulnerable indefinitely unless they upgrade to a maintained line.

Severity: CVSS, EPSS, and KEV Context

Django's security team classified CVE-2023-41164 as a low-to-moderate severity denial-of-service issue, consistent with NVD's CVSS v3.1 base scoring in the medium range (vector characteristics of network-exploitable, low attack complexity, no privileges or user interaction required, and an availability-only impact — no confidentiality or integrity loss). That combination reflects the real-world risk profile well: it's trivial to trigger remotely, but the blast radius is "a worker gets pegged at 100% CPU," not data theft or takeover.

From an exploitation-likelihood standpoint, EPSS scoring for this CVE has stayed low — consistently in the sub-1% percentile range typical of narrow, single-function algorithmic-complexity bugs rather than broadly weaponized web vulnerabilities. CVE-2023-41164 has not been added to CISA's Known Exploited Vulnerabilities (KEV) catalog, and there are no widely reported instances of it being used in active campaigns. That said, EPSS and KEV status are lagging, population-level signals — they describe what's been observed in aggregate, not whether your specific internet-facing Django deployment is a target. DoS primitives like this one are cheap for attackers to test opportunistically, especially against services that haven't rotated dependencies in a while.

Disclosure Timeline

  • Pre-disclosure: The issue was reported privately to the Django security team through Django's responsible disclosure process, consistent with how the project handles all security reports before a coordinated release.
  • September 4, 2023: Django published its security release advisory alongside patched versions 4.2.5, 4.1.11, and 3.2.21, closing the uri_to_iri() complexity issue.
  • September 2023: CVE-2023-41164 was formally assigned and published in the NVD, along with a corresponding GitHub Security Advisory carrying full technical detail and the patch diff.
  • Following weeks: Downstream Linux distributions and package maintainers (Debian, Ubuntu, RHEL/EPEL, and various container base images) backported the fix into their respective Django packages, and dependency-scanning vendors updated advisory databases to flag vulnerable version ranges.

As with most Django security releases, the practical patch window was short and well-publicized — the harder problem for most organizations isn't finding the fix, it's finding every place in their fleet still running an unpatched Django version months or years later.

Remediation Steps

  1. Upgrade Django immediately to a patched release: 3.2.21, 4.1.11, 4.2.5, or any later version in a still-supported line. If your environment has since moved to Django 5.x, confirm the version predates the fix window isn't in play — all currently maintained branches ship with the fix included.
  2. Audit for end-of-life Django installations. If any service is running Django 2.2, 3.0, 3.1, or 4.0, there is no vendor patch available for those branches. Treat this as a forcing function to plan an upgrade to a supported release rather than attempting to backport the fix yourself.
  3. Inventory every service that depends on Django, not just your primary web app. Internal admin tools, batch-processing services, and vendored microservices frequently run older, unmonitored Django versions that get missed in ad-hoc patch cycles.
  4. Add a compensating control at the edge while patches roll out: a WAF or reverse-proxy rule that caps request path/query length and rejects requests with an abnormal density of percent-encoded sequences reduces exposure without touching application code. This is a stopgap, not a substitute for patching.
  5. Confirm DEBUG=False in production. Several of the internal call sites for uri_to_iri() are exercised more aggressively when Django's debug error pages render request details — reducing debug-mode exposure in production is good hygiene independent of this CVE.
  6. Re-run dependency and SBOM scans post-upgrade to confirm the fixed version is actually what's deployed everywhere — pinned requirements files, lockfiles, and container base images are common places where an upgrade "lands" in source control but never makes it into the running artifact.

How Safeguard Helps

Vulnerabilities like CVE-2023-41164 are exactly where alert fatigue does the most damage: it's a real, patchable CVE, but most SCA tools will flag it identically across every Django app in your inventory regardless of whether that app's request paths ever exercise the vulnerable uri_to_iri() call chain. Safeguard's reachability analysis traces whether the vulnerable function is actually invocable from your application's entry points, so security teams can separate "patch this week" from "patch this quarter" instead of treating every hit as equally urgent. Griffin, Safeguard's AI-powered triage engine, correlates the finding against your SBOM, deployment context, and internet exposure to produce a prioritized, explainable risk verdict instead of a raw CVSS number. Continuous SBOM generation and ingest keep an accurate, always-current inventory of exactly which services run which Django versions — including the legacy, end-of-life instances that tend to hide in internal tooling — so nothing gets missed during a coordinated upgrade push. And once the fix is scoped, Safeguard's auto-fix PRs open the version bump directly against the affected repositories, turning a fleet-wide remediation effort into a batch of reviewable pull requests rather than a manual spreadsheet exercise.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.