CyberHeroes - Desafio hacker [TryHackMe]
Resolvendo a máquina CyberHeroes do TryHackMe: autenticação client-side, engenharia reversa de JavaScript e ofuscação por string invertida, e por que "se está no código do cliente, está exposto".
CyberHeroes - Writeup em Português
Resolução do desafio hacker fácil do TryHackMe
Want to be a part of the elite club of CyberHeroes? Prove your merit by finding a way to log in! Quer fazer parte do clube de elite dos CyberHeroes? Prove seu mérito encontrando uma maneira de fazer login!
Essa é uma máquina fácil, mas ela ensina um dos conceitos mais importantes (e mais mal-entendidos por quem está começando) em segurança web: a diferença entre validar algo no cliente e validar no servidor. Em vez de só “achar a senha”, eu quero que você saia daqui entendendo por que essa senha estava ao alcance da mão, porque esse mesmo erro aparece em aplicações reais, não só em CTF.
Informações da máquina:
- IP do alvo (gerado pra mim):
10.10.32.251- Plataforma: TryHackMe
- Tema: autenticação client-side / reverse de JavaScript
Teoria: o que é autenticação client-side (e por que ela é quebrada por natureza)
Antes de tocar na máquina, segura essa ideia, porque ela é o coração do desafio.
Quando você faz login num site bem feito, o que acontece é:
- O navegador (cliente) envia seu usuário e senha para o servidor.
- O servidor compara com o que está guardado (idealmente um hash no banco), decide se está certo e devolve uma sessão.
Repare: a decisão acontece no servidor, onde você (atacante) não tem acesso ao código nem aos dados. O cliente só manda os dados e recebe um “sim” ou “não”.
Agora imagine que um desenvolvedor preguiçoso coloca a senha correta dentro do JavaScript da página e faz a comparação no próprio navegador. É o equivalente a deixar o gabarito da prova impresso no verso da folha. Não importa o quão “embaralhado” esteja o gabarito, ele está na sua mão, e você consegue ler.
Nunca confie no client-side. Tudo que roda no navegador (HTML, CSS, JS) é 100% visível e modificável pelo usuário. Validação de segurança tem que acontecer no servidor. Se está no código do cliente, está exposto.
Guarda isso. A máquina inteira é uma demonstração desse erro.
1. Reconhecimento
Como sempre, começo com um Nmap pra saber o que está rodando. Confesso que, enquanto o scan completo rodava, eu já fui investigar no navegador e resolvi antes mesmo de ele terminar. Mesmo assim, o hábito de deixar rodando é bom: às vezes ele revela uma porta que você não acharia na mão.
1
sudo nmap -sS -sV -p- 10.10.32.251
Explico cada parâmetro:
-sSSYN scan (rápido e discreto; não completa o handshake TCP).-sVdetecta a versão dos serviços.-p-escaneia todas as 65535 portas.10.10.32.251o IP do alvo.
O resultado prático: um serviço web na porta 80. Abrindo no navegador:
1
http://10.10.32.251
Mentalidade: cheguei a pensar em deixar um brute-force de diretório (gobuster/ffuf) rodando, mas a regra de ouro em web é ler o código-fonte e observar o tráfego ANTES de sair forçando. O alvo te entrega muita coisa de graça se você olhar. Ainda bem que fiz isso primeiro.
2. Investigação: lendo o que o site entrega de graça
Olhando o fonte da página, vejo os .css e a estrutura sendo carregados. Decidi abrir os diretórios “pais” desses recursos numa aba nova, em especial o /assets/, que costuma guardar os recursos do site.
Pra minha surpresa, o directory listing do Apache estava habilitado em /assets/, dava pra ver os arquivos e até a versão do servidor.
O que é directory listing? Quando uma pasta não tem um
index.htmle o servidor está configurado pra listar o conteúdo, qualquer um vê todos os arquivos ali dentro (uma má configuração clássica, oOptions Indexesligado no Apache). Aqui não foi decisivo, mas em alvos reais é como achar a planta do prédio: você descobre arquivos de backup,.bak, fontes, configs.
Em CTF tem muita coisa pra desviar sua atenção, e isso cheirava a um desses desvios, então anotei mentalmente (“se eu não achar nada, volto aqui”) e segui. Voltando pra home e explorando as funcionalidades, encontrei a aba de login: um arquivo login.html.
Esse
.htmlno login me acendeu uma luz. Login normalmente é processado no backend (PHP, Node, etc.). Um login que é só um.htmlestático sugere fortemente que a lógica está no front, ou seja, no JavaScript do navegador. Primeira hipótese formada.
3. A tela de login: confirmando a hipótese client-side
Por reflexo, testei o feijão-com-arroz: admin:admin e um SQL injection básico ' or 1=1 --. Cheguei a pensar em subir o Burp pra interceptar e mandar pro Hydra fazer brute-force.
Por que o
' or 1=1 --? É o teste clássico de SQL injection em login: se a query fosse... WHERE user='$u' AND pass='$p', a aspa fecharia a string e oor 1=1tornaria a condição sempre verdadeira, logando sem senha. Mas isso só funciona se existir um banco/servidor processando, e é exatamente isso que eu vou descobrir que não existe aqui.
Antes de partir pra força bruta, fiz o passo que resolveu tudo: abri a aba Network do DevTools e tentei logar observando o tráfego. E nada. Nenhuma requisição saía pro servidor.
Esse “nada” é o achado. Se eu digito usuário e senha, clico em “login” e nenhuma requisição HTTP sai, então a decisão de “login certo ou errado” está sendo tomada dentro do navegador, em JavaScript. Hipótese confirmada: autenticação 100% client-side. O brute-force com Hydra seria inútil aqui, não há servidor pra forçar.
4. Engenharia reversa do JavaScript
Confirmado que a lógica estava no front, fui direto ao código-fonte caçar o script de autenticação.
Achei. Era a função que valida o login, e com ela o usuário e a senha. A “proteção” do dev foi guardar a senha invertida de trás pra frente (uma ofuscação ingênua). A lógica era, em essência, assim:
1
2
3
4
5
6
7
8
9
10
11
function login() {
let user = document.getElementById("username").value;
let pass = document.getElementById("password").value;
// usuário esperado e senha "ofuscada" (invertida) embutidos no JS
if (user === "h3ck3rBoi" && pass.split("").reverse().join("") === "54321@terceSrepuS") {
// libera a flag / redireciona
} else {
// "credenciais inválidas"
}
}
Repare no truque: o código pega a sua senha digitada, inverte com .split("").reverse().join("") e compara com a constante 54321@terceSrepuS. Ou seja, a senha real é essa string lida ao contrário.
O que é “ofuscação” e por que não é segurança? Inverter string, Base64, ROT13, hex, são transformações reversíveis, não criptografia. Qualquer uma é desfeita em segundos. Ofuscar um segredo que está no cliente é como escrever a senha de trás pra frente num post-it colado no monitor: continua na sua frente.
Pra reverter a string, o jeito mais rápido no terminal é o comando rev:
1
echo "54321@terceSrepuS" | rev
Explico cada parâmetro:
echo "..."joga a string ofuscada na saída padrão.| revorevinverte cada linha caractere a caractere.
Resultado:
1
SuperSecret@12345
Dica: pra brincar com Base64/ROT13/hex sem decorar comando, o CyberChef é um canivete suíço. Joga a string lá e testa todas as transformações.
Credenciais em mãos:
Credenciais: h3ck3rBoi : SuperSecret@12345
Cheguei a achar, por um segundo, que era pegadinha e que viria uma flag falsa, rs. Mas era pra valer.
🚩 Flag:
flag{edb0be532c540b1a150c3a7e85d2466e}
Resumo do Ataque
Cadeia de exploração
1
2
3
4
5
6
Recon (Nmap -> porta 80 web)
└─> Leitura do fonte / observação do tráfego (em vez de brute-force)
└─> Login é um login.html estático + DevTools Network NÃO mostra requisição
└─> Conclusão: autenticação 100% client-side (decisão no JavaScript)
└─> Reverse do JS: senha embutida e "ofuscada" (string invertida)
└─> echo "..." | rev => SuperSecret@12345 -> login -> flag
Defesas que falharam
| Falha | Por que é um problema | Correção |
|---|---|---|
| Autenticação no client-side (JS) | A decisão de login acontece onde o atacante tem controle total | Validar credenciais no servidor; o cliente só envia e recebe sim/não |
| Senha embutida no código do cliente | Qualquer um lê o JS e extrai o segredo | Senha nunca no front; guardar hash no servidor (bcrypt/argon2) |
| “Ofuscação” por string invertida | Transformação reversível não é proteção | Não existe ofuscação que proteja segredo no cliente, não coloque segredo lá |
Directory listing habilitado em /assets/ | Expõe arquivos e versão do servidor | Options -Indexes no Apache; index.html nas pastas |
Lição principal
Esse foi um CTF simples, mas a lição é grande e aparece em apps reais: olhe o código-fonte e observe a aba Network antes de qualquer coisa. O fato de nenhuma requisição sair ao tentar logar foi o sinal definitivo de que a autenticação era client-side. E lembre: ofuscação não é criptografia. Se o segredo está no cliente, ele está exposto. O servidor é o único lugar onde uma decisão de segurança pode ser realmente confiável.
Meu perfil na plataforma: https://tryhackme.com/p/laidler
Bons estudos e happy hacking! 🔒










