Exploitation
Vulnerabilidade Web
Vamos falar sobre algumas vulnerabilidades Web, sendo elas: Local File Inclusion (LFI), Log Poisoning, Remote Code Execution (RCE), Remote File Inclusion (RFI) e SQL Injection (SQLi). Essas falhas representam riscos reais e frequentemente explorados em ambientes de produção, sendo fundamentais para qualquer profissional de segurança entender profundamente seu funcionamento, impacto e formas de exploração.
Começando pelo LFI, essa vulnerabilidade permite que um atacante acesse arquivos internos do servidor web apenas manipulando parâmetros de entrada na URL ou formulários. Isso pode incluir desde arquivos de configuração, como /etc/passwd
em sistemas Linux, até arquivos de log e scripts. O atacante, mesmo sem acesso direto ao sistema, pode obter informações sensíveis que auxiliam na escalada do ataque. É comum ver essa falha em sistemas que montam caminhos de arquivos dinamicamente a partir da entrada do usuário, sem a devida sanitização.
A partir do LFI, é possível executar uma técnica chamada Log Poisoning. Aqui, a ideia é inserir um payload malicioso (geralmente um código PHP) diretamente nos arquivos de log do servidor, que costumam registrar cada requisição recebida. Quando o atacante faz com que o sistema leia esse log por meio do LFI, o código malicioso é interpretado e executado pelo servidor. Essa técnica é especialmente poderosa porque transforma uma vulnerabilidade de leitura (como o LFI) em uma execução remota de código.
Isso nos leva ao RCE, uma das vulnerabilidades mais críticas e perigosas em ambientes web. O RCE permite que um atacante execute comandos arbitrários no servidor afetado. Quando combinado com técnicas como Log Poisoning ou falhas de upload, o impacto é ainda maior, podendo resultar em controle total da máquina, extração de dados, pivô para outras redes internas e muito mais.
Já o RFI é uma vulnerabilidade semelhante ao LFI, mas com um agravante: em vez de acessar apenas arquivos locais, o atacante consegue fazer com que o servidor inclua arquivos externos. Isso significa que o atacante pode hospedar um script malicioso em um servidor próprio e forçar o servidor vulnerável a acessá-lo e executá-lo. É uma porta escancarada para a injeção de backdoors, shells remotos e outras ameaças.
Por fim, temos a SQL Injection, talvez a mais famosa das vulnerabilidades web. Ela ocorre quando os dados fornecidos pelo usuário são incorporados diretamente a uma consulta SQL sem a devida validação. Um atacante pode, com isso, manipular a lógica da consulta e acessar, alterar ou apagar dados do banco de dados. Em casos mais graves, pode inclusive conseguir executar comandos no sistema operacional via banco, dependendo do nível de permissões configurado.
Cada uma dessas vulnerabilidades demonstra como a combinação de entradas mal validadas, configurações inseguras e falhas na lógica de aplicações pode abrir portas para invasores. Entender essas falhas em detalhes é um passo crucial para qualquer profissional que deseje atuar com segurança ofensiva ou defensiva no mundo web.
Identificar SQL Injection (SQLi)
Para identificar e explorar campos vulneráveis a SQL Injection (SQLi), o primeiro passo é compreender como a aplicação interage com o banco de dados por meio das entradas do usuário. Normalmente, esses campos estão presentes em formulários de login, busca, filtros ou parâmetros na URL que resultam em consultas ao banco de dados. O objetivo inicial é testar se esses pontos de entrada são diretamente incorporados nas instruções SQL sem sanitização adequada.
A identificação pode começar de forma simples, utilizando valores como ' OR '1'='1
, que é um dos testes mais básicos. Se ao inserir esse valor em um campo de login, por exemplo, o sistema concede acesso sem necessidade de autenticação válida, isso indica que o campo está vulnerável. Outro indicativo é quando o sistema retorna erros de banco de dados ao inserir caracteres especiais como aspas simples ('
), o que sugere que a entrada do usuário está sendo interpretada diretamente na consulta SQL.
Esses comandos podem ser usados tanto na URL quanto nos campos de entrada da aplicação, como login, busca, filtros ou formulários em geral.
Uma vez confirmada a vulnerabilidade, o passo seguinte é explorar essa falha para extrair informações ou manipular os dados. A técnica consiste em entender como a consulta SQL é formada e adaptar os payloads de acordo. Por exemplo, usando operadores como UNION SELECT
é possível combinar resultados de várias tabelas, enquanto funções como VERSION()
ou DATABASE()
ajudam a descobrir o tipo de SGBD e informações sobre o ambiente.
Se a aplicação for minimamente protegida, o pentester pode aplicar técnicas de SQLi às cegas (Blind SQLi), nas quais ele analisa o comportamento da aplicação com base na resposta recebida, mesmo que não haja retorno visível do banco. Isso pode ser feito com base no tempo de resposta (Time-Based Blind) ou em respostas booleanas (True/False).
Ferramentas como sqlmap podem automatizar tanto a identificação quanto a exploração de SQLi, mas é essencial que o profissional entenda o processo manualmente para interpretar os resultados corretamente, adaptar os ataques ao contexto e evitar falsos positivos.
Além do clássico ' OR '1'='1
, existem vários outros comandos e técnicas que você pode usar para identificar vulnerabilidades de SQL Injection, dependendo do tipo de banco de dados e da forma como os dados são tratados pela aplicação. Aqui vão alguns exemplos práticos que ajudam a descobrir se o campo é vulnerável:
Uma abordagem básica é começar com entradas como '
, "
, --
, #
, ;
, entre outros. Esses caracteres são comuns em comandos SQL e, se causarem erro ou mudança no comportamento da aplicação, indicam que a entrada pode estar sendo interpretada diretamente no SQL.
Para verificar se a aplicação está concatenando os valores diretamente na consulta, tente variações como:
' OR 1=1--
" OR "a"="a
' OR 'x'='x
1' OR '1'='1
Essas expressões buscam quebrar a lógica da consulta e retornar todos os registros, ou permitir o login sem credenciais válidas. Você também pode testar o comportamento do banco de dados com comandos que não retornam nada, mas indicam que houve execução da instrução, como:
1 AND 1=1
(deve retornar normal)1 AND 1=2
(deve retornar vazio ou erro)
Se a diferença de comportamento entre esses dois comandos for perceptível, é um forte indício de SQLi. Em casos de Blind SQLi, use instruções que envolvem tempo para avaliar se a resposta da aplicação muda com base na lógica executada no banco. Por exemplo, em MySQL:
' OR IF(1=1, SLEEP(5), 0)--
1' AND SLEEP(5)--
Se a resposta demorar mais tempo do que o normal, isso mostra que o banco está executando o comando e é vulnerável. Por fim, ORDER BY
pode ser usado para descobrir o número de colunas na tabela:
ORDER BY 1--
ORDER BY 2--
ORDER BY 3--
... até que a aplicação retorne erro, indicando que você passou do número de colunas disponíveis.
Todos esses testes devem ser feitos com atenção, idealmente em ambientes de teste ou com autorização, já que envolvem simulação de exploração real. O objetivo é entender como a aplicação responde às manipulações e, com isso, confirmar a presença e a extensão da vulnerabilidade.
Outros comandos:
# Identificar o usuário que está executando o SQL:
' union select system user(),user()#
# Identificar o nome do banco:
' union select null, database()#
# Para obter os nomes de todas as bases de dados existentes na aplicação MySQL:
' union select null, schema_name from information_schema.schemata#
# Obter as tabelas do próprio information_schema, bastando modificar o final da injeção:
' union select null, table_name from information_schema.tables#
# Obter os usernames e senhas:
' union select null, concat(first_name,0x0a,password) from users#
Se a aplicação usa parâmetros via GET (ou seja, na URL), os testes podem ser feitos diretamente nela. Por exemplo:
http://site.com/produto.php?id=1
Você poderia modificar para:
http://site.com/produto.php?id=1' OR '1'='1
ou
http://site.com/produto.php?id=1 AND SLEEP(5)--
Essas variações ajudam a perceber se o parâmetro id
está sendo injetado diretamente na consulta SQL. Nos campos de login, como usuário e senha, você testa inserindo os comandos diretamente nos inputs do formulário. Um exemplo comum seria:
- Usuário:
' OR '1'='1
- Senha:
' OR '1'='1
Ou mesmo deixar o campo senha em branco e tentar:
- Usuário:
admin'--
- Senha: (em branco)
Se o login for aceito, é um forte indicativo de vulnerabilidade SQLi.
O importante é testar os pontos onde a aplicação interage com o banco de dados usando dados fornecidos pelo usuário. Tanto faz se essa interação vem da URL ou de campos preenchidos no site — o alvo é identificar onde o dado entra sem validação e vai direto para uma consulta SQL.
SQLMAP
O uso do sqlmap segue uma sequência prática de etapas para explorar uma vulnerabilidade de SQL Injection de forma automatizada. Primeiro, é necessário identificar o parâmetro vulnerável, suponhamos que esse seja o id
. No momento da autenticação, vamos interceptar a requisição com o proxy do Burpsuite, com a requisição capturada, vamos clicar com o botão direito do mouse na tela
da captura da requisição e copiá-la (isso copia especialmente os dados do formulário e os cookies da sessão).
Com essas informações em mãos, utiliza-se o sqlmap com a seguinte estrutura básica de comando:
sqlmap -u "http://alvo.com/caminho" --cookie="PHPSESSID=abc123; security=low" --data="id=1&Submit=Submit" --dbs
Esse comando identifica se há vulnerabilidade no parâmetro id
e, em caso positivo, lista os bancos de dados disponíveis. Após isso, você pode continuar com:
sqlmap -u "http://alvo.com/caminho" --cookie="PHPSESSID=abc123; security=low" --data="id=1&Submit=Submit" -D nome_do_banco --tables
Esse comando exibe as tabelas disponíveis no banco. Em seguida, para listar as colunas de uma tabela específica:
sqlmap -u "http://alvo.com/caminho" --cookie="PHPSESSID=abc123; security=low" --data="id=1&Submit=Submit" -D nome_do_banco -T nome_da_tabela --columns
Por fim, para extrair dados de colunas específicas como usuários e senhas:
sqlmap -u "http://alvo.com/caminho" --cookie="PHPSESSID=abc123; security=low" --data="id=1&Submit=Submit" -D nome_do_banco -T nome_da_tabela -C user,password --dump
Com esses comandos, o sqlmap automatiza toda a lógica de exploração SQLi, identificando, explorando e extraindo dados da aplicação web vulnerável de forma eficiente e sistemática.
Code Injection
O Code Injection e Remote Code Execution (RCE) estão intimamente ligados, mas não são exatamente a mesma coisa. O Code Injection é a vulnerabilidade que permite a injeção de código malicioso em uma aplicação. Quando essa injeção resulta na execução remota de comandos (como quando o código é processado e executado pelo servidor), temos um cenário de RCE.
Ou seja, o RCE é uma consequência possível (e grave) da exploração de uma vulnerabilidade de Code Injection. Quando se fala em PHP Code Injection, e o código injetado é algo como system('ls')
ou shell_exec('id')
, o que se está fazendo é, de fato, executar código remotamente. Então, nesse contexto, o Code Injection se manifesta como uma forma de RCE.
A distinção técnica está no vetor: Code Injection descreve a falha de segurança que permite a entrada de código não autorizado, já o RCE descreve o efeito prático quando esse código é executado no servidor remoto. Em resumo: toda RCE por code injection começa com uma code injection, mas nem toda code injection necessariamente resulta em RCE, isso depende da superfície de ataque e do ambiente de execução. Portanto, nem toda injeção de código leva automaticamente à execução do código injetado.
A exploração começa com a identificação de um campo vulnerável, geralmente um parâmetro GET ou POST que aceita dados do usuário sem validação. Ao injetar um payload como '; system('ls'); //
ou <?= shell_exec($_GET['cmd']); ?>
, o atacante pode fazer o servidor executar comandos arbitrários.
A vulnerabilidade de Code Injection não se limita apenas à linguagem PHP, pode ocorrer em qualquer linguagem ou sistema que permita a execução dinâmica de código e que falhe na validação da entrada do usuário. Porém, por ser muito comum em aplicações PHP mal configuradas, é um dos cenários mais explorados em ambientes de teste e didáticos.
Se uma aplicação foi desenvolvida em PHP, o servidor só entende e processa código PHP, ou seja, se você tentar injetar código em Python, C ou qualquer outra linguagem, não vai funcionar, porque o servidor vai simplesmente ignorar, ou tratar como texto comum, já que ele não entende.
Imagine um sistema de agendamento de consultas médicas desenvolvido em PHP. Se esse sistema tiver uma falha de segurança em algum ponto onde os dados inseridos pelo usuário são processados com funções perigosas como eval()
, você só conseguirá explorar essa falha injetando comandos PHP. Por exemplo:
eval($_GET['cmd']);
Se esse código estiver presente, e o atacante enviar algo como:
?cmd=phpinfo();
O servidor executa esse comando como código PHP, porque a função eval()
interpreta o valor da string como se fosse uma instrução PHP.
Essa função eval()
é especialmente perigosa porque transforma qualquer string em código executável. Por isso, é um dos alvos principais em análises de segurança. O problema não está apenas nela, mas em qualquer ponto onde dados externos são usados para formar ou controlar trechos de código. O mesmo risco se aplica a funções como preg_replace()
(quando usada com modificador e
), create_function()
ou assert()
.
O mesmo raciocínio vale para aplicações feitas em outras linguagens. Se o sistema for feito em ASPX (ASP.NET), ele só aceitará injeção de código na sintaxe dessa linguagem. Logo, para explorar a vulnerabilidade, você precisa conhecer como a linguagem da aplicação interpreta e executa o código.
O uso da função eval()
por si só não é necessariamente um problema. Ela é uma função legítima da linguagem PHP, com um propósito claro: interpretar e executar dinamicamente trechos de código PHP passados como string. Em certos contextos controlados e fechados, eval()
pode até ser útil, por exemplo, para interpretar expressões internas de um sistema ou manipular lógica configurável dentro de parâmetros bem definidos.
O problema começa quando o conteúdo passado para o eval()
vem de fora, ou seja, quando ele depende de entrada do usuário, e principalmente quando essa entrada não é validada. Nesse cenário, você dá ao usuário final o poder de injetar qualquer instrução PHP, o que pode resultar em execução de comandos no servidor, acesso a arquivos confidenciais, modificação de banco de dados ou até abertura de backdoors.
Um exemplo inofensivo de uso de eval()
seria algo como:
$codigo = 'echo 2 + 2;';
eval($codigo);
Aqui, o conteúdo é definido internamente no código. Nada de errado. Agora, compare com isso:
$codigo = $_GET['expressao'];
eval($codigo);
Neste caso, qualquer pessoa pode acessar a aplicação com algo como:
?expressao=phpinfo();
E o servidor irá executar essa função. Pior ainda, o usuário poderia enviar:
?expressao=system('rm -rf /');
O que, em um servidor mal configurado, pode ter consequências catastróficas. Portanto, o uso de eval()
não é inseguro por definição. O problema aparece quando você entrega o controle do que será executado a alguém de fora do sistema — ou seja, quando há entrada externa (usuário, parâmetros de URL, formulários, cookies etc.) sendo passada diretamente para a função. É aí que a injeção de código se torna viável.
A regra prática é, se você conseguir evitar o uso do eval()
, melhor. Se tiver que usar, nunca deixe que dados externos entrem nela sem validação rígida e controle total do que pode ou não ser executado.
Quebra de Autenticação
A quebra de autenticação é uma vulnerabilidade que permite a um invasor acessar sistemas como se fosse um usuário legítimo, sem possuir as credenciais apropriadas. Ela ocorre quando os mecanismos que deveriam proteger o processo de login, como senhas, sessões e autenticações multifator, são implementados de forma deficiente.
Existem diversas formas pelas quais essa falha pode se manifestar. Um exemplo clássico é permitir o uso de senhas fracas, previsíveis ou até mesmo padrão, como "admin" ou "123456". Outro ponto comum é permitir ataques automatizados, como força bruta ou "credential stuffing", em que o invasor testa diversas combinações de usuários e senhas até encontrar uma válida.
Além disso, um sistema vulnerável pode armazenar senhas de forma insegura, como em texto simples ou usando algoritmos de hash fracos (MD5, por exemplo), facilitando sua recuperação caso o banco de dados seja acessado. A ausência de autenticação multifator ou a exposição de identificadores de sessão (como tokens em URLs) também são exemplos de falhas que abrem caminho para a quebra de autenticação.
Outra falha recorrente é não invalidar corretamente os tokens ou IDs de sessão após o logout ou após um tempo de inatividade. Isso permite que uma sessão antiga continue válida e possa ser reutilizada por um atacante, caso ele tenha acesso ao token.
Na prática, um pentester pode explorar essa vulnerabilidade com ferramentas como Hydra ou BurpSuite, testando diferentes combinações de login em formulários web, interceptando requisições e manipulando sessões, sempre em busca de brechas que permitam acesso indevido ao sistema. Quando o sistema não responde com bloqueios, delays ou captchas, essas tentativas tendem a ser muito mais eficazes.
Brute Force
Brute Force é uma técnica de ataque que consiste em tentar repetidamente diferentes combinações de nome de usuário e senha até encontrar uma combinação válida. É um método direto e exaustivo, geralmente automatizado, que pode ser potencializado com o uso de wordlists, listas de senhas previamente compiladas. Para mais detalhes sobre Wordlist e ferramentas que podemos usar, clicar aqui.
A quebra de autenticação é uma das falhas mais críticas em sistemas web e acontece quando o controle de acesso a contas é mal implementado, permitindo que atacantes acessem áreas protegidas sem autorização. Isso pode ocorrer por uso de senhas fracas, ausência de autenticação multifator, armazenamento inseguro de credenciais ou falhas no gerenciamento de sessões.
Para explorar essa vulnerabilidade, utiliza-se a técnica de força bruta, onde combinações de usuário e senha são testadas até encontrar uma válida. Neste contexto, é aplicada a ferramenta Hydra, amplamente usada para automatizar esse tipo de ataque. Também são utilizados recursos do BurpSuite, que permite interceptar e manipular requisições HTTP, facilitando a identificação de brechas nos mecanismos de autenticação.
Antes de iniciar um ataque com o Hydra, é essencial ter uma wordlist, pois ela define as combinações de senhas que serão testadas durante o processo de força bruta. Quanto mais bem construída for essa lista, maiores são as chances de sucesso na invasão. Existem diversas ferramentas que podemos usar para criar nossas wordlists.
Entre as mais conhecidas estão o crunch, que permite criar listas personalizadas com base em padrões definidos pelo usuário. O CeWL, que gera wordlists a partir de palavras coletadas em sites. E o cupp, que cria listas com base em informações específicas da vítima, como nomes, datas e gostos pessoais. Essas ferramentas tornam o ataque mais direcionado e eficaz.
Gerar wordlist
Para criar uma wordlist com cada uma dessas ferramentas, você pode usar os seguintes comandos básicos no terminal do Kali Linux:
O crunch
gera combinações com base em um padrão de tamanho e conjunto de caracteres. Por exemplo, para criar uma lista com todas as combinações de 4 a 6 caracteres contendo apenas letras minúsculas:
crunch 4 6 abcdefghijklmnopqrstuvwxyz -o wordlist_crunch.txt
O comando acima gera todas as combinações possíveis com 4 a 6 letras minúsculas e salva em um arquivo chamado
wordlist_crunch.txt
.
O CeWL
coleta palavras de um site e gera uma lista baseada no conteúdo. Por exemplo:
cewl http://exemplo.com -w wordlist_cewl.txt
O comando acima acessa o site indicado, extrai palavras e cria uma wordlist com elas no arquivo
wordlist_cewl.txt
.
O cupp
é interativo e gera senhas baseadas em informações pessoais. Você pode iniciar assim:
cupp -i
Depois é só responder as perguntas (nome, sobrenome, data de nascimento etc.). Ao final, ele cria uma lista personalizada com base nas informações fornecidas.
Extração e Análise de Cookies de Sessão para Geração de Wordlists
Durante testes de quebra de autenticação, uma etapa estratégica é a análise dos cookies de sessão gerados após um login bem-sucedido. Cookies são arquivos enviados pelo servidor ao navegador para manter a sessão do usuário, e muitas vezes carregam identificadores ou padrões que podem ser explorados em ataques.
Para capturar esse cookie, é necessário abrir as ferramentas de desenvolvedor do navegador (F12 no Firefox ou Google Chrome), acessar a aba Rede (Network), e executar o login na aplicação. Em seguida, localize na lista de requisições a entrada correspondente ao recurso ou funcionalidade vulnerável, por exemplo, a rota usada para autenticação. Ao selecionar essa requisição, na aba Cabeçalhos (Headers), você encontrará o campo Cookie com o conteúdo transmitido.
Essa informação pode revelar tokens de sessão previsíveis, identificadores de usuários, ou padrões que ajudem a criar uma wordlist mais efetiva e direcionada. Caso o valor do cookie mude de forma padronizada com cada novo login, por exemplo, é possível gerar variações com ferramentas como crunch
, cupp
ou CeWL
, e usar essas variações em ataques de força bruta com ferramentas como Hydra. Essa abordagem aumenta significativamente a precisão e a eficiência da tentativa de quebra de autenticação.
Você pode se perguntar, caso você tenha o login e senha, porque iria fazer isso? De fato, se o objetivo for apenas capturar um cookie de uma sessão válida, o login precisa ter sido bem-sucedido. Porém, o valor está justamente no que pode ser aprendido com esse cookie, mesmo que já se tenha uma conta de teste.
Ao capturar o cookie de uma sessão autenticada (por exemplo, de um usuário padrão que você criou ou de uma conta de teste), você pode estudar a estrutura desse cookie e identificar padrões que ajudem a explorar o sistema. Em muitos sistemas mal projetados, os cookies seguem estruturas previsíveis: podem conter IDs sequenciais, codificações fracas (como base64), nomes de usuários, ou mesmo tokens de autenticação fáceis de forjar.
O valor disso está em aplicar o conhecimento para tentar gerar cookies válidos para outros usuários, principalmente administrativos, ou até para conduzir um ataque de sequestro de sessão (session hijacking). Nesse caso, a wordlist gerada a partir do padrão do cookie não serve para força bruta de senha, mas sim para força bruta de sessão.
Sabendo qual é o Cookie, podemos executar o seguinte:
$ cewl -H 'Cookie: MoodleSession=489115u4452jpral43mgcu3r72; _gcl_au=1.1.1520017614.1748389752; _ga_HGVX4493F8=GS2.1.s1748389752$o1$g1$t1748389763$j49$l0$h0; _ga=GA1.1.1438644974.1748389752; MOODLEID1_=%25F1%25F8%25C7%257Ch%253A%250D%259E; sesskey=mtZ4QmyJF4' -d 1 -w wordlist_bruteforce.txt http://192.168.1.2/login.php
Agora que já criamos nossa wordlist, podemos utilizar a ferramenta Hydra para realizar um ataque de força bruta. Para isso, seguiremos a mesma lógica apresentada anteriormente: usaremos as ferramentas de desenvolvedor do navegador para coletar as informações necessárias que permitirão configurar corretamente o ataque.
Ao acessar a página de login e preencher os campos, a requisição de autenticação é enviada ao servidor. Nesse momento, devemos ir até a aba "Network" das ferramentas de desenvolvedor (F12) e identificar a requisição do tipo POST responsável pelo envio das credenciais. É nessa requisição que encontraremos dados importantes como a URL do endpoint, os parâmetros de usuário e senha, e o formato exato que o Hydra precisará replicar para executar as tentativas com nossa wordlist.
Para montar corretamente o comando do Hydra, é necessário reunir algumas informações específicas da aplicação-alvo e entender como a ferramenta funciona:
É fundamental identificar qual é a URL responsável por processar a autenticação, ou seja, o endpoint para onde a requisição de login é enviada. Depois, usamos no Hydra duas variáveis especiais: ^USER^
e ^PASS^
, que representam os pontos onde ele irá substituir os valores da wordlist de usuários e senhas durante o ataque.
Também é preciso observar qual mensagem o sistema retorna quando as credenciais estão erradas, essa resposta será usada para identificar tentativas mal-sucedidas. O comando do Hydra utiliza dois-pontos (:
) na opção -m
para separar a URL do endpoint, os parâmetros da requisição POST e a mensagem de erro esperada.
As vezes a aplicação exige uma sessão autenticada para permitir o envio da requisição de login, nesses casos será necessário adicionar manualmente os cookies de sessão no comando. Esses cookies devem ser extraídos da aba "Network" do navegador, conforme explicado anteriormente.
Com todas essas informações em mãos, o comando final do Hydra fica assim:
hydra 192.168.2.2 http-post-form -m "/login.php:login=^USER^&password=^PASS^&form=submit:H=Cookie: MoodleSession=489115u4452jpral43mgcu3r72; _gcl_au=1.1.1520017614.1748389752; _ga_HGVX4493F8=GS2.1.s1748389752$o1$g1$t1748389763$j49$l0$h0; _ga=GA1.1.1438644974.1748389752; MOODLEID1_=%25F1%25F8%25C7%257Ch%253A%250D%259E; sesskey=mtZ4QmyJF4:Invalid credentials! Did you forgot your password?" -l <USERNAME> -P ./wordlist_bruteforce_test.txt
Esse comando direciona o Hydra a testar logins usando o usuário <USERNAME>
e a wordlist fornecida, contra o endpoint especificado, considerando os cookies da sessão ativa e tratando a mensagem “Invalid credentials! Did you forgot your password?” como indicativo de falha.
Gerenciamento inseguro de cookies
O famoso Cookie é um pequeno arquivo de texto que um site armazena no navegador do usuário com o intuito de guardar informações como preferências, histórico de navegação ou dados de sessão, permitindo que o site "lembre" do usuário em visitas futuras.
O Gerenciamento inseguro de cookies é uma falha clássica de gerenciamento de sessão que ocorre quando o desenvolvedor opta por criar e controlar manualmente os cookies, em vez de utilizar os mecanismos nativos e seguros da linguagem, como no caso do PHP, a função session_start()
.
Ao criar cookies personalizados para armazenar informações de autenticação, como o nome do usuário ou o nível de acesso, sem nenhum tipo de validação segura no servidor, abre-se uma brecha crítica: o próprio usuário pode manipular esses valores diretamente no navegador. Isso significa que ele pode, por exemplo, editar o cookie e se passar por outro usuário ou até por um administrador, caso saiba ou consiga adivinhar os valores esperados.
Na prática, se a aplicação verifica algo como if ($_COOKIE['user'] == 'admin')
para conceder privilégios, e esse valor pode ser alterado livremente no lado do cliente, a segurança do sistema está completamente comprometida. Esse tipo de falha permite uma escalada direta de privilégios com zero esforço, bastando ao invasor alterar o valor do cookie manualmente, por exemplo com o uso de extensões como "EditThisCookie" ou diretamente nas ferramentas de desenvolvedor do navegador.
Esse comportamento demonstra como é essencial delegar o gerenciamento de sessões ao servidor, utilizando funções como session_start()
em PHP, que gera identificadores únicos e armazena as informações sensíveis em memória no lado do servidor, longe do controle do usuário. Isso evita manipulação direta e torna a sessão significativamente mais segura.
As flags HTTPOnly
e Secure
são configurações de segurança aplicadas aos cookies, especialmente importantes para cookies de sessão. O problema surge quando essas flags não são corretamente configuradas.
A flag HTTPOnly
impede que o cookie seja acessado via JavaScript, o que ajuda a proteger contra ataques do tipo Cross-Site Scripting (XSS). Sem essa proteção, um invasor que consiga injetar código JavaScript em uma página pode roubar o cookie de sessão do usuário.
Já a flag Secure
garante que o cookie só será transmitido por conexões HTTPS, ou seja, criptografadas. Sem essa flag, o cookie pode ser enviado também por conexões HTTP, que são inseguras. Isso facilita ataques como o man-in-the-middle, onde o atacante intercepta a comunicação para capturar o cookie.
Exposição de Dados Sensíveis e Configurações
A exposição de dados sensíveis e configurações, é uma das vulnerabilidades mais críticas no contexto da segurança de aplicações web. Ela acontece quando informações que deveriam ser restritas, como senhas, chaves de API, dados pessoais ou arquivos de configuração, ficam acessíveis de forma não intencional para usuários ou agentes externos.
Na prática, isso pode ocorrer por vários motivos, como: má configuração de servidores, permissões mal definidas, ausência de controle de acesso eficaz ou mesmo negligência na manipulação de arquivos sensíveis. Um exemplo comum é deixar diretórios ou arquivos de configuração acessíveis diretamente via navegador, como arquivos .env
, config.php
, web.config
ou até backups .zip
em diretórios públicos.
Outro vetor frequente é o código-fonte mal escrito, que pode revelar dados em mensagens de erro, comentários HTML ou até variáveis no lado do cliente. Além disso, aplicações que armazenam informações sensíveis em cookies, sem as devidas flags de segurança (HTTPOnly
, Secure
, SameSite
), também se tornam alvos fáceis.
A OWASP lista a exposição de dados sensíveis como um dos principais riscos porque essas falhas normalmente resultam em comprometimento direto de contas, sistemas e até da infraestrutura. O impacto pode incluir roubo de identidade, acesso não autorizado a sistemas internos e vazamento de dados que comprometem a reputação da empresa.
Para mitigar esse tipo de vulnerabilidade, é essencial aplicar boas práticas como: armazenar informações sensíveis de forma criptografada (nunca em texto plano), definir corretamente permissões em arquivos e diretórios, não expor comentários ou trechos de código no HTML enviado ao cliente, configurar corretamente o servidor web para evitar listagem de diretórios e aplicar validações rigorosas nos pontos de entrada e saída de dados da aplicação.
Em termos técnicos, ferramentas como o Feroxbuster ajudam a identificar arquivos e diretórios expostos que não deveriam estar acessíveis. O uso de interceptadores como o Burp Suite permite inspecionar as requisições e respostas HTTP, revelando possíveis exposições de dados em headers, parâmetros ou corpo da resposta.
Quebra de Controle de Acesso
A quebra de controle de acesso, representa uma das categorias mais perigosas de falhas em aplicações web. Essa vulnerabilidade ocorre quando um sistema permite que usuários acessem dados ou funcionalidades que deveriam estar fora do seu alcance. Diferente de falhas de autenticação, que envolvem provar quem você é, as falhas de controle de acesso dizem respeito ao que você pode fazer ou ver depois de autenticado.
Esse tipo de falha costuma surgir por conta de lógica de autorização mal implementada, checagens inconsistentes no backend, ou uso incorreto de tokens de sessão. Na prática, é comum ver isso em URLs manipuláveis, parâmetros de requisição que controlam permissões (como IDs de usuário), ou até mesmo cookies com informações sensíveis que podem ser alteradas.
Por exemplo, se um sistema identifica o usuário pelo parâmetro id=5
na URL, e permite que ele altere para id=6
e acesse dados de outro usuário sem validação, temos uma quebra clara de controle de acesso. O mesmo vale quando um cookie traz informações codificadas em Base64 (ao invés de criptografadas) permitindo que um atacante as leia, modifique e assuma outro perfil.
Existe também o JWT (JSON Web Token), onde o token de autenticação carrega dados sobre o usuário. Se esse token não for assinado corretamente, ou se o sistema aceitar tokens manipulados, o atacante pode modificar o campo de identificação de usuário e obter acesso não autorizado, inclusive como administrador.
É importante entender que essa categoria inclui várias falhas conhecidas: acesso direto a URLs protegidas, escalonamento de privilégios, bypass de autenticação via parâmetros ou headers, e uso indevido de tokens ou sessões expiradas.
Para mitigar esses riscos, boas práticas incluem verificar sempre no backend se o usuário tem permissão para acessar os recursos solicitados, não confiar em dados vindos do cliente, adotar controle rigoroso de sessões, e aplicar o princípio do menor privilégio, onde cada usuário deve ter acesso apenas ao que realmente precisa.
Além disso, frameworks modernos geralmente oferecem mecanismos de controle de acesso mais robustos. Mas mesmo assim, a responsabilidade final de definir e validar as permissões é do desenvolvedor, que deve entender o funcionamento de cada parte do sistema e prever tentativas de manipulação.
Existem diversos tipos de ataques de quebra de controle de acesso, mas todos eles possuem um único objetivo em comum, permitir que os invasores contornem os controles normais de segurança existentes. Alguns dos exemplos mais comuns desse tipo de ataque são:
Ataques de força bruta
Ocorrem quando o atacante tenta repetidamente adivinhar credenciais de acesso, como nome de usuário e senha. Isso pode ser feito manualmente ou com ferramentas automatizadas, como Hydra ou Burp Suite Intruder. O sistema vulnerável não impõe limites de tentativas, não usa captcha, nem aplica delay entre as tentativas, o que facilita o ataque. Esse tipo de falha é extremamente comum em páginas de login mal protegidas.Sequestro de sessão
Neste cenário, o atacante rouba o identificador de sessão de um usuário legítimo, geralmente por meio de um XSS, acesso físico ao navegador, ou captura em tráfego inseguro (HTTP). Com esse ID em mãos, o atacante se passa pelo usuário sem precisar saber suas credenciais. Se o sistema não usa corretamente as flagsSecure
eHTTPOnly
nos cookies, e não faz verificação de IP ou fingerprint do navegador, esse tipo de ataque se torna muito fácil.Man-in-the-Middle (MitM)
Aqui, o atacante intercepta a comunicação entre cliente e servidor, podendo visualizar, alterar ou redirecionar os dados transmitidos. Em contextos onde TLS não está habilitado corretamente, especialmente em servidores que ainda usam HTTP puro ou não forçam HTTPS, esse ataque permite desde roubo de senhas até injeção de comandos. Pode ocorrer em redes públicas ou mal configuradas.Ataques de repetição (Replay)
Esse ataque acontece quando um invasor captura uma requisição legítima, como uma tentativa de login bem-sucedida ou uma solicitação de API, e a reenvia depois para obter o mesmo efeito. Se o sistema não tiver algum mecanismo como nonce, tokens temporários ou controle de tempo e estado, ele pode aceitar a requisição repetida como se fosse válida, permitindo acesso indevido.Elevação de privilégios
Esse ataque é um dos mais graves. Ocorre quando um usuário comum consegue acessar funcionalidades ou dados restritos a usuários com nível mais alto, como administradores. Pode acontecer por falhas na lógica de permissões, parâmetros não validados ou manipulação de tokens (como JWTs com o campo de “role” alterado). A elevação pode ser horizontal (acessar dados de outros usuários) ou vertical (ganhar poderes administrativos).
Todos esses ataques mostram que não basta autenticar o usuário uma única vez, é necessário validar constantemente o que ele está tentando fazer, manter a sessão protegida e aplicar boas práticas como limitação de tentativas, tokens com validade curta, e verificação de origem das requisições. São falhas sutis, mas com impacto muito alto.
Identificar vulnerabilidade de Quebra de Controle de Acesso
Para identificar vulnerabilidades de quebra de controle de acesso, é essencial pensar como um atacante e testar ativamente os limites do sistema, ou seja, tentar acessar recursos que teoricamente não deveriam estar disponíveis para o perfil logado. A prática exige observação cuidadosa da lógica da aplicação, inspeção de requisições e conhecimento das permissões esperadas para cada funcionalidade. Aqui vai um guia mais detalhado e didático sobre como fazer isso:
O primeiro passo é autenticar-se no sistema com um perfil comum, de preferência o mais limitado possível. A partir daí, comece a observar URLs acessadas, parâmetros usados, tokens ou cookies atribuídos. Ferramentas como Burp Suite são fundamentais: elas permitem interceptar e modificar requisições em tempo real.
Um teste básico é modificar identificadores de recursos. Por exemplo, se a URL acessada for /perfil/usuario?id=123
, tente mudar o ID para outros valores e veja se os dados retornam. Se você conseguir acessar informações de outros usuários, mesmo sem permissão, isso é uma falha de controle de acesso horizontal.
Você também pode capturar uma requisição feita por um usuário autorizado (em um ambiente de teste com outro perfil) e tentar reproduzi-la com a sessão do usuário comum. Se a aplicação não validar corretamente a permissão e aceitar a ação, isso revela uma falha vertical. É assim que se verifica, por exemplo, se um usuário comum consegue acessar um endpoint de administração.
Outros pontos importantes são os cookies e tokens JWT. Verifique se esses dados podem ser manipulados, especialmente se estiverem apenas codificados (como em Base64) e não assinados. Se for possível trocar valores manualmente, como um ID ou nível de acesso, e o sistema aceitar sem validação, é um problema grave.
Vale também forçar ações que não estão visíveis na interface, como acessar diretamente um endpoint de exclusão ou modificação de dados. Muitas aplicações escondem botões da interface de usuários sem permissão, mas não bloqueiam a funcionalidade no backend.
Não confie em menus, botões ou restrições visuais. A segurança real deve estar no servidor. Um teste de quebra de controle de acesso bem feito envolve alterar todos os elementos que determinam acesso, como: parâmetros GET e POST, headers, cookies, campos ocultos de formulários, e qualquer dado que indique a identidade ou permissões do usuário.
Por fim, registre cada comportamento anômalo. Se o sistema não retorna erro ou mesmo responde com dados indevidos, isso já é indicativo de falha. O ideal é fazer esses testes com diferentes perfis de acesso e em funcionalidades sensíveis, como consulta, edição, exclusão e operações administrativas.
Abaixo segue um "passo a passo" mais objetivo:
Teste de autenticação fraca: Tente logar com credenciais inválidas ou vazias; se o sistema aceitar, há falha na validação de login.
Teste de sessão ativa: Após logout, tente acessar áreas restritas. Se ainda funcionar, a sessão não está sendo encerrada corretamente.
Teste de permissões: Após o login, tente acessar funcionalidades que não deveria ver. Se conseguir, há falha na autorização.
Repetição de solicitações: Capture e repita requisições autenticadas de outro usuário. Se funcionar, o sistema não associa corretamente as sessões.
Brute force: Faça várias tentativas de login automatizadas. Se não houver limite ou resposta adequada, o sistema é vulnerável.
Teste de redefinição de senha: Verifique se o processo de recuperação é seguro e não pode ser explorado para assumir contas.
Teste de bloqueio de conta: Teste se é possível bloquear uma conta legítima por repetidas falhas de login, o que pode ser explorado.
Captura de tráfego: Use ferramentas como Wireshark para ver se dados de autenticação trafegam sem criptografia.
Revisão de código: Se tiver acesso, procure por ausência de verificação de senha, controle de sessão ou permissões.
Teste de redirecionamento: Tente forçar redirecionamentos para páginas restritas. Se o sistema não validar, pode ser burlado.
Teste de cookies: Veja se os cookies usados na autenticação têm as flags
HttpOnly
eSecure
; sem elas, são mais vulneráveis.Ferramentas automatizadas: Use Burp Suite ou OWASP ZAP para detectar falhas conhecidas em autenticação e sessão.
Cross- Site Scripting XSS
O Cross-Site Scripting (XSS) é uma das vulnerabilidades mais recorrentes em aplicações web, ela é abordada como uma porta de entrada para ataques de manipulação do navegador da vítima. O XSS permite que atacantes injetem scripts maliciosos em páginas legítimas, fazendo com que o código seja executado no navegador de outro usuário sem seu consentimento. O impacto vai desde roubo de cookies de sessão até a manipulação da interface e envio de dados sensíveis para servidores controlados pelo atacante.
Existem três tipos principais de XSS: refletido, armazenado e baseado em DOM. No XSS refletido, o script malicioso vem embutido em uma URL e é refletido pelo servidor na resposta, por exemplo, em mensagens de erro ou resultados de pesquisa. O usuário clica no link malicioso e, sem perceber, aciona o script.
O XSS armazenado é mais perigoso. Nesse caso, o código malicioso é salvo diretamente no banco de dados da aplicação, como num comentário de blog ou mensagem de fórum. Cada vez que outro usuário visualiza essa página, o script é executado automaticamente em seu navegador. Esse é o tipo de XSS mais explorado em ataques persistentes.
Já o XSS baseado em DOM ocorre quando o próprio código JavaScript da aplicação manipula diretamente o conteúdo da página com base em dados de entrada do usuário (como parâmetros da URL), sem realizar qualquer tipo de validação ou limpeza. Como ele acontece inteiramente no lado do cliente, muitas vezes passa despercebido em testes tradicionais.
O perigo do XSS está na confiança que o navegador tem no conteúdo vindo do próprio site. Quando o script malicioso é executado, ele roda no mesmo contexto de segurança do domínio original, o que significa que pode acessar cookies, tokens, sessões, e até interagir com a interface da aplicação. Isso permite que o atacante capture credenciais, simule ações em nome do usuário ou redirecione a vítima para sites falsos.
Para prevenir XSS, o caminho mais seguro é nunca confiar em dados fornecidos pelo usuário. Toda entrada deve ser validada e, principalmente, escapada corretamente ao ser inserida em HTML, atributos, ou scripts. Além disso, o uso de políticas como Content Security Policy (CSP) pode limitar o que pode ser executado no navegador, reduzindo o impacto caso um script seja injetado.
Outra prática essencial é desativar a execução de scripts em campos de entrada e remover qualquer tag potencialmente perigosa, como <script>
, <img onerror>
, ou até eventos como onclick
, onmouseover
etc. Ferramentas como Burp Suite ajudam a testar pontos vulneráveis enviando payloads simples, como <script>alert(1)</script>
, para ver se eles são refletidos e executados.
No contexto de um pentest, explorar XSS exige atenção ao comportamento da aplicação frente a entradas manipuladas, à forma como exibe essas entradas e ao tipo de conteúdo que pode ser injetado. Pequenos descuidos na filtragem ou na renderização do lado cliente podem abrir brechas enormes para ataques, mesmo em sistemas aparentemente robustos.
Identificar vulnerabilidades de Cross-Site Scripting (XSS)
Para identificar e testar vulnerabilidades de Cross-Site Scripting (XSS), é essencial compreender como essas falhas ocorrem e aplicar técnicas específicas para detectá-las. Abaixo podemos ver um guia básico de como identificar XSS:
1 - Identificação de Pontos de Entrada
Comece mapeando todos os pontos da aplicação que aceitam entrada do usuário, como campos de formulários, parâmetros de URL, cookies e cabeçalhos HTTP. Utilize ferramentas como o Burp Suite para interceptar e analisar as requisições e respostas entre o cliente e o servidor. Isso permitirá observar como os dados inseridos pelo usuário são processados e refletidos na aplicação.2 - Testes Manuais com Payloads Simples
Insira entradas benignas, como a palavra "teste", nos campos identificados para verificar se a aplicação reflete esses dados na resposta. Se a entrada for refletida, substitua-a por payloads simples de XSS, como<script>alert('XSS')</script>
, para testar se o código é executado no navegador. É importante adaptar o payload ao contexto em que a entrada é refletida, considerando se está dentro de tags HTML, atributos ou scripts.3 - Análise de Contexto e Escapamento de Caracteres
Examine como a aplicação trata os caracteres especiais na saída. Se os caracteres<
,>
,"
,'
e&
não forem devidamente escapados, a aplicação pode ser vulnerável a XSS. Utilize ferramentas de desenvolvimento do navegador, como o DevTools, para inspecionar o DOM e verificar a presença de scripts injetados.4 - Testes Automatizados com Ferramentas Especializadas
Empregue ferramentas automatizadas para ampliar a cobertura dos testes. O OWASP ZAP e o Burp Suite são amplamente utilizados para escanear aplicações em busca de vulnerabilidades XSS. Essas ferramentas automatizam o envio de diversos payloads e analisam as respostas para identificar possíveis falhas.5 - Exploração de XSS Armazenado e DOM-Based
Para XSS armazenado, insira payloads em campos que persistem dados, como comentários ou perfis de usuário, e verifique se o código é executado quando outros usuários acessam essas informações. No caso de XSS baseado em DOM, analise o código JavaScript da aplicação para identificar manipulações inseguras do DOM que possam levar à execução de scripts maliciosos.6 - Implementação de Medidas Preventivas
Além da identificação, é crucial implementar medidas para prevenir XSS:Validação e Sanitização de Entradas: Verifique e limpe todas as entradas do usuário, removendo ou escapando caracteres perigosos.
Escapamento de Saída: Ao exibir dados fornecidos pelo usuário, utilize funções de escapamento apropriadas para o contexto (HTML, JavaScript, URL, etc.).
Política de Segurança de Conteúdo (CSP): Implemente CSP para restringir as fontes de scripts e reduzir o risco de execução de código malicioso.
Uso de Frameworks Seguros: Utilize frameworks que oferecem proteção contra XSS por padrão, como React ou Angular, que escapam automaticamente as variáveis inseridas no DOM.
7 - Educação e Conscientização
Promova a conscientização sobre segurança entre os desenvolvedores e mantenha-se atualizado com as práticas recomendadas e as vulnerabilidades mais recentes. Participar de treinamentos e acompanhar recursos como o OWASP pode ser extremamente benéfico.
Ao seguir essas etapas, você estará melhor equipado para identificar, testar e mitigar vulnerabilidades de XSS em aplicações web, fortalecendo a segurança e protegendo os usuários contra possíveis ataques.
Deserialização Insegura
A desserialização é o processo de converter dados serializados, geralmente um fluxo de bytes ou uma string codificada, de volta para a estrutura de dados original, como um objeto em uma linguagem de programação. Esse processo é essencial em aplicações que armazenam ou transmitem objetos complexos entre sistemas.
A vulnerabilidade ocorre quando essa conversão é feita sem validar adequadamente os dados recebidos. Se um atacante conseguir manipular esses dados antes da desserialização, ele pode inserir um objeto malicioso. Isso pode resultar em execução de código arbitrário, elevação de privilégios ou controle remoto do sistema.
Na prática, a exploração pode permitir que um atacante se transforme em usuário administrador ou execute comandos diretamente no sistema alvo, dependendo da linguagem e do framework utilizados na aplicação vulnerável.
Em PHP, você pode usar a função serialize()
para transformar um array ou objeto em uma string. Por exemplo, um array ['user' => 'admin', 'level' => 10]
pode virar algo como a:2:{s:4:"user";s:5:"admin";s:5:"level";i:10;}
. Essa string pode ser armazenada em arquivos, bancos de dados ou cookies.
a
representa um array,s
representa uma string ei
representa um valor inteiro.
No Python, o módulo pickle
faz a serialização de objetos. Um dicionário como {"nome": "João", "idade": 30}
é convertido para um fluxo binário que pode ser salvo em arquivo ou transmitido. Existe também o json.dumps()
, que transforma o mesmo dicionário em {"nome": "João", "idade": 30}
, útil quando se quer interoperabilidade entre sistemas.
Em Java, qualquer objeto que implemente a interface Serializable
pode ser serializado com ObjectOutputStream
. Um objeto de uma classe Pessoa
com campos nome
e idade
vira um fluxo binário específico que pode ser salvo ou enviado pela rede.
A desserialização insegura acontece quando uma aplicação aceita dados serializados de fontes externas, como cookies, parâmetros de requisição ou arquivos, e os desserializa sem validar a integridade, autenticidade ou o conteúdo desses dados. Isso abre espaço para que um atacante envie um objeto malicioso, que será interpretado e executado pela aplicação, resultando em diferentes tipos de exploração.
Comando e Controle
Comando e Controle (ou simplesmente C2), é o mecanismo utilizado por atacantes para manter comunicação e controle remoto sobre máquinas comprometidas, geralmente em um ataque de malware ou em uma estrutura de botnet. A ideia é simples, uma vez que o atacante infecta uma máquina (ou várias), ele precisa de um canal confiável para enviar instruções e receber respostas dessas máquinas.
Esse canal de C2 pode ser baseado em vários protocolos, como: HTTP, HTTPS, DNS, IRC, entre outros. Esse canal vai servir como uma ponte entre o atacante (o servidor de C2) e os dispositivos infectados (os clientes ou "bots"). A partir do C2, o atacante pode, por exemplo, executar comandos remotamente, exfiltrar dados, instalar mais malwares, escalar privilégios ou até usar a máquina para atacar outras redes.
Na prática ofensiva, como em um exercício de Red Team, o C2 é usado para simular o comportamento de agentes maliciosos. É possível montar um ambiente onde o atacante (emulando um invasor real) distribui payloads que, ao serem executados nas máquinas-alvo, se conectam de volta ao servidor de C2, permitindo ações contínuas e coordenadas.
Ferramentas como Metasploit (com MSFConsole e MSFVenom), Cobalt Strike e outros são comumente usadas para construir e administrar essas infraestruturas de C2. Elas permitem configurar payloads para diferentes sistemas operacionais, automatizar tarefas e manter persistência dentro da rede comprometida.
Botnet
Uma botnet é uma rede de dispositivos comprometidos, chamados de bots ou zumbis, que são controlados remotamente por um atacante através de um servidor de Comando e Controle (C2). Esses dispositivos podem ser computadores pessoais, servidores, roteadores, dispositivos IoT e qualquer outro equipamento conectado à internet que tenha sido infectado.
O funcionamento básico é assim, o atacante distribui malwares que infectam os dispositivos e os fazem se conectar automaticamente ao servidor C2. A partir daí, ele pode controlar todos os bots de forma centralizada, sem que o usuário perceba. Isso cria uma infraestrutura poderosa e oculta para ataques em larga escala.
Botnets são usadas para diversas finalidades maliciosas. As mais comuns são ataques de negação de serviço (DDoS), envio massivo de spam, mineração de criptomoedas, roubo de dados, propagação de mais malwares e fraudes diversas. O impacto pode ser devastador, tanto em termos de danos operacionais quanto financeiros.
Na prática ofensiva (como em simulações de Red Team), a construção de uma botnet serve para mostrar como uma rede interna mal protegida pode ser dominada silenciosamente. Ferramentas como Metasploit permitem a criação de payloads que, quando executados nas máquinas vítimas, as conectam a um servidor C2, simulando exatamente esse cenário.
O grande desafio das botnets é sua capacidade de evasão. Muitas usam técnicas de criptografia, troca constante de IPs, comunicação por canais legítimos (como HTTPS) e até infraestrutura descentralizada (como redes P2P) para dificultar a detecção e o bloqueio. Isso torna a defesa uma tarefa complexa, exigindo monitoramento contínuo, análise de tráfego e resposta rápida a incidentes.
O botmaster é o operador ou controlador da botnet. É ele quem define os comandos, distribui os payloads maliciosos e decide o que os bots devem fazer, desde roubo de dados até ataques DDoS coordenados. Tradicionalmente, o botmaster se conecta a um servidor de Comando e Controle (C2) centralizado, que serve como o ponto de comunicação entre ele e os dispositivos zumbis.
Porém, as botnets modernas vêm utilizando arquiteturas distribuídas, como P2P (peer-to-peer) ou até mesmo infraestrutura em blockchain e serviços na nuvem. Isso torna muito mais difícil derrubar a rede, já que não há mais um único ponto de falha, como o servidor central. Em uma botnet distribuída, os próprios bots podem se comunicar entre si e repassar comandos, funcionando como nós autônomos dentro de uma rede resiliente.
Esse avanço arquitetural é proposital, ele visa dificultar a detecção, a interrupção do comando da rede e a rastreabilidade do botmaster. Para o Red Team, esse modelo distribuído representa um cenário mais realista e desafiador em simulações de ataque, enquanto para as equipes de defesa, exige estratégias mais robustas de detecção de comportamento e resposta a incidentes.
É possível transmitir dados através de protocolos como ICMP e DNS, o que é uma técnica comum em comunicações encobertas de uma botnet ou infraestrutura de Comando e Controle (C2). Esses protocolos são escolhidos justamente porque normalmente são permitidos por firewalls e filtros de rede, o que os torna ideais para evitar detecção.
No caso do ICMP, que é o protocolo usado, por exemplo, no comando ping
, o atacante pode injetar dados diretamente no campo payload de um pacote ICMP. Assim, o pacote aparentemente legítimo transporta comandos, arquivos ou partes de dados codificados. Do outro lado, o agente malicioso intercepta o pacote, extrai o conteúdo e processa a informação recebida.
Essa técnica é conhecida como ICMP tunneling, e pode usar ferramentas como icmpush
, ptunnel
, Icmpsh
e outras permitem montar canais de comunicação completos usando esse protocolo. O tráfego ICMP, sendo técnico e comum em redes corporativas, muitas vezes passa despercebido por sistemas de detecção, especialmente se for fragmentado ou disfarçado de tráfego normal.
Tipos de Conexões nas Botnets
As conexões bind e reverse são dois métodos distintos usados para estabelecer comunicação entre um sistema comprometido e um atacante, especialmente em contextos de exploração de vulnerabilidades, pentest ou infraestrutura de Comando e Controle (C2).
Na conexão bind, o payload faz com que a máquina alvo abra uma porta e fique escutando conexões. O atacante, então, se conecta a essa porta. Por exemplo, um shell bind no Windows pode abrir a porta 4444, e o atacante, usando netcat, se conecta diretamente a esse IP e porta. O problema desse método é que firewalls geralmente bloqueiam conexões de entrada, o que o torna menos eficaz em redes modernas.
Já na conexão reverse, o payload faz com que o sistema comprometido inicie uma conexão de saída de volta para o atacante. Nesse caso, o atacante espera com um listener, e o alvo "chama de volta" o IP e porta definidos. Essa técnica é muito mais comum e eficaz, porque firewalls e NATs geralmente permitem conexões de saída, como navegação web, facilitando a comunicação.
Em operações reais, especialmente em ambientes com restrições de rede, o reverse shell é o padrão preferido. Ele se adapta melhor às barreiras naturais das redes corporativas e se camufla mais facilmente no tráfego legítimo. Ferramentas como o Metasploit usam ambos os modelos, mas o reverse é quase sempre a escolha ideal em simulações e ataques reais.
Fundamentos de arquitetura
Para entender como as falhas de segurança funcionam de verdade, você precisa ter uma base sólida de arquitetura de computadores. Quando falamos disso, estamos lidando com a forma como o processador, a memória e os dispositivos de entrada e saída se organizam e se comunicam. A maioria dos computadores que usamos segue o modelo de Von Neumann, que basicamente estrutura tudo em três partes principais: a CPU, a memória RAM e os canais de entrada e saída, como teclado, disco e rede.
A CPU é onde tudo acontece. Ela pega as instruções da memória, executa operações, toma decisões e controla o fluxo do programa. Dentro dela, existem registradores importantes que você precisa conhecer se quiser trabalhar com exploits, por exemplo, o instruction pointer, que aponta para o próximo comando que será executado, e o stack pointer, que mostra onde estão os dados da pilha naquele momento.
A pilha é onde muita coisa sensível acontece. Quando um programa roda, ele usa a pilha para guardar variáveis locais, parâmetros de funções e, mais importante, o endereço de retorno de uma função. Se você conseguir manipular esse conteúdo, como acontece num buffer overflow, dá pra mudar o fluxo do programa e mandar ele executar o que você quiser.
E pra enxergar tudo isso funcionando de verdade, existe uma ferramenta chamada Immunity Debugger. Com ela, dá pra acompanhar em tempo real como o programa é executado: ver quais instruções estão sendo carregadas, como a memória é acessada, e o que está acontecendo com cada registrador. Quando você roda uma aplicação vulnerável ali, como o famoso vulnserver, fica claro como um erro de programação pode abrir a porta pra um exploit funcional.
Arquitetura x86
A arquitetura x86 é uma das mais utilizadas no mundo dos computadores, especialmente em PCs e servidores. Ela é baseada em um conjunto de instruções (ISA — Instruction Set Architecture) originalmente desenvolvido pela Intel, que define como o processador entende e executa comandos. Quando falamos em arquitetura x86, estamos falando tanto da forma como o processador é construído quanto das instruções que ele é capaz de interpretar.
Esse modelo segue a estrutura de Von Neumann, onde memória e instruções compartilham o mesmo barramento. Dentro do processador, a execução é controlada por registradores, pequenos espaços de armazenamento extremamente rápidos. Entre os principais estão o EIP
(ou RIP
em sistemas de 64 bits), que indica qual instrução está sendo executada, e o ESP
(ou RSP
), que aponta para o topo da pilha.
Na x86, a pilha cresce de endereços mais altos para mais baixos na memória, e isso é fundamental na hora de entender como acontecem falhas como buffer overflows. Quando uma função é chamada, o endereço para onde o código deve retornar é empilhado, junto com outros dados. Se um atacante consegue ultrapassar o limite do buffer e sobrescrever esse endereço, ele pode redirecionar a execução para um código malicioso.
Outra característica marcante da arquitetura x86 é que ela permite acesso direto à memória e uso de instruções de baixo nível, o que facilita tanto a programação mais precisa quanto a criação de exploits. É por isso que grande parte dos exemplos de segurança ofensiva são baseados nela. Quando você entende como os registradores, a pilha e o fluxo de execução se comportam nesse ambiente, fica muito mais claro como funcionam ataques como RCE (Remote Code Execution), escalonamento de privilégios e manipulação de memória.
Componentes da CPU
Os componentes da CPU, na perspectiva da segurança ofensiva e especialmente da arquitetura x86, são as engrenagens fundamentais que permitem entender como o sistema executa instruções e onde exatamente falhas podem ser exploradas. Quando você analisa uma aplicação vulnerável, é dentro da CPU que todo o comportamento do exploit se materializa.
A CPU é formada por três partes principais. A Unidade de Controle é o cérebro que coordena tudo. Ela busca as instruções na memória, interpreta o que deve ser feito e orquestra os demais componentes para executar a tarefa. Esse componente decide, por exemplo, quando carregar um valor de um registrador, quando acessar a memória ou quando empurrar dados na pilha.
A Unidade Lógica e Aritmética (ALU) executa cálculos matemáticos e operações lógicas. Ela é usada constantemente em tarefas simples, como somar dois números, comparar valores, aplicar operações de bits e decidir o caminho de um if
. Quando uma instrução envolve manipulação de dados, é a ALU que faz o trabalho bruto.
Temos também os registradores, que são pequenas áreas de memória interna ultrarrápidas. Eles não fazem parte da memória RAM, mas ficam dentro do próprio processador e são usados para armazenar valores temporários essenciais para a execução das instruções. São esses registradores que você vê sendo diretamente manipulados quando usa ferramentas como o Immunity Debugger. Eles mostram exatamente onde está o ponteiro de instrução, qual valor está sendo comparado, qual endereço está sendo lido, etc.
Toda a CPU funciona dentro de um ciclo de busca e execução, ela busca uma instrução da memória, armazena temporariamente nos registradores, executa com ajuda da ALU e segue para a próxima instrução, tudo isso orquestrado pela Unidade de Controle. Esse fluxo, quando entendido com clareza, permite que você visualize onde exatamente um exploit precisa atuar para redirecionar a execução, alterar valores ou forçar uma falha.
Registradores
Os registradores são elementos essenciais dentro da CPU e, na arquitetura x86, eles exercem um papel direto na execução de qualquer código, inclusive código malicioso. Eles funcionam como "variáveis internas" do processador, extremamente rápidas e acessadas constantemente durante a execução das instruções.
Na arquitetura x86, você tem registradores de propósito geral, registradores de controle e alguns específicos para controle de fluxo e manipulação de pilha. Os principais registradores que você precisa conhecer ao analisar uma aplicação vulnerável ou montar um exploit são:
EAX, EBX, ECX, EDX: esses são os registradores de uso geral. Servem para armazenar temporariamente dados utilizados em cálculos, movimentações de memória ou qualquer operação intermediária. Por exemplo, a maioria das instruções aritméticas usa
EAX
como acumulador padrão.ESI e EDI: são usados comumente em operações com blocos de memória, como manipulação de strings e buffers. Em alguns casos, são usados em operações de cópia e comparação de dados.
EBP (Base Pointer): ele é fundamental para o controle de escopo de uma função. Quando uma função é chamada, o endereço do
EBP
atual é salvo e um novo valor é empilhado, delimitando a base da pilha para aquela função. Isso facilita acessar parâmetros e variáveis locais de forma organizada.ESP (Stack Pointer): é o registrador que aponta para o topo atual da pilha. Cada vez que um valor é empilhado com um
PUSH
, oESP
é decrementado. CadaPOP
incrementa oESP
, retirando o valor da pilha. Manipular oESP
afeta diretamente a integridade da pilha e o comportamento da execução.EIP (Instruction Pointer): esse registrador é talvez o mais importante no contexto ofensivo. Ele aponta para o endereço da próxima instrução a ser executada. Quando um exploit de buffer overflow é bem-sucedido, o atacante consegue sobrescrever o EIP e redirecionar a execução para um shellcode ou outro payload.
Durante a análise de uma aplicação no Immunity Debugger, é comum monitorar esses registradores em tempo real. Isso permite ver, por exemplo, quando o EIP foi desviado, quando um valor suspeito foi carregado no EAX ou quando o ESP aponta para uma região da memória que foi injetada com código malicioso.
Registrador EIP
O registrador EIP (Extended Instruction Pointer) é um dos pontos mais críticos da arquitetura x86 quando o assunto é exploração de vulnerabilidades. Ele tem uma função bem específica, indicar o endereço da próxima instrução que a CPU vai executar. Em outras palavras, o EIP é o que comanda o fluxo de execução de um programa.
Durante a execução normal de uma aplicação, o EIP vai sendo atualizado automaticamente pela CPU, seguindo o encadeamento das instruções. Quando você faz uma chamada de função, o endereço atual do EIP é salvo na pilha para que, ao final da função, o programa saiba para onde retornar. Esse comportamento é essencial para manter o controle da lógica de um software.
Agora, o que torna o EIP tão importante do ponto de vista ofensivo é o fato de que, se ele for sobrescrito com um endereço arbitrário, a CPU vai simplesmente pular para esse novo endereço e continuar executando a partir dali. Isso é a base de um ataque de buffer overflow, você insere dados além do limite esperado, invade o espaço reservado para o endereço de retorno e coloca ali o endereço de um shellcode ou payload controlado.
No desafio com o Immunity Debugger, dá para observar esse processo em tempo real. Quando o exploit é disparado e o programa trava, você consegue verificar que o EIP foi substituído pelo valor que você injetou. Se esse valor apontar para uma área da memória onde você colocou código malicioso, o controle da aplicação passa para você. Isso mostra, na prática, o impacto direto de se controlar o EIP.
Por isso, durante a criação de um exploit, um dos passos mais importantes é encontrar exatamente quantos bytes são necessários para alcançar o EIP. Esse deslocamento (offset) precisa ser calculado com precisão. Uma vez feito isso, você pode testar a injeção de endereços de forma controlada até alcançar a execução do código desejado.
Entendendo a Anatomia da Pilha
Entender a anatomia da pilha é essencial para quem trabalha com exploração de vulnerabilidades, especialmente em arquiteturas como a x86. A pilha é uma estrutura de memória do tipo LIFO (Last In, First Out), usada intensivamente durante a execução de funções. Sempre que uma função é chamada, informações críticas são empilhadas: o endereço de retorno, os parâmetros da função, os valores dos registradores, entre outros.
Na arquitetura x86, a pilha cresce de endereços de memória mais altos para mais baixos. Isso quer dizer que, a cada valor empilhado (com o comando PUSH
), o ponteiro da pilha, que é o registrador ESP
, é decrementado. Quando um valor é retirado (com o comando POP
), o ESP
é incrementado, movendo-se para cima na memória.
Durante uma chamada de função, o conteúdo do EIP
(o endereço da próxima instrução após a chamada) é empilhado para que a aplicação saiba onde continuar depois de concluir a função. Em seguida, o EBP
(base pointer) é atualizado para marcar a base da pilha dessa função, criando uma estrutura organizada para acesso às variáveis locais e parâmetros.
Essa organização é explorável. Se, por exemplo, uma função define um buffer para armazenar uma string, e o programa não limita corretamente o tamanho dessa entrada, é possível escrever além dos limites do buffer e invadir o espaço onde está o endereço de retorno. Se o atacante inserir bytes suficientes, ele pode sobrescrever esse endereço e fazer com que, ao final da função, o programa continue a execução em um local arbitrário — geralmente onde está um shellcode.
Esse tipo de ataque é o buffer overflow clássico. Durante a análise com uma ferramenta como o Immunity Debugger, você pode ver o ESP
apontando para a região da pilha onde os dados foram injetados, e o EIP
sendo redirecionado para essa área, confirmando que a pilha foi manipulada com sucesso.
Portanto, dominar a anatomia da pilha é mais do que entender uma estrutura de dados. É saber como a execução do programa se organiza internamente e como, manipulando essa organização, é possível redirecionar o fluxo de execução e assumir o controle do sistema. É a espinha dorsal de praticamente todos os ataques de execução de código locais.
ASSEMBLY x86
Assembly x86 é a linguagem de mais baixo nível que podemos usar para interagir diretamente com a arquitetura de processadores da família x86. Ela reflete de forma literal as instruções que a CPU executa, sem abstrações. Cada linha de código em Assembly representa uma instrução específica da CPU, controlando diretamente registradores, memória, saltos de execução e operações aritméticas ou lógicas.
Na prática, trabalhar com Assembly x86 significa lidar com instruções como MOV
, ADD
, SUB
, JMP
, CALL
, RET
, PUSH
, POP
, entre outras. Por exemplo, MOV EAX, 1
move o valor 1
para o registrador EAX
. Já JMP 0x08048484
redireciona o fluxo do programa para o endereço indicado. Esses comandos operam diretamente sobre os registradores e a memória, com total controle da CPU.
Um ponto crítico na exploração de vulnerabilidades é entender como o Assembly manipula a pilha e os registradores durante chamadas de função e controle de fluxo. Quando uma função é chamada com CALL
, o endereço de retorno é automaticamente empilhado, e o ponteiro de instrução (EIP
) é alterado. Ao fim da função, RET
retira o endereço da pilha e o coloca de volta no EIP
. Se esse endereço for controlado por um atacante (como num buffer overflow), ele pode redirecionar a execução para onde quiser.
Outro conceito essencial é o uso dos operadores condicionais e saltos como JE
, JNE
, JG
, JL
, que controlam a lógica condicional diretamente com base em flags da CPU, como o Zero Flag ou o Carry Flag. Isso permite construir estruturas de decisão e laços diretamente em Assembly, ainda que de forma muito mais detalhada e manual que em linguagens de alto nível.
No contexto de exploits, Assembly é usado para construir shellcodes, analisar como os programas se comportam na memória e entender como instruções vulneráveis podem ser exploradas. Quando você usa uma ferramenta como o Immunity Debugger, é exatamente o código Assembly que você está lendo na execução do processo — linha por linha, como a CPU enxerga.
Dominar Assembly x86 é, portanto, essencial para entender profundamente como uma aplicação funciona no nível mais baixo, e principalmente, como ela pode ser quebrada. É onde termina a abstração e começa o controle absoluto sobre o sistema.
Instruções
As instruções do Assembly x86 são o núcleo da linguagem, cada uma delas representa uma operação que a CPU executa diretamente. São essas instruções que permitem mover dados, controlar o fluxo do programa, realizar cálculos, manipular a pilha e interagir com a memória. Cada instrução é escrita de forma direta, sem abstrações, você diz exatamente o que a CPU deve fazer e com quais registradores ou endereços.
Uma das instruções mais básicas e comuns é o MOV
, usada para mover dados entre registradores, ou entre registradores e memória. Por exemplo, MOV EAX, 1
coloca o valor 1 no registrador EAX
. Se você quiser pegar esse valor e colocá-lo na memória, poderia usar algo como MOV [EBX], EAX
, assumindo que EBX
tem um endereço de memória válido.
As instruções PUSH
e POP
manipulam a pilha. PUSH EAX
empilha o valor de EAX
, decrementando o registrador ESP
e armazenando o dado no novo topo da pilha. Já POP EBX
faz o inverso: remove o valor do topo da pilha e coloca no registrador EBX
, além de ajustar o ESP
para cima. São essas operações que preparam o ambiente de uma função, salvam estados temporários ou manipulam parâmetros.
Instruções de controle de fluxo, como JMP
, CALL
e RET
, são fundamentais em qualquer exploit. JMP
faz um salto incondicional: redireciona o EIP
para outro endereço. CALL
chama uma função e empilha o endereço de retorno. RET
pega esse endereço da pilha e coloca no EIP
, voltando ao ponto de origem. Se o endereço empilhado for manipulado, o RET
pode redirecionar a execução para um shellcode, por exemplo.
Existem ainda as instruções condicionais, como JE
(jump if equal), JNE
(jump if not equal), JG
, JL
, etc. Elas funcionam após instruções de comparação, como CMP
, que ajustam os flags da CPU. Por exemplo, CMP EAX, EBX
compara os dois registradores; se forem iguais, a flag de zero (ZF
) é ativada, e um JE
logo em seguida será executado.
Também temos instruções aritméticas e lógicas como ADD
, SUB
, INC
, DEC
, AND
, OR
, XOR
. Essas operam diretamente sobre os valores nos registradores ou na memória. São fundamentais tanto em cálculos quanto na preparação de estruturas de controle.
Essas instruções todas operam de maneira muito direta sobre o hardware, e justamente por isso são tão importantes no contexto de engenharia reversa e exploração de vulnerabilidades. Quando se entende o que cada instrução faz, é possível prever e manipular o comportamento da aplicação — especialmente em cenários onde se quer alterar o fluxo de execução, introduzir código próprio ou abusar de instruções mal programadas.
Buffer Overflow
O Buffer Overflow é uma vulnerabilidade clássica e crítica encontrada em software, que acontece quando uma quantidade excessiva de dados é inserida em um buffer, uma área de armazenamento temporário na memória, excedendo seu limite e sobrescrevendo áreas adjacentes da memória.
Sobrescrever o buffer de memória pode causar comportamentos inesperados no programa, como travamentos, alteração no fluxo de execução ou, em cenários mais graves, a execução de código malicioso. O atacante pode aproveitar essa falha para injetar instruções arbitrárias no sistema, comprometendo-o, obtendo acesso não autorizado ou até mesmo controle remoto.
Durante a exploração prática, uma aplicação vulnerável é executada (como o Vulnserver) dentro de uma máquina virtual (geralmente Windows XP), e ferramentas como o Immunity Debugger e o plugin Mona são usadas para mapear e entender o comportamento da memória. A partir daí, realiza-se o fuzzing, que é o envio de dados progressivamente maiores ao buffer, para descobrir em que ponto ocorre a falha.
Existem técnicas específicas para exploração, como o SEH Based, que manipula a estrutura de tratamento de exceções do Windows, e o Egghunter, usado quando o espaço para payload está limitado. Nessas técnicas, o objetivo é redirecionar o fluxo do programa para o código malicioso injetado, controlando assim a execução do sistema.
Essa exploração exige conhecimento detalhado da arquitetura da aplicação, da pilha (stack) e da manipulação de registradores da CPU, sendo considerada uma das práticas mais sofisticadas em segurança ofensiva.
Fases do Buffer Overflow
As fases de criação de um exploit de buffer overflow seguem um processo técnico e metódico, especialmente quando se usa como base a técnica SEH (Structured Exception Handler). Essas etapas são essenciais para manipular o fluxo de execução da aplicação de forma precisa. A seguir, explico cada fase de forma prática, como apresentada nas aulas:
Primeiro, é feita a conexão com a aplicação vulnerável. Normalmente se usa o Netcat para testar a resposta da aplicação e identificar comandos válidos. Isso ajuda a escolher o ponto certo onde o exploit será injetado, como o comando "GMON" no Vulnserver.
Em seguida, vem o fuzzing, que é o envio de dados de tamanho crescente até que a aplicação trave. O objetivo é encontrar o tamanho exato do buffer que causa o estouro, o que é feito com um script que envia "A" repetidamente e identifica o momento do crash. Quando a aplicação quebra, é anotado o tamanho do buffer que causou isso.
Com esse tamanho identificado, usa-se um padrão cíclico (gerado por ferramentas como o pattern_create.rb
do Metasploit) para substituir os "A" e descobrir exatamente qual parte do buffer sobrescreve os registradores críticos, como o EIP ou o SEH. Após o crash, o valor sobrescrito é usado com o pattern_offset.rb
para calcular a posição exata no buffer.
A próxima etapa é identificar um endereço de retorno confiável — normalmente um ponteiro para uma instrução POP POP RET
— que esteja em uma DLL sem proteção (sem ASLR, DEP, etc). Esse endereço será usado para redirecionar o fluxo de execução para nosso shellcode ou código de desvio.
Com o endereço em mãos, monta-se a estrutura do exploit: preenche-se o buffer até o ponto de controle, injeta-se o shellcode (ou um stub de Egghunter, se necessário), e posiciona-se o endereço de retorno para executar esse código malicioso. No caso do SEH, esse processo também envolve manipular os campos Next SEH e SEH corretamente.
Por fim, testa-se o exploit completo. O payload é enviado para a aplicação, e espera-se que o código malicioso seja executado. Isso pode resultar em uma reverse shell, por exemplo, comprovando que o exploit teve sucesso.
Esse processo exige precisão e paciência, e cada fase depende da observação cuidadosa do comportamento da aplicação no debugger, como o Immunity, para garantir que cada detalhe do buffer esteja controlado.
Para um processo real (em ambiente controlado), veja essa exploração de buffer overflow.