Persona 6
Descrição do Desafio:
Autor: HenriUz Categoria: pwn Descrição:
Há um tempo, um amigo me contou que estava realmente ansioso pelo anúncio de Persona 6. Então, decidi criar a minha própria versão de Persona 6 para ele se distrair enquanto espera.
Arquivos
flag.txt
Flag real do desafio.
persona6.c
Cósigo-fonte.
persona6
Executável (ELF).
📥 Download: Arquivos
Passo a passo da solução
1. Análise do código-fonte
O desafio fornece apenas o código-fonte persona6.c
, e o executável. Analisando ele, podemos ver que o código simula um jogo simples, no qual o jogador é representado pela struct Player
que contém um inteiro sinalizado para a vida e um inteiro não sinalizado para as moedas. Também há os inimigos que são representados pela struct Enemy
que contém apenas um inteiro para a vida.
No quesito das funções, a função main()
contém apenas a instância do Player
e o loop principal, que lê a entrada do usuário e de acordo com essa entrada chama as outras duas funções ou encerra o código (situação que também ocorre se a variável derrota
for diferente de 0).
A função dungeon()
é aquela que modifica a variável de derrota, analisando seu comportamento vemos que ela intância o inimigo com 10 de vida, e imprime algumas coisas na tela. Após isso entra em um loop que fica até o jogador ou o inimigo morrer, sendo que dentro do loop o usuário pode escolher entre atacar o inimigo (e causar 1 de dano), atacar usando a persona HAWK (e causar 5 de dano), e por fim fugir (que não é tratada no código, então nada acontece e o loop continua). Após o turno do usuário o inimigo ataca causando um dano entre 1-11.
Se o jogador for derrotado, a função retorna 1 e variável de derrota é atualizada encerrando o loop principal, mas se o jogador derrotou o inimigo, a variável continua com o valor 0 e o jogador ganha uma quantidade aleatória de moedas, no intervalo entre 1-5.
Note que a main()
e a dungeon()
não possuem nenhuma vulnerabilidade e nem algo relacionado com a flag
. Então sobra analisar a loja()
.
Essa função imprime as moedas do jogador e dá duas opções de compra:
Vida por 10 moedas.
Flag
por 999999 moedas.
E pronto, sabemos onde a flag
é acessada, basta saber como conseguir 999999 moedas, pois como visto na função dungeon()
é impossível conseguir esse valor antes do jogador morrer.
2. Exploit
A vulnerabilidade nesse desafio é bem simples, e ela acontece na verificação para comprar os itens:
Note que quando queremos comprar a flag
o código verifica se temos as moedas necessárias, mas quando vamos comprar a vida, o código não verifica. Isso faz com que você possa ter moedas negativas quando você comprar com uma quantidade inferior a 10.
Mas nesse código a moeda não fica negativa, pois como visto na estrutura do jogador a moeda é declarada como inteiro não sinalizado, ou seja, não pode ser um número negativo.
Note que na memória do computador os valores negativos são representados por valores altos, por exemplo: -1 equivale à 0xFFFFFFFFFFFFFFFF
(8 bytes
). Mas como o tipo é não sinalizado, esse valor é tratado como um número positivo, equivalendo à 4294967295, um número muito superior a 999999.
3. Solução
Sabendo o exploit
basta executar o programa, digitar a opção 2 para ir para a loja, selecionar a vida, e depois repetir o processo só que comprando a flag
dessa vez.
Importante: Desafios de PWN
normalmente são feitos para serem executados remotamente, via comando nc
, porque as flags
só estarão no computador dos organizadores do desafio. Porém para facilitar a análise, ó código e o exploit
são testados localmente, e para isso é utilizado uma fake flag
para o código não quebrar na hora de abrir o arquivo. Esse desafio não era diferente, a flag
só seria revelada se o exploit
fosse realizado via nc
no IP e porta fornecidos.
Pelo mesmo motivo da flag
estar somente no computador dos organizadores, não adianta modificar o código-fonte na sua máquina, pois o que será obtido é a fake flag
, e não a real.
Flag
hawk{p3Rs0n@_ta_dIF3rEnte!}
Atualizado