Description
Final boss! Log in as admin using the SQLite-backed login form. More keywords are filtered this time, and the combined username + password input is capped at 25 characters.
Visit /filter to see the full blocklist before attempting your bypass.
Setup
Open the challenge URL and check /filter to see blocked keywords.
curl http://mercury.picoctf.net:<PORT_FROM_INSTANCE>/filterSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
|| concat variant that keeps the payload under the character limit. For the broader filter bypass library, see the SQL Injection for CTF guide.Step 1
Enumerate the expanded blocklistObservationI noticed the challenge description mentioned a /filter endpoint and a 25-character cap, which suggested the first move was to enumerate the expanded blocklist before crafting any payload.GET /filter to see every blocked term. Compared to Web Gauntlet 2, this version adds=,>,<,;,--,/*, and*/to the blocklist. Crucially, the||string-concatenation operator is still allowed, and so areIS NOTandGLOB. The twist is a hard 25-character limit on the combined username + password input.bashcurl http://mercury.picoctf.net:<PORT_FROM_INSTANCE>/filterWhat didn't work first
Tried: Trying the same 'ad'||'min' + OR 1=1 payload that worked in Web Gauntlet 2
OR and = are both blocked in this round, so the tautology 'OR 1=1' is stripped before the query runs. The filter response at /filter explicitly lists both operators. You need a different always-true expression such as 'a' IS NOT 'b' that avoids every blocked keyword.
Tried: Skipping /filter and guessing the blocklist from prior rounds
Each Web Gauntlet round adds new blocked terms, so the prior-round blocklist is incomplete. This round adds =, >, <, ;, --, /*, and */ compared to Gauntlet 2. Crafting a payload without first checking /filter risks using operators that are now banned and receiving a silent login failure with no useful error message.
Learn more
Full blocklist. The filtered terms are:
or and true false union like = > < ; -- /* */ admin. Everything else is fair game, including||,IS NOT,GLOB, and the bitwise|operator.Strategy. The same
||concatenation trick that cracked Gauntlet 2 still works here. The new challenge is staying within 25 total characters while also bypassing the password check without=orLIKE.Step 2
Build 'admin' via || concatenation and bypass the password checkObservationI noticed that the blocklist allowed || (concatenation) and IS NOT while banning OR, =, and LIKE, and that the 25-character cap forced a compact payload, which suggested splitting 'admin' with || and using 'a' IS NOT 'b' as the always-true password tautology to fit exactly within the limit.Split the blocked wordadminacross two string literals joined by||: usernameadm'||'in. SQLite evaluates the concatenation before comparing, so the query seesusername='admin'without the literal word ever appearing in your input. For the password, useIS NOT(not blocked) to construct a condition that is always true:a' IS NOT 'b. Together the two fields are 21 characters, well within the 25-character cap.bash# Username: adm'||'in (9 chars) # Password: a' IS NOT 'b (12 chars) # Total: 21 chars -- well within the 25-char limit curl -X POST http://mercury.picoctf.net:<PORT_FROM_INSTANCE>/login \ --data-urlencode "username=adm'||'in" \ --data-urlencode "password=a' IS NOT 'b"A successful login redirects to a page containing the flag.What didn't work first
Tried: Using GLOB '*' as the tautology but exceeding the 25-character limit by choosing a long username split
Both username splits ('adm'||'in' and 'ad'||'min') are 9 characters each. Combined with the 12-character password 'a' IS NOT 'b' the total is 21 characters, well within the 25-char cap. No alternative split is actually required by the character limit.
Tried: Using 'OR 'a'='a or 1=1 as the password tautology since those patterns bypassed earlier gauntlet rounds
Both = and OR appear on this round's blocklist, so either form is stripped from the input. The login silently fails and returns the login page again with no SQL error. IS NOT is the unblocked alternative: 'a' IS NOT 'b' evaluates to 1 (true) in SQLite without using any blocked operator.
Learn more
How the query looks to SQLite. The server likely runs something like:
SELECT username, password FROM users WHERE username='adm'||'in' AND password='a' IS NOT 'b'
adm'||'inevaluates toadmin, matching the row.'a' IS NOT 'b'is alwaysTRUE(they are different strings), so the password condition is satisfied regardless of the stored password.Why IS NOT works as a truth bomb. Unlike
=(blocked),IS NOTis SQLite's null-safe inequality operator.'a' IS NOT 'b'is always 1 (true), giving us a tautology that replaces the blockedOR 1=1pattern.25-character accounting. If the limit is tight, count carefully:
adm'||'inis 9 characters,a' IS NOT 'bis 12 characters, summing to 21 (well within the 25-char cap). Alternative: usernamead'||'min(9 chars) plus a shorter tautology likea' GLOB '*(10 chars) totals 19.See SQL injection for CTF for the broader operator substitution trick library.
Interactive tools
- SQL Injection Payload GeneratorGenerate SQL injection payloads for auth bypass, UNION extraction, blind SQLi, NoSQL operator injection, and sqlmap commands. Supports MySQL, PostgreSQL, SQLite, and MSSQL.
Flag
Reveal flag
picoCTF{k3ep_1t_sh0rt_...}
Per-instance flag. All confirmed variants share the prefix k3ep_1t_sh0rt_ with a different hex hash suffix per team (e.g. fc8788aa1604881093434ba00ba5b9cd, ef4a5b40aa736f5016b4554fecb568d0, 30593712914d76105748604617f4006a).