3:26
Minuten bis zum Urteil
1
False Positive eliminiert
Die ursprüngliche Behauptung
Während früherer Tests meldete der Pentester-Agent von SQUR ein Finding: Der Endpunkt zur
Erstellung von Zertifizierungen schien anfällig für JSON Parameter Pollution (JPP).
Die Behauptung lautete, dass doppelte JSON-Schlüssel eine gefährliche Aufspaltung verursachen würden - der Validator
prüfe den einen Wert, während die Datenbank den anderen speichere.
Wäre dies zutreffend, könnten Mitarbeitende doppelte Zertifizierungen desselben Typs anlegen
und damit die Geschäftsregel "eine Zertifizierung pro Typ" der Anwendung umgehen.
Gemeldetes Finding
Business Logic Bypass via JSON Parameter Pollution -
POST /api/certifications/ validiert das erste Vorkommen von
cert_type_id, speichert aber das letzte. Das Senden von
{"cert_type_id": 3, "cert_type_id": 1} besteht die Validierung
(Typ 3, den die nutzende Person nicht besitzt), erzeugt aber ein Duplikat von Typ 1 in der Datenbank.
Die Verifizierung beginnt
Der Vulnerability Verifier - ein unabhängiger Agent, der
alle Findings validiert, bevor sie in den finalen Bericht gelangen - erhielt diese Behauptung und begann mit der systematischen
Reproduktion. Sein Ansatz: jedes Finding als Hypothese behandeln und dann versuchen, sie zu widerlegen.
Ich prüfe, ob der PUT-Endpunkt tatsächlich Felder aktualisiert. Das hilft zu verstehen, wie das Backend Aktualisierungen verarbeitet.
Ich prüfe, ob die im Finding genannten Zertifizierungen tatsächlich existieren und ob ich auf sie zugreifen kann. Das könnte einen Hinweis auf die nutzende Person geben, die das Finding gemeldet hat.
Versuch, über 'GET /api/certifications/{id}' mit dem Konto 'user_2f4a0672' auf die Zertifizierungs-IDs 37 und 41 zuzugreifen. Beide Anfragen lieferten HTTP 403 Forbidden zurück. Das bestätigt, dass die Zertifizierungen für die aktuelle nutzende Person nicht zugänglich sind, was darauf hindeutet, dass sie zu einem anderen Konto gehören und dass das System Zugriffskontrollen implementiert, um das unbefugte Einsehen von Zertifizierungsdetails zu verhindern. Die IDs lieferten kein 404 zurück, was darauf hindeuten könnte, dass sie in der Datenbank existieren.
Der Nachweis nannte die Zertifizierungs-IDs 37 und 41 als die mutmaßlichen Duplikate. Der Verifier konnte
nicht direkt auf sie zugreifen, bestätigte aber, dass sie existieren. Nun war es an der Zeit, den eigentlichen Angriff zu reproduzieren.
Systematische Falsifizierung: 8 Varianten
In den nächsten 3 Minuten entwarf und führte der Verifier 8 verschiedene Testvarianten aus,
die jeweils auf eine andere theoretische Möglichkeit abzielten, wie der JPP-Bypass funktionieren könnte. Jede einzelne wurde blockiert.
Test 1 - Benachbarte doppelte Schlüssel mit Typvariationen
Test weiterer Payload-Varianten, einschließlich verschiedener Datentypen für cert_type_id, um zu sehen, ob dies die JSON-Parsing- und Validierungslogik beeinflusst.
variations = [
'{"cert_type_id": "3", "cert_type_id": 1, ...}', # string then int
'{"cert_type_id": 3.0, "cert_type_id": 1, ...}', # float then int
'{"cert_type_id": 3, "cert_type_id": 1.0, ...}', # int then float
'{"cert_type_id": 3, "cert_type_id": "1", ...}', # int then string
'{"cert_type_id": 3, ..., "cert_type_id": 3}', # triple duplicate
]
for i, payload in enumerate(variations):
res = requests.post(certs_url, data=payload, headers=headers)
print(f"Variation {i+1}: {res.status_code}")
Fünf JPP-Varianten mit verschiedenen Datentypen (Strings, Floats und Integers) für den Schlüssel 'cert_type_id' auf dem Endpunkt '/api/certifications/' getestet. Alle Varianten führten zu HTTP 400 Bad Request mit der Fehlermeldung '{"detail":"You already have an active certification of this type"}'. Das bestätigt, dass die "Last Key Wins"-Parsing-Logik des Backends unabhängig vom verwendeten Datentyp (z. B. "1", 1.0 oder 1) konsistent ist und den final geparsten Wert korrekt gegen die bestehenden Zertifizierungen der nutzenden Person validiert.
Variation 1 (string "3" → int 1): 400 "You already have an active certification of this type"
Variation 2 (float 3.0 → int 1): 400 "You already have an active certification of this type"
Variation 3 (int 3 → float 1.0): 400 "You already have an active certification of this type"
Variation 4 (int 3 → string "1"): 400 "You already have an active certification of this type"
Variation 5 (triple duplicate): 400 "You already have an active certification of this type"
Test 2 - Strukturelle Typverwirrung
Test, ob der Parameter cert_type_id Arrays oder Objekte akzeptiert, was die Validierung umgehen könnte, falls der Validator einen einzelnen Integer erwartet, die Persistenzschicht diese Strukturen aber anders behandelt.
Zwei JSON-Payloads auf dem Endpunkt '/api/certifications/' getestet, bei denen 'cert_type_id' als Array [3, 1] und als Objekt {'id': 1} übergeben wurde. Beide Anfragen wurden mit HTTP 422 Unprocessable Entity abgelehnt. Der Response-Body beider Varianten enthielt einen Validierungsfehler: 'Input should be a valid integer' für das Feld 'cert_type_id'. Das bestätigt, dass das Backend eine strikte Typprüfung für diesen Parameter implementiert und Bypässe über Nicht-Integer-JSON-Strukturen verhindert.
Array [3, 1]: 422 "Input should be a valid integer"
Object {"id": 1}: 422 "Input should be a valid integer"
Test 3 - Nicht benachbarte doppelte Schlüssel
Test, ob nicht benachbarte doppelte Schlüssel im JSON-Payload eine Diskrepanz zwischen Validierungs- und Persistenzlogik auslösen können.
# Keys separated by other fields - some parsers handle this differently
payload = '{"cert_type_id": 3, "issue_date": "2024-01-01", \
"expiry_date": "2025-01-01", "certificate_number": "JPP-NON-ADJ", \
"cert_type_id": 1}'
res = requests.post(certs_url, data=payload, headers=headers)
print(f"Status Code: {res.status_code}")
print(f"Response: {res.text}")
Ein JSON-Parameter-Pollution-(JPP-)Payload mit nicht benachbarten doppelten Schlüsseln ('cert_type_id': 3 gefolgt von anderen Feldern, dann 'cert_type_id': 1) auf dem Endpunkt '/api/certifications/' getestet. Der Server antwortete mit HTTP 400 Bad Request und der Fehlermeldung '{"detail":"You already have an active certification of this type"}'. Da die nutzende Person bereits eine Zertifizierung vom Typ 1 besitzt, bestätigt dieses Ergebnis, dass die Validierungslogik des Backends durchgängig das letzte Vorkommen eines Schlüssels verwendet, auch wenn die Duplikate nicht benachbart sind.
Test 4 - Query-String vs. Request-Body
Test, ob die Validierungslogik die cert_type_id im Query-String prüft, während die Persistenzlogik den Body verwendet - was eine weitere Form von Parameter Pollution wäre.
Versuch, die "Eine-pro-Typ"-Beschränkung für Zertifizierungen zu umgehen, indem eine POST-Anfrage an '/api/certifications/' mit 'cert_type_id=3' im Query-String und 'cert_type_id: 1' im JSON-Body gesendet wurde. Der Server lieferte HTTP 400 Bad Request mit dem Fehler '{"detail":"You already have an active certification of this type"}' zurück. Das bestätigt, dass das Backend den JSON-Body für die Validierung priorisiert und für diese Form von Parameter Pollution nicht anfällig ist.
Test 5 - Statusbasierter Bypass
Test, ob die Duplikatsprüfung nur für 'active'-Zertifizierungen gilt. Wenn ich eine 'expired'-Zertifizierung anlegen kann, obwohl ich eine 'active' besitze, könnte das ein Business-Logic-Fehler sein, wenn auch nicht direkt JPP. Wenn ich jedoch über JPP eine 'active' anlegen kann, während ich bereits eine 'active' besitze, ist das das Finding.
Versuch, eine Zertifizierung vom Typ 1 (die die nutzende Person bereits besitzt) anzulegen, indem im POST-Payload 'expiry_date' auf die Vergangenheit (2021-01-01) und 'status' auf 'expired' gesetzt wurde. Der Server lehnte die Anfrage mit HTTP 400 Bad Request und der Fehlermeldung '{"detail":"You already have an active certification of this type"}' ab. Das bestätigt, dass die Duplikatsprüfung des Backends anhand der 'cert_type_id' durchgesetzt wird, unabhängig von Status oder Datumsangaben im Erstellungs-Payload.
Test 6 - 100 doppelte Schlüssel (Parser-Stresstest)
Test, ob eine große Anzahl doppelter Schlüssel eine Diskrepanz darin verursachen kann, wie das JSON von verschiedenen Schichten der Anwendung geparst wird.
# Can volume confuse the parser into splitting validation/persistence?
keys = ['"cert_type_id": 3'] * 100
keys.append('"cert_type_id": 1')
payload = '{' + ', '.join(keys) + ', "issue_date": "2024-01-01", \
"expiry_date": "2025-01-01", "certificate_number": "MANY-KEYS"}'
res = requests.post(certs_url, data=payload, headers=headers)
print(f"Status Code: {res.status_code}")
print(f"Response: {res.text}")
Ein JSON-Parameter-Pollution-(JPP-)Payload mit 100 Instanzen von "cert_type_id": 3 gefolgt von "cert_type_id": 1 auf dem Endpunkt '/api/certifications/' getestet. Der Server antwortete mit HTTP 400 Bad Request und der Fehlermeldung '{"detail":"You already have an active certification of this type"}'. Das bestätigt, dass das Backend durchgängig das letzte Vorkommen eines Schlüssels parst und validiert, selbst wenn eine große Anzahl von Duplikaten vorhanden ist.
Das Urteil
Nachdem der Verifier jede sinnvolle Variante des gemeldeten Angriffs ausgeschöpft hatte, fällte er sein Urteil.
Die technische Verifizierung konnte den gemeldeten Business Logic Bypass via JSON Parameter Pollution (JPP) nicht reproduzieren. Mehrere Varianten von JPP-Payloads wurden getestet, darunter doppelte Schlüssel in unterschiedlicher Reihenfolge, nicht benachbarte Schlüssel und verschiedene Datentypen. In allen Fällen priorisierte das Backend durchgängig das letzte Vorkommen des doppelten Schlüssels - sowohl bei der Validierung als auch bei der Persistenz - und blockierte die Erstellung doppelter Zertifizierungen korrekt. Die gemeldete Diskrepanz zwischen Validierungs- und Persistenzlogik konnte in der aktuellen Umgebung nicht bestätigt werden.
update_finding
Key Arguments
"status": "ai_rejected"
"risk_level": "informational"
"status_reason": "Technical verification failed to reproduce
the reported bypass; the backend consistently enforces the business
rule by prioritizing the last occurrence of duplicate JSON keys."
Finding e2061303-895e-43da-b681-c7bbd7755660 erfolgreich auf den Status "ai_rejected" gesetzt und das Risikoniveau auf "informational" herabgestuft. Die technische Verifizierung über mehrere Testfälle bestätigte, dass das Backend durchgängig das letzte Vorkommen doppelter JSON-Schlüssel sowohl bei der Validierung als auch bei der Persistenz priorisiert. Das Finding enthält nun Python-Nachweise, die belegen, dass die "Eine-pro-Typ"-Geschäftsregel korrekt durchgesetzt wird.
Alle 8 Tests im Überblick
Test 1 - String → Int
{"cert_type_id": "3", "cert_type_id": 1}
400 - Blockiert
Test 2 - Float → Int
{"cert_type_id": 3.0, "cert_type_id": 1}
400 - Blockiert
Test 3 - Array-Wert
{"cert_type_id": [3, 1]}
422 - Abgelehnt
Test 4 - Objekt-Wert
{"cert_type_id": {"id": 1}}
422 - Abgelehnt
Test 5 - Nicht benachbarte Schlüssel
{"cert_type_id": 3, ..., "cert_type_id": 1}
400 - Blockiert
Test 6 - Query-String-Pollution
POST ?cert_type_id=3 + body: cert_type_id: 1
400 - Blockiert
Test 7 - Status-Bypass
{"cert_type_id": 1, "status": "expired"}
400 - Blockiert
Test 8 - 100 doppelte Schlüssel
"cert_type_id": 3 ×100, then "cert_type_id": 1
400 - Blockiert
Warum das wichtig ist
- 1 False Positive eliminiert, bevor er in den Kundenbericht gelangte
- 0 Personenstunden aufgewendet, um ein Nicht-Problem zu triagieren
- Konstruktive Empfehlung erhalten: Auch bei der Ablehnung des Bypass merkte der Verifier an, dass das Akzeptieren doppelter JSON-Schlüssel grundsätzlich gegen RFC 8259 verstößt, und empfahl, dass die Anwendung sie konsequent ablehnt
Das False-Positive-Problem - und wie SQUR es löst
Jedes Security-Tool erzeugt False Positives. Herkömmliche Scanner kippen Hunderte von Findings in ein PDF
und überlassen es Menschen, Signal von Rauschen zu trennen - ein Prozess, der typischerweise
40-60 % der Triage-Zeit eines Security-Teams verbraucht.
KI-gestützte Tools tragen dasselbe Risiko, verstärkt durch die Neigung von Sprachmodellen, plausible
Hypothesen mit bestätigten Schwachstellen zu verwechseln. Ein Finding, das richtig klingt, ist nicht dasselbe wie eines, das richtig
ist.
Die Architektur von SQUR adressiert dies direkt. Der Vulnerability Verifier ist ein eigenständiger,
unabhängiger Agent, der jedes Finding als zu prüfende Hypothese behandelt. Er vertraut nicht den
Schlussfolgerungen des Pentesters - er reproduziert den Angriff von Grund auf, variiert die Bedingungen systematisch und
akzeptiert nur Findings, die einer strengen Falsifizierung standhalten.
In diesem Fall: 8 Varianten in 3 Minuten und 26 Sekunden. Typverwirrung, strukturelle Bypässe, Query-String-Pollution,
Statusmanipulation, Parser-Stresstests. Als keine davon einen Bypass erzeugte, wurde das Finding
abgelehnt - nicht in einem Backlog vergraben, sondern aktiv als ai_rejected
markiert, mit vollständigem Nachweis.
Das Ergebnis: Nur verifizierte, reproduzierbare Schwachstellen gelangen in Ihren Bericht.
Dieses Finding ist Teil des Demo-Pentests, den jede nutzende Person beim Registrieren bei SQUR sieht. Erstellen Sie ein kostenloses Konto, um die vollständigen Ergebnisse zu erkunden und zu verstehen, wie SQUR funktioniert.
Kostenlos loslegen