Description
Even more SQL injection. A plain authentication-bypass payload returns a blank page instead of logging you in. The twist is hidden in the page source: a debug parameter that, when enabled, shows the server is transforming your input before it reaches the query.
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Find the hidden debug parameter in the page source
ObservationA standard authentication-bypass payload returns a blank page, not a login success and not a visible error. Something is transforming the input, so the raw page source is worth checking for hidden form fields.Submit a normal authentication-bypass payload (for example ' or 1=1 - ) and notice you get a blank page rather than a login or a clear error. View the source of the login page: there is a hidden form field named debug with value 0. Resend the request with debug=1 to turn on the server's debug output, which prints the SQL query it actually built - and you can see your input has been mangled by a letter rotation before it was placed in the query.bash# In the browser: View Source on the login page, find <input ... name="debug" value="0">bash# Resend the POST with debug flipped on so the server echoes the built query:bashcurl -s 'http://<host>:<PORT_FROM_INSTANCE>/login.php' \bash--data "username=test&password=' or 1=1 -- &debug=1"Expected output
' be 1=1 --
What didn't work first
Tried: Send ' or 1=1 - directly as the username without enabling debug, hoping the blank page is just a CSS glitch
The blank page is intentional - the server applies ROT13 to your input before building the query, so the keyword OR becomes BE and the injection never fires. You need debug=1 to see the mangled query and understand what transformation is happening.
Tried: Try adding debug=1 as a GET parameter in the URL rather than as a POST body field
The PHP script reads debug from the POST body, not the query string. Appending ?debug=1 to the URL leaves the body unchanged, so the server still returns a blank page. Put debug=1 inside the --data string alongside username and password.
Learn more
The real lever in this challenge is the hidden
debugparameter, not the encoding itself. Settingdebug=1makes the server print the SQL string it constructed. Comparing what you sent against what the server built shows that your letters were rotated:orarrives asbe, the whole alphabet comes back shifted. That is ROT13 applied to your input before it hits the query.ROT13 is a Caesar cipher that rotates each letter by 13 positions; applying it twice returns the original. Crucially it touches only letters - digits, spaces, and punctuation (including SQL characters like
'and-) pass through unchanged. So the SQL structure of your payload survives, but any letters (table names, keywords like OR) are scrambled.Step 2Pre-rotate your payload so it survives the transform
ObservationThe debug output shows the keyword OR came back as BE, so the server ROT13s the username before building the query. ROT13 is self-inverse, so pre-rotating the payload makes the server's own transform restore the SQL keywords.Take the authentication-bypass payload that worked in part 2 and ROT13 its letters before sending it. Because ROT13 is self-inverse, the server's rotation restores the original keywords. The apostrophe, dashes, spaces, and digits are unaffected, so the SQL structure stays intact. For example ' or 1=1 - has its letters rotated to ' be 1=1 - ; when the server ROT13s that back, OR returns and the injection runs.pythonpython3 -c "import codecs; print(codecs.encode(\"' or 1=1 -- \", 'rot_13'))"bash# Send the rotated string; the server ROT13s it back to a valid SQLi payload.bashcurl -s 'http://<host>:<PORT_FROM_INSTANCE>/login.php' \bash--data "username=' be 1=1 -- &password=x"What didn't work first
Tried: ROT13-encode the entire payload including the apostrophe and dashes, expecting the punctuation to also need encoding
ROT13 only shifts letters; apostrophes, spaces, and dashes pass through unchanged. Python's codecs.encode behaves the same way, so the SQL structural characters are already correct. Over-encoding the non-letters is a no-op here, and the real payload is exactly ' be 1=1 - .
Tried: Send the rotated payload in the password field instead of the username field
The debug output shows the server uses the username field as the injection point. Put the payload in the password field and the username slot stays a plain string that never breaks the query, so authentication just fails. Only the username gets ROT13'd and interpolated into the WHERE clause.
Learn more
The trick is to pre-image the transformation. You ROT13 only the letters of your payload, so after the server applies its own ROT13 the keywords come back to life:
bebecomesORagain. The non-letter SQL syntax (apostrophe, spaces, digits, dashes) is identical before and after rotation, so the injection structure is never disturbed.If you want a literal username match instead, the same logic applies:
admin'--rotated isnqzva'--, which the server turns back intoadmin'--.Step 3Submit the payload and get the flag
ObservationWith the payload pre-rotated, the debug output shows the server reconstructing a valid OR-based injection. Sending it for real should authenticate and return the flag.Send the pre-rotated payload as the username. The server ROT13s it back into a valid SQL injection, the broken query authenticates you, and the flag is returned. You can leave debug on while testing to confirm the reconstructed query looks right.Learn more
This challenge shows that a transformation on input is not a security control: the injection is unchanged once you account for the rotation. Two lessons stack here - a hidden
debugparameter handed you the server-side behavior, and a reversible transform on input does nothing to stop injection. The real fix is parameterized queries regardless of any encoding applied to the input.
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{3v3n_m0r3_SQL_...}
ROT13-encode your SQL injection payload before submitting - the server ROT13-decodes it before placing it in the query.