Case Study
Startseite Case Studies False Positives erkennen
False Positive → Abgelehnt

Eigene Fehler erkennen: Wie der Vulnerability Verifier ein Finding widerlegte

Der Pentester-Agent von SQUR meldete einen JSON-Parameter-Pollution-Bypass im Zertifizierungssystem. Der Vulnerability Verifier - ein eigenständiger, unabhängiger Agent - widerlegte ihn systematisch in 8 Testvarianten in 3 Minuten und zeigte damit die Prüftiefe, die belastbare Findings von Rauschen trennt.

Ziel: CertTracker (Plattform für Zertifizierungsmanagement)
Endpunkt: POST /api/certifications/
Behauptete Schwachstelle: JSON Parameter Pollution (JPP)
Urteil: Abgelehnt
8
Testvarianten
3:26
Minuten bis zum Urteil
0
Gefundene Bypasses
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.

Agentengedanke 02:14:14
Vulnerability Verifier
Ich prüfe, ob der PUT-Endpunkt tatsächlich Felder aktualisiert. Das hilft zu verstehen, wie das Backend Aktualisierungen verarbeitet.
Agentengedanke 02:14:36
Vulnerability Verifier
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.
Tool-Antwort 02:14:49
execute_python
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

Agentengedanke 02:14:57
Vulnerability Verifier
Test weiterer Payload-Varianten, einschließlich verschiedener Datentypen für cert_type_id, um zu sehen, ob dies die JSON-Parsing- und Validierungslogik beeinflusst.
Tool-Ausführung 02:14:57
execute_python
Python - 5 JPP Variations
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}")
Tool-Antwort 02:15:16
execute_python
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.
Ergebnis - Alle 5 blockiert
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

Agentengedanke 02:15:22
Vulnerability Verifier
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.
Tool-Antwort 02:15:36
execute_python
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.
Ergebnis - Beide blockiert
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

Agentengedanke 02:15:42
Vulnerability Verifier
Test, ob nicht benachbarte doppelte Schlüssel im JSON-Payload eine Diskrepanz zwischen Validierungs- und Persistenzlogik auslösen können.
Tool-Ausführung 02:15:42
execute_python
Python - Non-Adjacent Key Test
# 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}")
Tool-Antwort 02:15:57
execute_python
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

Agentengedanke 02:16:16
Vulnerability Verifier
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.
Tool-Antwort 02:16:30
execute_python
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

Agentengedanke 02:16:35
Vulnerability Verifier
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.
Tool-Antwort 02:16:49
execute_python
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)

Agentengedanke 02:16:55
Vulnerability Verifier
Test, ob eine große Anzahl doppelter Schlüssel eine Diskrepanz darin verursachen kann, wie das JSON von verschiedenen Schichten der Anwendung geparst wird.
Tool-Ausführung 02:16:55
execute_python
Python - 100 Duplicate Keys
# 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}")
Tool-Antwort 02:17:09
execute_python
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.

Agentengedanke 02:17:32
Vulnerability Verifier
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.
Tool-Ausführung 02:17:32
update_finding
update_finding
Key Arguments
Finding Update
"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."
Tool-Antwort 02:17:40
update_finding
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