February 22, 2018

pwnable.kr write-up : dragon

danger description of pwnable.kr write-up

To comply with the rule 3, I masked some contents that is needed to solve this challenge. 




description of pwnable.kr write-up

It is called RPG game which can't win. It probably doesn't seem to win in the usual way.




run test of pwnable.kr write-up

After running it a few times, I found that the Baby Dragon appears first and next is the Mama Dragon. The HP of the Mama Dragon is 80.

The dragon could not be defeated in the usual way. It was impossible to make the dragon's HP zero.

However, there is one thing that is noteworthy. the first shown Dragon's HP is not the maximum HP. dragon's HP will increase even if you use the defense skill "[3] HollyShield".




source code of pwnable.kr write-up

The above decompiled code is a part of the defense skill "[3] HollyShield". "*(ptr+8) = *(ptr+9)" is the part where the dragon's HP increases.

"ptr+8" has the Dragon's HP and "ptr+9" has amount of HP increment 5. 1 Byte difference from the next variable means that the range of the dragon's HP is 1 Byte. In other words, you can kill a dragon through making Its HP to 0 or exceeding 127.




win source code of pwnable.kr write-up

If the dragon is killed, "call eax" is executed.

A UAF vulnerability occurs when allocating 16 bytes of heap space to v2. The "PriestAttack" and "KnightAttack" functions release 16 Bytes memory v5 but do not initialize, so v5 becomes a dangling pointer. See here for a description of UAF(Link).

Eventually, EIP jumps to the address entered in v2.


Below is an associated decompilation source.

<main>
int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  puts("Welcome to Dragon Hunter!");
  PlayGame();
  return 0;
}

<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
  _DWORD *ptr; // [sp+14h] [bp-14h]@1
  _DWORD *v5; // [sp+18h] [bp-10h]@1

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

<PriestAttack>
int __cdecl PriestAttack(int a1, void *ptr)
{
  int v2; // eax@1

  do
  {
    (*(void (__cdecl **)(void *))ptr)(ptr);
    (*(void (__cdecl **)(int))(a1 + 12))(a1);
    v2 = GetChoice();
    switch ( v2 )
    {
      case 2:
        puts("Clarity! Your Mana Has Been Refreshed");
        *(_DWORD *)(a1 + 8) = 50;
        printf("But The Dragon Deals %d Damage To You!\n", *((_DWORD *)ptr + 3));
        *(_DWORD *)(a1 + 4) -= *((_DWORD *)ptr + 3);
        printf("And The Dragon Heals %d HP!\n", *((_BYTE *)ptr + 9));
        *((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9);
        break;
      case 3:
        if ( *(_DWORD *)(a1 + 8) <= 24 )
        {
          puts("Not Enough MP!");
        }
        else
        {
          puts("HolyShield! You Are Temporarily Invincible...");
          printf("But The Dragon Heals %d HP!\n", *((_BYTE *)ptr + 9));
          *((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9);
          *(_DWORD *)(a1 + 8) -= 25;
        }
        break;
      case 1:
        if ( *(_DWORD *)(a1 + 8) <= 9 )
        {
          puts("Not Enough MP!");
        }
        else
        {
          printf("Holy Bolt Deals %d Damage To The Dragon!\n", 20);
          *((_BYTE *)ptr + 8) -= 20;
          *(_DWORD *)(a1 + 8) -= 10;
          printf("But The Dragon Deals %d Damage To You!\n", *((_DWORD *)ptr + 3));
          *(_DWORD *)(a1 + 4) -= *((_DWORD *)ptr + 3);
          printf("And The Dragon Heals %d HP!\n", *((_BYTE *)ptr + 9));
          *((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9);
        }
        break;
    }
    if ( *(_DWORD *)(a1 + 4) <= 0 )
    {
      free(ptr);
      return 0;
    }
  }
  while ( *((_BYTE *)ptr + 8) > 0 );
  free(ptr);
  return 1;
}

<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;
}


I haven't told you yet, the "SecretLevel" function actually has a key for solving this challenge. However, since this key is more than 10 Bytes, it is impossible to satisfy it by inputting a value.


Below is the Python code to get the flag.

<dragon.py>
from pwn import *

payload = ■■■■■■■■■■■
r = remote("pwnable.kr", 9004)

for i in range(0, 4):
r.send("1\n")

for ■■■■■■■■■■■
■■■■■■■■
■■■■■■■■
■■■■■■■■

r.send(payload + "\n")
r.interactive()




get flag of pwnable.kr write-up

The flag is obtained successfully.