Web attacks

SQL injection: three requests from probe to database version

Updated

File
http_dvwa_sqlinjection.pcapng
Format
pcapng, ether
Size
9.86 KB
Packets
35
Duration
45 s
Captured (UTC)
2017-04-05 20:15:44
SHA-256
09f674a52703aaa23fa73fa47fd2e23030a5649815395dc6c11242e1e01f1102

This SQL injection PCAP holds three GET requests to one DVWA endpoint over 45 seconds, and three 200 OK answers that mean three different things. The first response lists every user record, the second is a 139-byte MySQL syntax error, and the third carries the database version grafted into the page. SQL injection is easy to see in a packet capture, because the attack and its result both travel in the HTTP request and response. A-Packets rebuilds each request with the response that followed, so you can read the escalation the way the attacker worked it, one step at a time.

The target is the SQL injection exercise in DVWA, the Damn Vulnerable Web Application, and the traffic is plain HTTP, so nothing is hidden: the crafted parameter and the data it pulled back are both in the capture. That makes this a clean teaching example of what a successful injection looks like on the wire, and of the one response detail, a change in what the page returns, that separates a real finding from a request that was merely attempted.

HTTP view of the SQL injection PCAP in A-Packets. The expanded HTTP session shows the corrected UNION version() request with a trailing comment and its 200 OK response.
The expanded HTTP session shows the corrected UNION version() request with a trailing comment and its 200 OK response.

Three requests that escalate from probe to data

The requests all hit one endpoint, /dvwa/vulnerabilities/sqli/, and all tamper with one parameter, id. A-Packets shows each crafted value in full in the HTTP sessions view; decoded from URL encoding, they read as a textbook manual-testing sequence.

HostRequestStatusType
10.2.2.104GET /dvwa/vulnerabilities/sqli/?id=%25%27+or+%270%27%3D%270&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.36200 OKtext/html 1.46 KB
10.2.2.104GET /dvwa/vulnerabilities/sqli/?id=%25%27+or+0%3D0+union+select+null%2C+version%28%29&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.36200 OKtext/html 139 Bytes
10.2.2.104GET /dvwa/vulnerabilities/sqli/?id=%25%27+or+0%3D0+union+select+null%2C+version%28%29+%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.36200 OKtext/html 1.51 KB

Open the HTTP view →

The first value is %' or '0'='0. The stray single quote breaks out of the string the application wraps the input in, and the or '0'='0 makes the WHERE clause always true, so the query returns every row instead of one. Seeing the full list of names come back where a single record should appear is the confirmation that the parameter is injectable: the input changed the meaning of the query, not just its value.

The next two values show a failed attempt followed by a correction. %' or 0=0 union select null, version() appends a second query whose results are grafted onto the application's own output; the attacker chooses what it selects, here version(), the database's version string. Its answer, frame 18 on TCP stream 1, is 139 bytes of MySQL syntax error near the leftover quote, under an HTTP status of 200, and the error itself tells the attacker which database engine is listening. The third request, frame 27, adds a trailing # to comment out the remainder of the original query so the injected UNION parses cleanly, and its 1,543-byte answer in frame 30 carries the database version back inside the page: the attacker reading data of their choosing straight out of the application.

The # travels as %23 and becomes a SQL comment only after the server decodes the parameter. Typed literally into a browser's address bar it would start a URL fragment and never reach the server, which is why the request target as sent, not the URL as displayed, belongs in the notes.

Why UNION SELECT is the workhorse of manual injection

A UNION combines the rows of two SELECT statements into one result set, provided both return the same number of columns with compatible types. That is why injection testers probe column counts first (an automated tool does it with ORDER BY 1, ORDER BY 2 and so on until the query errors) and why this sample selects two values, null and version(), to match the two columns the original query returns: null fills one position, and version() lands in a column the page displays. Once the shapes match, the attacker can replace version() with any expression the database can evaluate: the current user, the list of tables, or the contents of a table. The password dump sample is the same technique pointed at the users table.

The reason UNION-based injection is so visible in a capture is that the stolen data comes back in the normal response body, in place of or alongside the application's own output. There is no separate channel and no encoding to undo when the site is plain HTTP: the request names what to steal and the response contains it. That is the opposite of a blind injection, where the attacker infers data one bit at a time from the application's behaviour and the responses look almost identical, a pattern the sqlmap scan shows when a tool automates it.

Reading the response, not just the request

The request tells you what was attempted; the response tells you whether it worked, and that distinction is the whole job when you triage an injection alert. A payload in a log or a capture proves someone tried; a response that returned extra rows, a version string, or a column of data that the endpoint has no business returning proves the flaw is real. In this capture the proof is plain: the first probe returns multiple names, the un-commented UNION returns an error, and the corrected UNION returns the version. A-Packets carves the response bodies so you can confirm the data came back rather than guessing from the request alone.

SQL injection progression: a boolean bypass, a UNION syntax error, then a corrected UNION that returns the database version.
SQL injection progression: a boolean bypass, a UNION syntax error, then a corrected UNION that returns the database version.

In Wireshark you can isolate the same evidence with http.request.uri contains "union" for the UNION attempts, or more broadly http.request.uri contains "select" and the single-quote and comment sequences that mark injection, then follow each TCP stream to read the response. On a production web server the equivalent is the access log, which records the injected parameter with the status and the response size. This capture shows why the size matters more than the status: all three answers are 200, but the failed one is 139 bytes against roughly 1.5 KB for the two that worked.

When the data does not come back in the page

This capture is the easy case, where the injected query's results appear directly in the response. Real targets are often less generous, and the techniques an attacker falls back to change what the capture shows. In blind injection, the application returns no data and no error, so the attacker infers it one true/false question at a time from a difference in the page (boolean-blind) or from a deliberate delay (time-based blind, using a database sleep function). In those cases the capture holds many similar requests and near-identical responses, and the signal is the pattern and the timing rather than a visible data leak, the shape the automated sqlmap scan produces when it has to work blind.

There is also out-of-band injection, where the attacker makes the database itself open a connection, a DNS lookup or an HTTP request, to a server they control, carrying the stolen data in that request. That leaves no trace in the web response at all; the evidence is an unexpected outbound connection from the database server, which is why monitoring egress from database hosts matters as much as watching the web tier. The UNION-based sample here is the most visible point on this spectrum; knowing the quieter variants exist is what keeps a defender from assuming that a clean web-response capture means no injection occurred.

Fixing the flaw and detecting the attempt

The defence is in the application, not the network. Parameterised queries, also called prepared statements, send the SQL structure and the user input to the database separately, so bound values are treated as data rather than SQL syntax; an injected quote becomes part of the data, not part of the query. Constraining what the database account can read, so the web application's user cannot reach other tables, limits the damage if a flaw slips through, and an object-relational mapper used correctly gives parameterisation by default. These remove the vulnerability; input filtering and web application firewalls only raise the bar.

Prepared parameters cover values, not SQL identifiers or sort directions. If a route lets the user choose a column or a table, map the choice through a fixed server-side set instead of interpolating it, and review stored procedures and ORM escape hatches for dynamic SQL; the OWASP prevention guidance covers each case. Retest the repaired lookup with ordinary IDs, missing IDs and strings full of punctuation: every one must keep the same query structure and return only authorized records, with no database error text, and a WAF that blocks this exact spelling proves nothing about the query underneath.

On the detection side, the injected parameters are what to alert on and to search logs and captures for: quotes, UNION SELECT, SQL comment markers and boolean tautologies like or 1=1 in query strings. A web application firewall will flag and slow these, and rate limiting blunts an automated scan, but neither is a substitute for parameterised queries. In MITRE ATT&CK terms a web-facing injection is T1190, Exploit Public-Facing Application, and the data it returns maps to collection techniques depending on what the attacker pulls.

This sample is the manual cousin of the automated sqlmap scan and the lighter end of the password-dump case. For the broader set of web attacks visible in traffic, see top threats found in PCAP analysis, or upload a capture of your own to read its HTTP the same way.

More sample reports