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

Arquivo
Descrição

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.

Note: O código pode ser compilado por meio do comando gcc persona6.c -o persona6, pois nesta situação não faz diferença se o código foi gerado na sua máquina ou na outra, já que não é um problema envolvendo proteções e/ou bibliotecas.

Player-e-Enemy.c
typedef struct player {
    int vida;
    unsigned int moedas;
} Player;

typedef struct Enemy {
    int vida;
} Enemy;

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:

  1. Vida por 10 moedas.

  2. 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.

Flag

hawk{p3Rs0n@_ta_dIF3rEnte!}

Atualizado