Proof of exploit
K0G-2026-0417 · run #8842 · engagement: acme-demo · generated 2026-08-04 14:22 UTC

Unauthenticated SQL injection in the client portal login chains to full administrative takeover

CRITICAL · CVSS 9.8 CWE-89 · SQL Injection OWASP A03:2021 — Injection PROOF CAPTURED FIX VERIFIED
Target
portal.whitmorehale.example
Endpoint
POST /client-login
Parameter
username (form)
Access required
None — pre-authentication
CVSS v3.1 vector
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Time to prove
3 min 41 s from first request
Blast radius
1,284 client records reachable
Status
Remediated & retested

This is an illustrative report. The target, hostnames and records here are a constructed demonstration — the same scenario you can run yourself in Demo Room 02. The structure, identifiers (CVSS, CWE, OWASP, MITRE ATT&CK) and the safe-proof methodology are exactly what K0G produces against a real engagement.

01

Executive summary

A K0G agent swarm reached the client portal at portal.whitmorehale.example and found the login form builds its SQL query by string concatenation, with no parameterisation on the username field. A single crafted value authenticates as the first row in the users table — which is the firm's own administrator — with no valid password and no prior access.

On its own this is one injectable field. Chained, it is a straight line from the public internet to the administrative backend: the agent authenticated as an admin, enumerated the matters dashboard, and confirmed that 1,284 client records — including sealed settlements and privileged correspondence — were reachable from that session. K0G stopped at proof and did not read a single record's contents. The finding is rated Critical (CVSS 9.8) because it is remotely exploitable, needs no privileges or user interaction, and fully compromises confidentiality, integrity and availability of the application's data. Integrity and availability score High because the administrator role reached here confers write and destructive capability over that data — demonstrated as reachable, not exercised (see §4).

Why this matters to the business

  • Confidential client data is one request away from the open internet — no credentials, no phishing, no insider.
  • Privileged legal material (settlements under seal, strategy memos) sits behind the same broken check.
  • Breach-notification exposure — unauthorised access to client PII would trigger contractual and regulatory disclosure obligations.
  • Undetectable in logs as written — the request looks like an ordinary failed-then-successful login.
02

Attack chain

The severity is in the chain, not the single flaw. Five steps take the agent from anonymous reconnaissance to data it was never meant to see. MITRE ATT&CK techniques are noted per hop.

1

Reconnaissance & surface mapping

Agents crawled the public marketing site, enumerated links and directories, and discovered the client portal on a separate subdomain that is not linked from the primary navigation.

T1595 Active ScanningT1590 Gather Victim Network Info
2

Credential spraying — unsuccessful

The swarm attempted a bounded set of common and firm-themed credentials against the login. All were rejected; lockout did not trigger, but no valid pair was found. This path was abandoned, and the observation ("no rate limiting on failed auth") was recorded as a secondary finding.

T1110.003 Password Spraying
3

Injection discovery

Probing the username field with a single quote returned a 500 with a database error fragment, confirming unsanitised input reaching the SQL layer. The agent fingerprinted the backend as MySQL from the error signature and stopped fuzzing.

T1190 Exploit Public-Facing ApplicationCWE-89
4

Authentication bypass

The canonical tautology ' OR 1=1 -- in username made the WHERE clause always true. The query returned the first user row and the application logged the agent in as that identity — the firm administrator.

T1078 Valid AccountsCWE-287 Improper Authentication
5

Impact — administrative data access

With an admin session the agent loaded the matters dashboard, counted the reachable client records to establish blast radius, captured proof of the privilege level, and halted. No record contents were read or exfiltrated.

T1213 Data from Information RepositoriesT1078.001 Default Accounts
03

Reproduction

Deterministic. Any engineer can replay this in under a minute against the same build.

Confirm the injection point

Submit a single quote as the username. The error fragment confirms input reaches the query unescaped.

requestHTTP
POST /client-login HTTP/1.1
Host: portal.whitmorehale.example
Content-Type: application/x-www-form-urlencoded

username='&password=x
response500
HTTP/1.1 500 Internal Server Error

{ "error": "SQL syntax error near ''' at line 1",
  "code": "ER_PARSE_ERROR" }

Bypass authentication

Send the tautology payload. The response is a 302 to the dashboard with a valid admin session cookie — no password required.

requestHTTP
POST /client-login HTTP/1.1
Host: portal.whitmorehale.example
Content-Type: application/x-www-form-urlencoded

username=' OR 1=1 -- &password=x
response302
HTTP/1.1 302 Found
Location: /dashboard
Set-Cookie: session=a8f2…9c1d; HttpOnly; Path=/
X-Auth-User: admin@whitmorehale.example  # first row in users

Confirm the resulting query

The application composed this statement (recovered from the query log). The comment sequence discards the password check entirely.

server-side querySQL
SELECT * FROM users
WHERE username = '' OR 1=1 -- ' AND password = '…ignored…'
-- resolves to: WHERE username='' OR 1=1  → every row; LIMIT 1 returns the admin
04

Proof of impact — captured safely

The point of a proof is to remove all doubt that impact is real, without becoming the breach it warns about. K0G proves reach, then stops. Here is exactly what was and was not done.

SignalWhat K0G capturedDamage done
Privilege levelResponse header X-Auth-User: admin@… and the admin-only nav renderingNone
Session validityRe-used the cookie on a second admin route; got 200None
Blast radiusRow count onlySELECT COUNT(*) returned 1,284; no rows fetchedNone
Data sensitivityColumn names from schema metadata (e.g. settlement_amount) — never valuesNone
ScreenshotDashboard chrome with all record bodies masked client-side before captureNone

The line K0G does not cross

  • Counts, never contents. Reach is proven with COUNT(*) and schema metadata, not by reading or dumping records.
  • Read, never write. No INSERT, UPDATE, DELETE or DROP is ever issued. The database is left byte-for-byte unchanged.
  • Prove the path, don't walk it. Once admin access is demonstrated, the run halts — it does not pivot deeper or touch other systems.
  • Every request is logged in order with timestamps, exportable to your SIEM, so your team sees exactly what the agent did.
05

Remediation

The root cause is string-built SQL. The fix is a parameterised query so user input can never change the statement's structure. K0G opened this as a pull request against the target repo, in the codebase's own style.

auth/login.jsPR #4471
 function findUser(username, password) {
-  const q = `SELECT * FROM users
-    WHERE username = '${username}' AND password = '${hash(password)}'`;
-  return db.query(q);
+  const q = `SELECT * FROM users WHERE username = ? AND password = ?`;
+  return db.query(q, [username, hash(password)]);   // parameterised — input can't alter structure
 }

Defence in depth, recommended alongside: enforce rate limiting and lockout on failed authentication (the secondary finding from hop 2); adopt a query builder or ORM that parameterises by default across the codebase; and add a WAF rule as a compensating control while the fix ships — noting a WAF is mitigation, not a fix.

ReferenceIdentifier
WeaknessCWE-89 — Improper Neutralization of Special Elements used in an SQL Command
Auth weakness (chain)CWE-287 — Improper Authentication
OWASP Top 10 (2021)A03:2021 — Injection
OWASP test guideWSTG-INPV-05 — Testing for SQL Injection
Compliance touchpointsPCI DSS v4.0.1 Req. 6.2.4 · SOC 2 CC7.1 · ISO/IEC 27001:2022 A.8.28
06

Verified retest

A finding is not closed because a PR merged — it is closed because the original attack path no longer works. When the fix landed, K0G replayed the exact chain from hop 3.

Before · run #8842
302 → admin session

The tautology payload authenticated as admin. 1,284 records reachable. Critical, exploitable.

After · run #8967
401 → rejected

Identical payload treated as a literal username, matched no user, returned 401. Injection error gone. Path closed.

retest — same payload, patched build401
POST /client-login  →  username=' OR 1=1 -- &password=x

HTTP/1.1 401 Unauthorized
{ "error": "invalid credentials" }   # payload matched no user; no SQL error

✓ attack path K0G-2026-0417 no longer reproducible — finding closed 2026-08-05

Every K0G finding looks like this

  • Point K0G at one environment and we will show you a working attack path within the hour — with a proof exactly this concrete — or tell you plainly that we could not find one.
Book a live attack Run this scenario yourself →