pCTF 2014: kappa

2014-04-13 00:00:00

Task Description

There's got to be a way to get into this service set up by the Plague at Can you find it?


$ file kappa
kappa: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xd69c0e2ebc9cfa9df957e89a3ba37fe6241a4bc0, stripped
$ execstack kappa
- kappa
$ ./kappa
Thank you for helping test CTF plays Pokemon! Keep in mind that this is currently in alpha which means that we will only support one person playing at a time. You will be provided with several options once the game begins, as well as several hidden options for those true CTF Plays Pokemon fans ;). We hope to expand this in the coming months to include even more features!  Enjoy! :)
Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork


kappa is a pokemon game. The player can find, fight, collect pokemon, and change the ASCII art image of pokemon in his collection.

The game has 3 types of pokemon: jesusbird, kakuna, and charizard. Their representation in memory only differs in the size of the art field.

struct charizard_s {
    char name[15];
    char art[2153];
    int health;
    int power;
    char **attack;
    void (*inspect)(struct charizard_s *);

struct kakuna_s {
    char name[15];
    char art[501];
    int health;
    int power;
    char **attack;
    void (*inspect)(struct kakuna_s *);

struct jesusbird_s {
    char name[15];
    char art[1501];
    int health;
    int power;
    char **attack;
    void (*inspect)(struct jesusbird_s *);

The player's collection of pokemon, or his pokedex, is stored by two arrays.

NULL is used to mark unused elements in pokedex. When a pokemon is inserted, or captured, it's pointer is stored in the first unused element of pokedex. When a pokemon is removed, or released, the elements of pokedex that follow the removed pokemon are shifted up one element.


When a pokemon is captured but pokedex has no empty elements, the player is prompted to choose a pokemon from pokedex to replace with the newly captured pokemon. The element in pokedex is replaced with a pointer to the new pokemon, but pokedex_types is not updated.

puts("Oh no! you don't have any more room for a Pokemon! Choose a pokemon to replace!");
choice_i = choose_pokemon();
    if(choice_i <= 4){
        pokedex[choice_i] = pokemon;
    } else {
        puts("Invalid Choice!");
} else {
    printf("%s can't be freed!\n", pokedex[0]);

Because the art field can be changed by the player, and because the art field of a charizard contains attack and inspect fields of a kakuna, these fields are now controllable.

By changing the attack field, it is possible to read memory from the kappa process, and by changing the inspect field, it is possible to change the path of execution.


The steps for the exploit are:

  1. Play the game collecting kakunas until the pokedex is full.
  2. Continue playing until a charizard appears. Capture it, name it /bin/sh, and replacing a kakuna with it. At this point, kappa will now treat the charizard as a kakuna.
  3. Read a pointer from the GOT by changing the art field of the charizard with a specially crafted string containing a pointer to two bytes into a JMP instruction in the PLT at the offset matching the attack field of a kakuna. Then, inspect the pokemon using menu option 3 (Inspect your pokemon).
  4. Calculate the address of system() using the pointer read from the GOT.
  5. Change the art of the charizard with a specially crafted string containing a pointer to system() at the offset matching inspect for a kakuna.
  6. Trigger the exploit by causing inspect() to be called by using main menu option 3.