Description
Can you beat the filters? Log in as admin across 5 progressive rounds, each adding more filtered SQL keywords.
Setup
Navigate to the Web Gauntlet challenge URL and open the login page.
Each round adds more blocked SQL keywords -- you must find an injection that bypasses the current filter.
Solution
- Step 1Round 1 -- filter: orOnly 'or' is filtered. A classic comment-based injection works: enter admin'-- as the username. The SQL becomes SELECT * FROM users WHERE username='admin'--' AND password='...' -- the comment drops the password check.Username: admin'--Password: anything
Learn more
SQL injection occurs when user-supplied data is interpolated directly into a SQL query without sanitization. The single-quote character
'closes the string literal the database parser expects, allowing the attacker to append arbitrary SQL syntax. The--sequence is a SQL line comment -- everything after it on that line is ignored, so theAND password='...'condition is never evaluated.The resulting query becomes:
SELECT * FROM users WHERE username='admin'--' AND password='anything'. The database seesusername='admin'as a complete, valid condition and ignores the rest. This is authentication bypass -- the attacker is authenticated as admin without ever knowing the password.The fix is parameterized queries (prepared statements). When you write
WHERE username = ?and pass the value separately, the database treats the value as data -- no matter what characters it contains -- and the quote characters in the input can never escape the string context. - Step 2Round 2 -- filter: or, and, =, like, --Comments and most comparison operators are now blocked. Use a UNION-based injection that avoids all filtered words.Username: admin' union select * from users where '1
Learn more
UNION-based injection appends a second
SELECTstatement to the original query. Here the payload closes the first string literal, adds aUNION SELECTthat returns the admin row directly from the users table, and ends withwhere '1-- an open string that the original closing quote will close, keeping the SQL syntactically valid.The filter blocks
=, so the original query'sWHERE username = '...'comparison would fail -- but by making the UNION return the target row outright, you sidestep that comparison entirely. The database evaluates the union of both SELECT results and the application sees an admin record.Keyword-based SQL injection filters are fundamentally flawed because SQL is a rich language with many equivalent ways to express the same logic. Security-focused developers should use parameterized queries instead of attempting to enumerate and block every possible injection pattern.
- Step 3Round 3 -- filter adds >, <, --Angle brackets are now blocked. A semicolon terminator still works to end the statement cleanly.Username: admin';
Learn more
A semicolon terminator ends the SQL statement immediately after the injected condition. The payload
admin';producesWHERE username='admin';-- a complete, self-contained query that selects the admin user. The rest of the original SQL (the password check) becomes a second, separate statement that many SQL engines either ignore or reject harmlessly.Whether a trailing semicolon works depends on the database driver. Some drivers (notably older versions of PHP's
mysql_query()) execute only the first statement. Others support multi-statement execution, making semicolons dangerous for stacked queries -- the ability to inject completely separate SQL commands likeDROP TABLE users;.SQLite, which this challenge uses, is permissive about trailing semicolons in single-statement execution contexts. Understanding which database is in use guides which injection techniques are viable -- MySQL, PostgreSQL, SQLite, and MSSQL each have different syntax, functions, and behavior.
- Step 4Round 4 -- filter adds 'admin'The literal word 'admin' is now filtered. SQLite supports the || string concatenation operator, which reassembles the word at query time after the filter has already run.Username: ad'||'min';
Learn more
SQLite's
||operator concatenates strings at query evaluation time. The payloadad'||'minis read by the filter as two separate fragments -- neither of which is the blocked wordadmin-- but when the SQL engine evaluates the expression, it concatenates them to produceadmin, matching the row in the database.This technique -- splitting a blocked keyword across a string concatenation -- is a classic filter evasion. Other database-specific equivalents include MySQL's
CONCAT()function, SQL Server's+operator for strings, and comment injection (ad/**/min) which some parsers collapse before matching keywords.This round illustrates why input filters are the wrong security model: the filter operates on the raw input string, but the database sees the evaluated result. Any transformation that the database applies after the filter -- concatenation, decoding, case folding -- can be used to reconstruct blocked strings.
- Step 5Round 5 -- filter adds unionThe UNION keyword is blocked, but the concatenation trick from Round 4 still works unchanged.Username: ad'||'min';
Learn more
The same
ad'||'min';payload from Round 4 remains effective here because it does not useUNIONat all. It works at a more fundamental level -- matching the username directly -- making it robust to any additional keyword filters that do not block the||operator or the semicolon.This progression through five rounds mirrors real-world iterative security hardening: developers add one restriction at a time in response to discovered attacks, creating a patchwork filter that still has gaps. Each individual restriction is easy to bypass when you can find an equivalent expression the filter does not recognize.
The correct defense -- parameterized queries -- would have prevented every round of this challenge with a single change. The lesson is that defense-in-depth is not the same as defense-in-filters: applying many shallow defenses does not substitute for one correct structural fix.
- Step 6Retrieve the flag from /filter.phpAfter completing all five rounds, navigate to /filter.php on the same host. The page displays the flag directly.curl http://<host>/filter.php
Learn more
/filter.phpis an exposed debug or status page that lists all the filters applied in each round. In a real application, such pages should never be accessible without authentication -- leaving admin or diagnostic pages publicly readable is a common misconfiguration vulnerability classified as Security Misconfiguration in the OWASP Top 10.The challenge rewards completing all five rounds by exposing this page, tying together the SQL injection exercise with a basic web reconnaissance skill: checking for predictable paths (
/admin,/debug,/config,/filter.php) that application developers left publicly accessible. Tools like dirb, gobuster, and feroxbuster automate this path enumeration in real penetration tests.
Flag
picoCTF{...}
SQLite's || string concatenation operator can reconstruct filtered keywords -- when 'admin' is blocked, ad'||'min' reassembles it at query time after the filter has already checked the input.