Dias após a concepção da equipe, nós tivemos a oportunidade de representar a UTFPR na nossa primeira competição de Capture the Flag ou CTF, o University CTF 2024: Binary Badlands da plataforma HackTheBox, que aconteceu entre os dias 13 e 15 de Dezembro. O evento contou com 49 flags, que são pequenos códigos escondidos que você precisa encontrar ou conquistar para ganhar pontos na competição, que foram distribuídas em desafios de diferentes frentes de segurança cibernética.
Após uma sequência de oficinas preparatórias que fizemos para capacitar os membros da equipe para participar na competição, obtivemos a classificação 108 de um total de 1128 equipes participantes de universidade ao redor do mundo. Nosso resultado foi muito satisfatório considerando o pouco tempo que tivemos para preparar a equipe para a competição, o que nos fez perceber o quanto o nosso projeto é promissor.
Um dos desafios concluídos pela equipe na categoria de engenharia reversa foi o ColossalBreach, que consistia em dois arquivos: brainstorm.ko
que era um kernel object file do Linux e um arquivo de texto simples chamado de logs
. O desafio contava com 7 perguntas, e a resposta para cada uma delas era considerada uma flag:
- Who is the module’s author?
- What is the name of the function used to register keyboard events?
- What is the name of the function that converts keycodes to strings?
- What file does the module create to store logs? Provide the full path
- What message does the module print when imported?
- What is the XOR key used to obfuscate the keys? (e.g. 0x01, 0x32)
- What is the password entered for ‘adam’?
Para encontrar a maioria das respostas, fizemos a engenharia reversa do arquivo brainstorm.ko
utilizando a ferramenta Ghidra.
Para a primeira pergunta, que era o nome do autor do módulo, foi possível encontrar o nome na aba de strings do Ghidra após fazer a análise do arquivo, mas também poderia ser encontrado utilizando o comando strings
do Linux combinado com grep
para encontrar um texto específico: strings brainstorm.ko | grep "author"
.
Resposta: 0xEr3n
.
Para a segunda pergunta, o nome da função usada para registrar eventos do teclado, pode ser utilizado um decompiler como o Ghidra, BinaryNinja ou uma ferramenta online como o https://dogbolt.org para fazer uma análise do código. No output gerado, podemos encontrar a função register_keyboard_notifier()
, que na verdade é uma função do próprio kernel.
Resposta: register_keyboard_notifier
.
Similarmente para a terceira pergunta, podemos encontrar uma função chamada keycode_to_string()
no código, que como o próprio nome já diz, converte keycodes
para strings
. Convenientemente a função foi nomeada exatamente como esperávamos, mas poderia ser o caso de se ter que analisar a lógica da função para entender o funcionamento caso o nome fosse condizente com a pergunta.
Resposta: keycode_to_string
.
A quarta pergunta envolveu um pouco mais de pesquisa, e se tratava de determinar o arquivo criado para armazenar os logs da aplicação e o caminho completo (full path) de onde ele era salvo na máquina. Para chegar na resposta, encontramos no código gerado pelo Ghidra uma instrução debugfs_create_dir("spyyy",0)
, que cria um diretório chamado spyyy
. Ao procurar na documentação do kernel do Linux a função DebugFS, descobrimos seu diretório padrão: /sys/kernel/debug
, tendo assim o caminho completro: /sys/kernel/debug/spyyy
.
Para descobrir o nome do arquivo de logs, observamos que logo após a criação do diretório havia uma instrução debugfs_create_file(&DAT_00100c6c,...)
, onde &DAT_00100c6c
é um ponteiro referenciando uma região da memória. Ao navegar para esse endereço pelo Ghidra, podemos ver os caracteres ocupando esse e os próximos endereços formando a palavra keys
.
Resposta: /sys/kernel/debug/spyyy/keys
A resposta da quinta pergunta estava convenientemente localizada nos próximos endereços de memória logo após a palavra keys
localizada anteriormente, que era a mensagem imprimida quando o módulo era importado: w00tw00t
.
Resposta: w00tw00t
.
Para a sexta pergunta, que era qual a XOR key utilizada para ofuscar as chaves, fizemos uma busca pelo caractere ‘^
‘ que simboliza a operação XOR sendo feita no código, e encontramos a seguinte instrução: *pbVar2 = *pbVar2 ^ 0x19;
nos dizendo que a chave utilizada foi o valor 0x19
.
Resposta: 0x19
.
A pergunta final era o a senha utilizada pelo usuário ‘adam’, e isso nos levou de volta ao outro arquivo apresentado no desafio, o arquivo logs
. No arquivo, podemos ver uma série de caracteres encriptados, que, podemos concluir pela pergunta anterior, que foram ofuscados utilizando a operação XOR com a chave 0x19
. Existem diversas formas de reverter essa operação, como criar um script em Python para desfazer a operação, mas nesse caso utilizamos a ferramenta CyberChef, que nos dá diversas opções de operações de criptografia. Ao inserir o texto encriptado e a ‘receita’ XOR do CyberChef e a chave 0x19
, podemos observar no log decifrado a senha inserida pelo usuário: “supers3cur3passw0rd”.
Resposta: supers3cur3passw0rd
.
Com isso concluímos o desafio encontrando as 7 flags!
Esse foi apenas um dos 32 desafios concluídos pela equipe, mas que foi interessante por ser o primeiro contato de alguns membros com a categoria de engenharia reversa, o que despertou a vontade de se aprofundar no tema e aprender cada vez mais!
Ao final do evento, os times das universidades mais bem classificadas receberam prêmios e todos os alunos integrantes das equipes receberam um certificado de participação.
