Neste desafio, voltado para a engenharia reversa, era necessário extrair a chave vinculada ao usuário administrador do sistema. Desta vez, procurei fazer com que o desafio tivesse uma resolução bem direta para facilitar a análise de quem nunca fez engenharia reversa antes.
Uma das coisas que aprendi quando comecei a estudar engenharia reversa, há alguns anos atrás, é que a maneira mais fácil de descobrir o que um botão faz é apertando esse botão, mas vale ressaltar que isso nunca deve ser feito de forma leviana.
Sempre que a análise de um artefato se faz necessária, procurem usar uma máquina virtual previamente configurada com todos os recursos necessários e assegurem-se de que esta máquina esteja adequadamente isolada, tanto via rede como via compartilhamento de pasta. Caso contrário, se o arquivo analisado contiver uma ameaça, ela poderá infectar o sistema operacional host. Sendo assim, mesmo tendo sido eu mesmo quem criou o executável, utilizei uma VM pra analisá-lo.
Solução do Desafio
1. Como costumo fazer, primeiro executei o programa para ver como ele se comporta.
Como é possível ver na imagem acima, o programa parece ser um sistema de autenticação que solicita a inserção de um nome de usuário. Fiz tentativas com alguns nomes, mas o programa sempre pede o nome de usuário novamente. Isso já pode dar indicações que ele só pedirá a senha caso o nome de usuário esteja correto. Como não obtive sucesso, passei para a análise do executável.
2. Para analisar o artefato utilizei um debugger muito famoso chamado x64dbg. Apesar do nome, ele também conta com uma versão embutida para análise de executáveis x32 que usarei a partir deste ponto.
3. Assim que o programa foi carregado no debugger, executei-o até que chegasse no Entry Point do nosso artefato e pesquisei por strings presentes dentro do programa. Muitas vezes, os textos presentes dentro de um software trazem bons indícios do que ele executará. Como o nosso caso é um desafio para encontrar uma chave, a mesma poderia estar presente nestas strings.
Para acessá-las basta clicar com o botão direito na janela principal e seguir as opções Search for > Current Module > String references.
Automaticamente, o programa irá para a aba References, mostrando tudo o que conseguiu interpretar em formato string dentro do código.
4. Apesar da chave não estar presente nas strings, ainda assim é possível observar informações que ele exibe em tela como a sequência de sinal "-" e a frase pedindo a inserção de usuário. Parece um bom ponto de partida... então configuro um breakpoint para a linha tracejada pressionando F2 e executo o programa.
Ele chega até o breakpoint e pressiono F8 algumas vezes para que termine de exibir as mensagens na tela e aguarde nossa entrada de informações.
Como ainda não é possível saber qual usuário a aplicação aceitará, entro com o usuário “teste” e sigo a execução da aplicação.
5. Após a entrada do nome do usuário, segui com a execução por mais algumas linhas e me deparei com uma informação curiosa, o programa jogou a palavra “usuário” e a palavra “teste” para a memória e comparou as duas.
Caso o programa identifique que não são iguais, ele pegará o desvio indicado.
6. Obviamente as palavras ‘teste’ e ‘usuário’ não são iguais mas, utilizando o debugger é possível fazer com que ele se desvie do caminho habitual. Além disso, optei por mudar manualmente a ZF (Zero Flag) destacada na imagem abaixo para que ele considere o usuário como correto.
Para mudar qualquer uma das flags, basta dar dois clicks nela. A flag modificada passa a ter o número 1 e a indicação de desvio de execução não será mais seguida.
7. Na imagem anterior, é possível ver que imediatamente após o reconhecimento do usuário, o programa pede que a senha seja inserida, como já era de se esperar. Como ainda não temos indícios de qual senha será a correta, insiro o valor “testesenha” e sigo a execução por mais algumas linhas.
8. Novamente o programa chega a um desvio condicional, jogando valores na memória para comparação. Desta vez, os valores “senha” e “testesenha”. Como eles são diferentes, o programa pegaria este desvio. No entanto, para que isso não aconteça, modifico novamente o ZF para que siga a execução como se a entrada fosse bem sucedida.
Observação – Na imagem abaixo a flag ainda não está alterada.
9. Seguindo por mais algumas linhas é possível ver que imprime algo em tela. Olhando na janela de execução do programa é possível ver que ele exibe a mensagem “Bem-vindo”.
10. Para efeito de validação, encerro o debug do programa e o executo normalmente, inserindo os dados que encontramos durante a analise, “usuário” e “senha”, e o programa exibe novamente a saudação, imediatamente após exibi-la ele pede novamente um nome de usuário.
11. Executo o programa no debugger novamente, mas desta vez opto por inserir um nome de usuário invalido novamente e observar a execução em debug durante todo o programa.
Assim que executado, antes de cada desvio condicional ele exibe o que parecem ser os nomes de todos os usuários presentes na aplicação.
12. Como o enunciado nos diz que a informação que precisamos encontrar está vinculada ao usuário administrador e o único que não tem um nome convencional é o usuário “ownsys” refaço os passos e altero o comportamento do debugger para que ele interprete como usuário inserido corretamente. Durante a execução é possível ver a senha utilizada pelo usuário ownsys.
13. Continuando por mais algumas linhas é possível ver que o programa tem um comportamento diferente, e imprime uma série de variáveis logo após a mensagem de boas vindas “May the force be with you”. Ao olhar na janela de execução do programa é possível ver que a chave do nosso desafio foi impressa.