Ao iniciar a análise de um binário sem empacotamento em um ambiente de reversão, como o IDA Pro, uma das primeiras etapas é inspecionar as strings internas. No caso em questão, a varredura (shift+F12) revelou duas sequências de caracteres peculiares, notavelmente terminadas por ==. Esse padrão é um forte indicativo de codificação Base64.
A investigação da função principal (main) do programa demonstrou que ele solicita uma entrada ao usuário, designada como "flag". Posteriormente, essa entrada é submetida a um processo de tratamento e validação, comparando o resultado com um valor pré-definido para determinar a correção da flag.
Prosseguindo para a função crítica identificada, sub_401570, observamos a implementação de um algoritmo de codificação. Esta função é responsável por transformar uma sequência de bytes de entrada em uma string codificada em Base64, mas com uma variação importante: o uso de um alfabeto de caracteres diferente do padrão.
A lógica de sub_401570, em sua essência, segue os passos gerais da codificação Base64:
- Cálculo do Comprimento: Determina o tamanho da string de entrada.
- Alocação de Memória: Baseado no comprimento da entrada, calcula o espaço necessário para a string codificada e aloca dinamicamente essa memória.
- Processamento em Blocos: A string de entrada é processada em blocos de três bytes, que são convertidos em quatro caracteres Base64.
- Mapeamento de Caracteres: Cada valor de 6 bits resultante da divisão dos bytes de entrada é mapeado para um caractere específico de um alfabeto personalizado.
- Adição de Preenchimento (Padding): Caso o comprimento da entrada não seja um múltiplo de três, caracteres
=são adicionados ao final da string codificada, conforme a regra do Base64. - Retorno: O endereço da string Base64 modificada é retornado.
A seguir, uma representação em C da lógica de codificação utilizando um alfabeto variante, similar ao que foi observado no disassemb. Esta implementação ilustra como a string de entrada é convertida para a forma Base64, empregando um conjunto de caracteres não padrão para os 64 valores possíveis.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// O alfabeto personalizado Base64, derivado da análise do binário.
// Este é o 'aQvejafhmuyjbac' do disassembly.
const char *ALFABETO_BASE64_CUSTOM = "qvEJAfHmUYjBac+u8Ph5n9Od17FrICL/X0gVtM4Qk6T2z3wNSsyoebilxWKGZpRD";
/**
* @brief Codifica uma string de entrada utilizando um algoritmo Base64 com um alfabeto personalizado.
*
* @param dados_originais Ponteiro para a string de entrada a ser codificada.
* @return Um ponteiro para uma nova string alocada dinamicamente contendo os dados codificados.
* Retorna NULL em caso de falha na alocação de memória.
*/
char* codificar_base64_modificado(const char *dados_originais) {
if (!dados_originais) return NULL;
size_t len_origem = strlen(dados_originais);
// Calcula o tamanho necessário para a string de saída.
// Para cada 3 bytes de entrada, 4 bytes de saída são gerados.
// O '+2' e a divisão por 3 garantem o arredondamento para cima,
// e o resultado é multiplicado por 4.
size_t len_saida = 4 * ((len_origem + 2) / 3);
char *buffer_codificado = (char *)malloc(len_saida + 1); // +1 para o terminador nulo
if (!buffer_codificado) return NULL;
buffer_codificado[len_saida] = '\0'; // Garante terminação nula
size_t idx_entrada = 0;
size_t idx_saida = 0;
unsigned char bloco_3_bytes[3];
unsigned char indices_6_bits[4];
while (idx_entrada < len_origem) {
// Pega até 3 bytes da entrada. Se não houver, preenche com 0.
bloco_3_bytes[0] = dados_originais[idx_entrada++];
bloco_3_bytes[1] = (idx_entrada < len_origem) ? dados_originais[idx_entrada++] : 0;
bloco_3_bytes[2] = (idx_entrada < len_origem) ? dados_originais[idx_entrada++] : 0;
// Divide os 3 bytes (24 bits) em 4 grupos de 6 bits.
indices_6_bits[0] = (bloco_3_bytes[0] & 0xFC) >> 2;
indices_6_bits[1] = ((bloco_3_bytes[0] & 0x03) << 4) | ((bloco_3_bytes[1] & 0xF0) >> 4);
indices_6_bits[2] = ((bloco_3_bytes[1] & 0x0F) << 2) | ((bloco_3_bytes[2] & 0xC0) >> 6);
indices_6_bits[3] = (bloco_3_bytes[2] & 0x3F);
// Mapeia os índices de 6 bits para caracteres usando o alfabeto personalizado.
buffer_codificado[idx_saida++] = ALFABETO_BASE64_CUSTOM[indices_6_bits[0]];
buffer_codificado[idx_saida++] = ALFABETO_BASE64_CUSTOM[indices_6_bits[1]];
buffer_codificado[idx_saida++] = ALFABETO_BASE64_CUSTOM[indices_6_bits[2]];
buffer_codificado[idx_saida++] = ALFABETO_BASE64_CUSTOM[indices_6_bits[3]];
}
// Gerencia o preenchimento (padding) com '='.
int bytes_faltantes = len_origem % 3;
if (bytes_faltantes == 1) { // Faltam 2 bytes para um bloco completo de 3
buffer_codificado[len_saida - 2] = '=';
buffer_codificado[len_saida - 1] = '=';
} else if (bytes_faltantes == 2) { // Falta 1 byte para um bloco completo de 3
buffer_codificado[len_saida - 1] = '=';
}
return buffer_codificado;
}
Para decodificar a string codificada fornecida pelo programa (que seria o resultado da codificação da flag correta), é necessário reverter essa transformação. Primeiramente, é preciso mapear os caracteres do alfabeto variante de volta para o alfabeto Base64 padrão. Somente após essa normalização, a decodificação Base64 tradicional pode ser aplicada.
O script Python abaixo realiza este processo:
import base64
# String codificada encontrada no binário, que representa a flag esperada.
string_alvo_codificada = "5Mc58bPHLiAx7J8ocJIlaVUxaJvMcoYMaoPMaOfg15c475tscHfM/8=="
# O alfabeto personalizado que a função de codificação do binário utiliza.
# Este é o 'aQvejafhmuyjbac' que foi identificado no disassembly.
alfabeto_modificado_nss = "qvEJAfHmUYjBac+u8Ph5n9Od17FrICL/X0gVtM4Qk6T2z3wNSsyoebilxWKGZpRD"
# O alfabeto padrão da codificação Base64, para o qual normalizaremos a string.
alfabeto_base64_padrao = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# Cria uma tabela de tradução que mapeia cada caractere do alfabeto modificado
# para seu equivalente no alfabeto Base64 padrão.
tabela_conversao = str.maketrans(alfabeto_modificado_nss, alfabeto_base64_padrao)
# Aplica a tradução à string codificada, convertendo-a para uma forma Base64 padrão.
string_normalizada_base64 = string_alvo_codificada.translate(tabela_conversao)
# Decodifica a string normalizada usando a função b64decode do módulo base64 do Python.
# O resultado é uma sequência de bytes, que então é decodificada para uma string UTF-8.
dados_decodificados = base64.b64decode(string_normalizada_base64).decode('utf-8')
# Imprime a flag decodificada.
print(f"A flag decodificada é: {dados_decodificados}")
A execução do script Python revela a flag oculta:
A flag decodificada é: NSSCTF{a8d4347722800e72e34e1aba3fe914ae}