Web attacks
SQL injection password dump: five accounts in one HTTP response
Updated
- File
- http_dvwa_sqlinjection_passwords.pcapng
- Format
- pcapng, ether
- Size
- 4.21 KB
- Packets
- 12
- Duration
- 9 s
- Captured (UTC)
- 2017-04-05 20:20:43
- SHA-256
- 2c9208b44e2bcc7fa4340b7d9f0c7cfd92c2b41b24294e532eddd6359b36d2de
One request, one response, five accounts: in this SQL injection password dump PCAP a single UNION SELECT
against DVWA's id parameter returns the names, usernames and password hashes of five users inside a
1,685-byte page, and the whole capture is twelve packets. It is the SQL injection exercise in DVWA driven one step
past proving the flaw: instead of reading the database version, the attacker selects the users table,
and the rows come straight back in the HTTP response. A-Packets rebuilds the request and its answer, which is all it
takes to show how a single unparameterised query turns into a full credential leak.
The brevity is the lesson. There is no multi-stage intrusion here, no lateral movement, no second payload: one crafted request and one response. That is how fast a confirmed injection against a sensitive table becomes a breach, and why an injection alert on an authentication or user endpoint deserves immediate attention rather than a queue.
One SQL injection request returns five credential records
The request appends a UNION SELECT to the vulnerable id parameter that concatenates four
columns from the users table, the first name, surname, username and password, into the application's normal output.
The server answers 200 OK and the rows are in the response body, which A-Packets carves from the HTTP
flow. In one exchange the attacker has gone from an injectable parameter to five account records and their password
hashes.
| Host | Request | Status | Type |
|---|---|---|---|
| 10.2.2.104 | GET /dvwa/vulnerabilities/sqli/?id=%25%27+and+1%3D0+union+select+null%2C+concat%28first_name%2C0x0a%2Clast_name%2C0x0a%2Cuser%2C0x0a%2Cpassword%29+from+users+%23&Submit=SubmitUA: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 | 200 OK | text/html 1.65 KB |
The expression in frame 4 is built for a clean read. and 1=0 before the UNION suppresses
the original lookup's own rows, so only the injected result comes back. The two selected expressions,
null and a concat() of first name, last name, user and password separated by
0x0a, keep the two-column shape the page expects while packing four fields into the one column it
displays, and a trailing encoded comment stops the rest of the application's SQL from breaking the statement.
The answer, frame 7, returns the familiar DVWA accounts, admin, Gordon, Hack, Pablo and Smith, each followed by a 32-character hexadecimal value: DVWA's unsalted MD5 password hashes, masked on this page. Its 1,685 bytes are the whole HTML page, markup included, not the size of a database export. Because the capture is plain HTTP, both the injection string and the returned rows are fully visible; the HTTP sessions view shows exactly where in the exchange the dump happened.
Two failures in one response
A dump like this exposes two separate weaknesses, and a thorough response addresses both. The first is the
injectable parameter: user input reached the SQL query unparameterised, which is what let the UNION
run at all. The second is how the passwords were stored. These are unsalted MD5 hashes, a scheme that is fast to
compute and has no per-account salt, so identical passwords produce identical hashes and candidate passwords can be
checked cheaply. Weak and common passwords fall to precomputed lookups first; long random ones still hold out.
The point for a defender is that the injection flaw determines whether the table can be stolen, and the hashing scheme determines how bad the theft is. A leaked table of slow, salted hashes buys time to force a reset before the passwords are recovered; unsalted MD5 leaves weak passwords at immediate risk. Both defences are needed because they protect against different stages of the same incident.
What the dumped rows tell an investigator
A credential table pulled this way is not just a list of hashes; it is a map of the application's accounts and how
they were protected. The five rows here show the structure every dump has: an identity column or two, a username,
and the password material. In a real incident the same shape lets a responder answer the questions that matter
immediately, which accounts are disclosed and what password representation they expose. The admin row
is always the first to check, because an administrator password recovered from a dump converts directly into
control of the application.
The hashing scheme answers the "how bad" question before any cracking is attempted. Unsalted MD5, which is what this table uses, means identical passwords share a hash, so an attacker can spot password reuse across accounts at a glance and check the whole column against a precomputed table in seconds. That is why the response to a confirmed dump cannot wait for proof that the hashes were recovered: with a fast, saltless scheme, the weak passwords in the table are recoverable before containment completes. The capture gives the responder the one fact that sets the clock, that the table left the database, and the hashing scheme tells them how little time they have.
Why a dump is worse than the hashes suggest
The immediate loss is this application's accounts, but a credential dump rarely stays contained to the application it came from. People reuse passwords, so a password recovered from this table is worth trying against the same users' mail, VPN and single-sign-on accounts, the classic credential-stuffing path. That is why the response cannot be scoped to "reset passwords on this app": the exposed passwords should be treated as burned everywhere the affected users might have reused them, and the users told to change them on other services too. The dump is a seed for attacks far beyond the breached application.
It also changes the detection calculus going forward. Once an attacker holds a valid username and password for an account, their next login looks legitimate, so the signal shifts from the injection, which is loud, to the account takeover, which is quiet. Watching for logins from new locations or at odd times for the accounts in a dumped table, and forcing a reset before those logins can happen, is the follow-through that a confirmed dump demands. The capture gives you the one thing that starts that clock: proof the table left the database, and the list of accounts to protect.
Fixing it and responding to a confirmed dump
Close the injection with parameterised queries and a least-privilege database account, so the web application's user cannot read the credential store even if a flaw exists elsewhere. Store passwords with a slow, salted, purpose-built algorithm, bcrypt, scrypt or Argon2, so a future leak is far less catastrophic. And when a dump is confirmed, as it is here by credential records in the response, containment and scoping should proceed together: force a reset of every account in the dumped table, on the assumption that the hashes will be recovered, rather than waiting to see whether they are. Take the full list of affected accounts from the database and its logs, since one response shows what the page displayed, not everything the query could reach.
Changing the hashing function does not retroactively protect the stolen MD5 values. A migration needs fresh passwords, or a rehash on each user's next login plus a plan for accounts that never return. For the disclosed set, force the reset and invalidate the application's sessions, so an old cookie does not outlive the password change, and check how reset tokens, remember-me cookies and API tokens are issued before assuming a password change revokes them. Tune the new algorithm's memory and time cost to the service, following the OWASP password storage guidance, and retest the data-query route separately from the login page: strong storage does not stop names or emails leaking through an endpoint that is still injectable.
On the network side, the injected parameter is what to hunt for. A query string containing union select
and from users, or any reference to a credential table, paired with a 200, identifies a
transaction to inspect, and the rows in the body confirm the disclosure. Size will not help: five credential records
fit inside an ordinary 1.6 KB page. In Wireshark, http.request.uri contains "union" finds the request
and following its stream shows the returned rows.
This is the high-impact end of the flaw shown in the SQL injection sample, where the same technique reads only the database version, and the manual counterpart to the automated sqlmap scan. In MITRE ATT&CK terms it is T1190, Exploit Public-Facing Application, leading directly to credential access. See also extracting credentials from PCAP files, or upload a capture to inspect one of your own.