Description
Can you find the flag on this website.
Solution
- Step 1Initial explorationLet's start by trying "admin" for both the username and password to see what happens.
Looking at the error, we can see the SQL query being executed. Notice two important things: the password field comes first in the query, and it uses single quotes (not double quotes). This tells us where to inject our payload.For SQL injection, common comment characters are#and--.SELECT id FROM users WHERE password = 'admin' AND username = 'admin'Learn more
SQL injection (SQLi)occurs when user-supplied input is concatenated directly into a SQL query without sanitization or parameterization. The database engine cannot distinguish between the developer's intended query structure and the attacker's injected SQL code, so it executes both together.
The error message here is invaluable: it reveals the exact query template. Knowing that the password field appears first and uses single quotes tells you precisely where to inject and which quote character to close. Verbose error messages - which reveal internal query structure - are themselves a security vulnerability (CWE-209: Information Exposure Through an Error Message) that should be disabled in production.
The two most common SQL comment styles are
--(ANSI standard, supported by PostgreSQL, SQLite, SQL Server) and#(MySQL-specific). Knowing which database backend is running helps you choose the right syntax. Stack traces and error messages often reveal the database type. - Step 2Bypassing the loginNow let's use a classic SQL injection payload in the password field. The first single quote closes the password string,
OR 1=1is always true, and the--comment makes everything after it irrelevant.This transforms the SQL query into:
Success! We're in and now see a database interface with another input field, which means more SQL injection is needed.' OR 1=1 -SELECT id FROM users WHERE password = '' OR 1=1 - ' AND username = 'does not matter'Learn more
The payload
' OR 1=1 --works by exploiting the query structure. The opening single quote closes the string literal that started with the password field.OR 1=1adds a condition that is always true, so the WHERE clause evaluates to true for every row. The--comments out the rest of the original query (the username check), making it irrelevant. The result: the query returns at least one row, and the application logs you in.This is one of the most well-known attack patterns in web security - it appears in OWASP's Top 10 as "Injection." The definitive fix is parameterized queries (also called prepared statements), where SQL structure and user data are sent to the database separately:
SELECT id FROM users WHERE password = ? AND username = ?. The?placeholders are filled by the database driver, which treats the values as data - never as SQL code - regardless of what they contain. - Step 3Finding the number of columnsFirst, we need to figure out how many columns the query returns. We'll use a UNION SELECT with null values.
' UNION SELECT null,null,null;--Before:
After:
Perfect! The query works with three null values, which means we're dealing with three columns.Learn more
UNION-based SQL injection appends a second SELECT statement to the original query using the SQL
UNIONoperator. For UNION to succeed, both SELECT statements must return the same number of columns with compatible data types. The standard technique to discover the column count is to tryUNION SELECT null,null,...with increasing numbers of nulls until the query succeeds instead of erroring.nullis used because it is compatible with every data type.Once the column count is confirmed, you identify which columns are displayed to the user (they must be of string type) by replacing nulls one at a time with a visible string like
'a'. The position where 'a' appears in the output is the column you will use to exfiltrate data in subsequent queries.This enumeration process is tedious to do manually. Tools like sqlmap automate the entire workflow - detection, column counting, type detection, and data extraction - but understanding the manual process is essential for CTF challenges where sqlmap may be blocked or too noisy.
- Step 4Enumerating the database schemaSince this is likely SQLite (common in CTF challenges), we can query
sqlite_master, an internal table that contains schema information.
Excellent! From the output, we can see there's a table calledmore_tablewith a column namedflag. That's exactly what we need!' UNION SELECT sql,null,null FROM sqlite_master;--Learn more
Every database engine exposes metadata about its own structure through special tables or views. In SQLite,
sqlite_mastercontains one row per database object (table, index, view, trigger) with the originalCREATEstatement in thesqlcolumn. This lets an attacker enumerate every table name and column definition in a single query.Equivalent metadata tables in other databases: MySQL/MariaDB uses
information_schema.tablesandinformation_schema.columns; PostgreSQL usespg_tablesandinformation_schema; Microsoft SQL Server usessys.tablesandsys.columns. Knowing which system you're targeting determines which metadata query to use.This schema enumeration step is critical in real-world SQLi attacks - it tells the attacker exactly what data exists before they start extracting it. Modern web application firewalls (WAFs) often block queries containing
information_schemaorsqlite_master, which is why understanding multiple bypass techniques is valuable. - Step 5Extracting the flagNow we just need to select the flag from more_table. You can put the flag in any column position.
And there's the flag!' UNION SELECT null,null,flag FROM more_table;--' UNION SELECT flag,null,null FROM more_table;--Learn more
With the table and column names confirmed, the final UNION SELECT directly reads the target data. Placing the interesting column in position 1, 2, or 3 (whichever appears in the output) lets you retrieve it. If the table has multiple rows, all of them appear in the result set - a UNION returns all matching rows from both SELECT statements.
In real-world SQL injection, the "flag" is whatever sensitive data the attacker is after: password hashes, credit card numbers, session tokens, private keys, or PII. The attack chain is always the same: bypass authentication, enumerate schema, extract target columns. The entire process takes minutes with sqlmap, or an hour manually - which is why parameterized queries are the only reliable defense.
For learning, try practicing SQLi on intentionally vulnerable applications like DVWA (Damn Vulnerable Web Application), WebGoat, or HackTheBox web challenges. Understanding both the attack and the defense makes you a better developer and security tester.
Flag
picoCTF{G3tting_5QL_1nJ3c7I0N_l1k3_y0u_sh0...}