Solução do Fetch the Flag CTF 2022: Pay Attention
Assaf Ben Josef
9 de novembro de 2022
0 minutos de leituraObrigado por participar do Fetch com a gente! Parabéns aos milhares de jogadores que participaram do Fetch the Flag CTF. E um agradecimento especial aos Snykers que criaram, testaram e documentaram os desafios!
Neste post, vamos ver como nossa equipe resolveu o desafio Pay Attention do Fetch the Flag CTF 2022 da Snyk. O desafio simula uma situação em que um pacote popular provavelmente foi sequestrado e transformado em algo malicioso — e vamos assumir o papel de pesquisadores de segurança investigando o caso!
Passo a passo
Ao começar, encontramos um arquivo para download, pytest-7.1.3.tar.gz, e a dica: “Você tem prestado atenção ultimamente?”.
De cara, notamos o nome do arquivo. Pytest é um framework de testes Python muito popular e, até o momento em que este texto foi escrito, a versão 7.1.3 era a mais recente. Portanto, parece que esse arquivo é um pacote da versão mais nova do pytest.
Mas ele não pode ser uma cópia idêntica do pytest 7.1.3 original, senão não teríamos por onde começar! Para confirmar, vamos acessar o repositório do pytest no GitHub e baixar o código-fonte da versão 7.1.3. Ao comparar as propriedades dos arquivos, percebemos que a versão do pytest no CTF é um pouco maior que a versão original.
Para descobrir exatamente onde os dois pacotes diferem, podemos extrair as pastas e compará-las no terminal com o comando diff -rq, que percorre recursivamente o conteúdo das pastas em busca de diferenças entre os arquivos. Por exemplo:

Encontramos algumas pequenas diferenças em arquivos de metadados. Mas, mais importante, um dos arquivos de código-fonte, fixtures.py, é diferente nas duas pastas — deve ser isso!
Analisando o código suspeito
Ao inspecionar o arquivo no nosso IDE favorito e percorrê-lo por um tempo (ou ao receber um alerta do IDE sobre uma indentação inesperada), encontramos esta linha de código suspeita:
À primeira vista, essa linha parece importar algumas bibliotecas e depois chamar funções interessantes, como b64decode e exec. Isso é definitivamente suspeito, mas ainda não dá para saber exatamente o que está acontecendo. Vamos decodificar.
Vamos começar pelas strings mais curtas que seguem um padrão semelhante, como \x65\x78\x65\x63.
Talvez alguns de vocês saibam que o Python 3 usa Unicode para representar strings. Se consultarmos a documentação do Python 3, vamos descobrir que esse padrão é apenas uma representação hexadecimal de dois dígitos dos pontos de código Unicode.
Mas, na verdade, não precisamos saber tudo isso. Ao observar a linha de código, vemos que essas strings são passadas sem alterações para funções nativas do Python, como __import__(). Portanto, podemos presumir que o interpretador do Python já sabe como lidar com elas. Basta abrir um REPL do Python e chamar a função print() com qualquer uma dessas strings para exibir sua representação Unicode legível. Por exemplo, print(‘\x65\x78\x65\x63’) exibiria a string exec. Legal!
Depois de resolver tudo isso, chegamos ao seguinte resultado:
Parece que esse script decodifica uma string em base64, compila o resultado e depois o executa. Agora queremos descobrir qual código está sendo executado. Vamos decodificar essa string em base64 com alguma ferramenta de decodificação online. O resultado é:
Desta vez, o código parece criar um arquivo temporário, gravar um script curto nele e depois executá-lo. O script nesse arquivo temporário baixa e executa um trecho de código de um recurso online (no domínio do Snyk CTF).
Queremos seguir as pistas e descobrir qual código é executado no final dessa cadeia, mas nos deparamos com um desafio e tanto. O código baixado está totalmente ofuscado! A única parte compreensível está bem no início — mas ela revela o nome e o repositório do GitHub da ferramenta usada para ofuscar o código: Hyperion.
Nossa primeira reação poderia ser pensar que, como temos o script ofuscado e o código-fonte da ferramenta de ofuscação, devemos colocar o chapéu de engenharia reversa e começar a trabalhar. No entanto, como o script parece muito bem ofuscado e o README do repositório da ferramenta no GitHub informa que ainda não existe uma ferramenta para desfazer a ofuscação, decidimos esgotar primeiro todas as outras possibilidades.
Com isso em mente, decidimos baixar primeiro o script ofuscado para um arquivo local e configurar um ambiente de depuração para ver se encontrávamos algo interessante durante a execução. Com nosso IDE favorito, podemos depurar e executar o código linha por linha para acompanhar o que acontece.
Aviso de segurança
Se fosse uma situação real e estivéssemos lidando com um script realmente criado para causar danos, faríamos tudo isso em um ambiente de sandbox seguro!
E, felizmente, depois de algum tempo, nosso pequeno olho consegue encontrar a flag escondida entre as variáveis declaradas (depois de remover as barras invertidas):

Atenção redobrada!
Recapitulando, tivemos:
Um pacote aparentemente inofensivo, com uma linha de código suspeita injetada em um de seus arquivos
A linha de código compila e executa um script codificado em base64
Esse script cria e executa um arquivo temporário
Esse arquivo acessa um recurso remoto para baixar um script ofuscado e, em seguida, executá-lo
Ao inspecionar e depurar esse script ofuscado, finalmente encontramos a flag escondida entre suas declarações.
Esse tipo de ofuscação em várias camadas é muito comum em injeções de scripts maliciosos. Que jornada!
Quer saber como encontramos todas as outras flags? Acesse nossa página de soluções do Fetch the Flag para ver como fizemos.



