# BabyMaze (reverse, 122p, 53 solved)
> The flag is on the wings of the flying plane. It is possible to get the flag without instrumenting or modifying the binary. Tested on Ubuntu 16.04, Ubuntu 18.04, and Ubuntu 20.04. You should use a system able to run the game at approximately 60 fps. It requires: sudo apt-get install freeglut3
```shellnc babymaze.challenges.ooo 7777```
## Files:
- [BabyMaze](https://raw.githubusercontent.com/TFNS/writeups/master/2020-05-18-DefconQualification/babymaze/files/BabyMaze) `4d7aabfb62d46ea98379254a62023f178c1922c0908f3ff19634ca8c22f97cef`
## Overview
This challenge is about a game binary communication with a classic TCP server. The game shows up a 3D game containing a maze which contains a computer, a yellow box and plane flying around over the maze.
To solve the challenge, we have to read the flag on the plane wings (flying required). Here are the steps required to get the flags:
- Walk to the computer- Press `1` to trigger the 8-second countdown and spawn the second part of the maze after the computer- Catch the yellow box in 8 seconds (which is impossible without cheating)- Catching the yellow box activates a fly hack- Fly on top of the plane to read the flag
As the description says, this challenge is not supposed to be "hacked" with any instrumentation techniques (gdb, memory hacking, etc). This first idea that comes in mind is that maybe a glitch is possible in the game.
However, we did not find any glitch allowing us to see the flags without "cheating". The expected solution was to go around the computer by spamming direction keys.
The maze looks as follows:
![maze.png](https://raw.githubusercontent.com/TFNS/writeups/master/2020-05-18-DefconQualification/babymaze/screenshots/maze.png)
## Technical details
The main binary `BabyMaze` is spawning another binary downloaded from the Internet (`babymaze.challenges.ooo:7777`) through execve. There are a father and a son:
- The father is the original binary downloaded on the CTF platform- The son is the other binary downloaded from the server containing all the game logics/graphics.
The important thing to note is that the parent is tracing the son, so weren't able to trace the son (graphic/logic part).
To cheat or to modify the behavior of the child, we’re gonna have to `LD_PRELOAD` our own `.so` into the game. However the following check is preventing us from doing so:
```c  __nptr = *env;  uVar4 = 0x0;  while (__nptr != NULL) {    if (((*__nptr == 'L') && (__nptr[0x1] == 'D')) && (__nptr[0x2] == '_')) {      puts("Invalid environment");      exit();    }    uVar4 = uVar4 + 0x1;    __nptr = env[uVar4];  }```
As the father/son are both doing integrity checks, we were not able to patch the binary without having written "Cheater" on the plane wings.
We need to instrumentate the binary so that it is not exiting anymore. To do that, we should be able to use `gdb`. However, after finding the computer, the program crashes for some reason. Thanks to a friend, I found a way to do it stealthily.
We can use `pintool` with a custom trace module:
```c#include <stdio.h>#include "pin.H"
FILE * trace;
VOID printip(VOID *ip) {  fprintf(trace, "%p\n", ip);}
void jmp(ADDRINT ip, CONTEXT* ctxt){    PIN_SetContextReg(ctxt, REG_INST_PTR, ip+0x1d); // Jump outside of the puts    PIN_ExecuteAt(ctxt);}
// Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v){    // Insert a call to printip before every instruction, and pass it the IP    if(((INS_Address(ins)&0xff0000000000) == 0x550000000000/* is it in .text ?*/) && ((INS_Address(ins)&0xfff)==0x0F0 /*There is two check, this is the first one*/|| ((INS_Address(ins)&0xfff) == 0x979 /* and the second one*/)))    {        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)jmp, IARG_INST_PTR, IARG_CONTEXT, IARG_END);    }}
// This function is called when the application exitsVOID Fini(INT32 code, VOID *v){    fclose(trace);}
/* ===================================================================== *//* Print Help Message                                                    *//* ===================================================================== */
INT32 Usage(){    PIN_ERROR("This Pintool prints the IPs of every instruction executed\n"              + KNOB_BASE::StringKnobSummary() + "\n");    return -1;}
/* ===================================================================== *//* Main                                                                  *//* ===================================================================== */
int main(int argc, char * argv[]){    trace = fopen("itrace.out", "w");
    // Initialize pin    if (PIN_Init(argc, argv)) return Usage();
    // Register Instruction to be called to instrument instructions    INS_AddInstrumentFunction(Instruction, 0);
    // Register Fini to be called when the application exits    PIN_AddFiniFunction(Fini, 0);
    // Start the program, never returns    PIN_StartProgram();
    return 0;}
```
Compile this with pintool:
```shellmake -C  pin-3.13-98189-g60a6ef199-gcc-linux/source/tools/ManualExamples/```
The load it in the binary:
```shellLD_PRELOAD=./hook.so ./pin-3.13-98189-g60a6ef199-gcc-linux/pin -t ./pin-3.13-98189-g60a6ef199-gcc-linux/source/tools/ManualExamples/obj-intel64/itrace.so -- ./BabyMaze babymaze.challenges.ooo 7777```
> To hook into this game, I used SwapBuffer function of the freeglut library. This function is called all the time so we can easily display stuff.
```cvoid                    glutSwapBuffers(void){  // Print something  printf("Hooked!\n");
  // Call the good old one :)  handle = dlopen("/usr/local/lib/libglut.so.3.9.0", RTLD_LAZY);  old_swap = dlsym(handle, "glutSwapBuffers");  old_swap();}```
The following two functions have been used to read/write the memory:
```cint    read_memory(unsigned long addr, void *data, size_t size){  int  fd = open("/proc/self/mem", O_RDONLY);
  lseek(fd, addr, SEEK_SET);  int n = read(fd, data, size);
  close(fd);  return n;}
int     write_memory(unsigned long addr, void *data, size_t size){  int   fd = open("/proc/self/mem", O_RDONLY | O_WRONLY );
  lseek(fd, addr, SEEK_SET);  int n = write(fd, data, size);
  close(fd);  return n;}```
To read the good data and find the player address, we simply reverse the son binary after dumping it through `/proc/:pid/exe`.
These two addresses contains ASLR addresses to interesting structure:
```c# define PLAYER_BASE (0x00dc05f8) // Player ptr address# define COORD_BASE (0x00dc0588) // Player coordinates base address```
According to the binary, the player position can be retrieved like this:
```cint                     read_int(unsigned long addr) {  int                   value = 0;
  read_memory(addr, &value, sizeof(int));  return value;}
void                    write_pos(double x, double y, double z){  unsigned long         position = read_int(COORD_BASE);
  write_memory(position+(22 * 8) + (8 * 0), &x, 8);  write_memory(position+(22 * 8) + (8 * 1), &y, 8);  write_memory(position+(22 * 8) + (8 * 2), &z, 8);}
void                    read_pos(double *x, double *y, double *z){  unsigned long         position = read_int(COORD_BASE);
  read_memory(position+(22 * 8) + (8 * 0), x, 8);  read_memory(position+(22 * 8) + (8 * 1), y, 8);  read_memory(position+(22 * 8) + (8 * 2), z, 8);}```
Also, writing, '3' or '2' at the following address allowed us to fly around:
```cvoid                    fly(){  unsigned long long    a = 3;
  write_memory(0x00dc05c8, &a, sizeof(unsigned long long));  printf("Let's fly !\n");}```
However, updating this field breaks the integrity checks and "Cheater" got written into the plane's wings. But this could be used to register player position to teleport fast enough :)
The binary is handling keyboard as follows:
```cstatic player_t   *player;
void              keyboardHandler(char key){  switch(key) {  case 'A':  case 'a':    player->key = 0x61;    break;  default:    if (key - 0x31U < 0x9) {      player->key = key;    }    break;  case 'D':  case 'd':    player->key = 0x64;    break;  case 'F':  case 'f':    player->key = 0x66;    break;  }  // [...]}```
As the key is stored in memory, we can use that to write player position according to the positions we saved earlier. The final hook function will look as follows:
```cvoid                    glutSwapBuffers(void){  static void           *handle = NULL;  static GLUTSWAP       old_swap = NULL;  t_player              player;  double                x, y, z;  t_matrix              m1;
  printf("-----------------------------------\n");
  get_player(&player);  get_matrix_at(COORD_BASE, &m1;;
  // print_matrix(&m1;;  read_pos(&x, &y, &z);  printf("x = %f; y = %f; z = %f;\n", x, y, z);
  // This allows us to toggle noclip/fly mode  if (player.key == 0x61) {    walk();  }  if (player.key == 0x64) {    change_velocity(2.0);    fly();  }
  // If the 'f' key is pressed, teleport to the computer  if (player.key == 0x66) { // f    x = -48.002144; y = 3.189641; z = 88.500946;  }
  // If the 'v' key is pressed, teleport to the yellow box  // Once done, it will enable fly mode and we will be able to read the flag on the plane.  if (player.key == 0x76) { // v    x = 144.077972; y = 1.670742; z = 142.010620;  }
  write_pos(x, y, z);
  set_player(&player);
  // Call the good old one :)  handle = dlopen("/usr/local/lib/libglut.so.3.9.0", RTLD_LAZY);  old_swap = dlsym(handle, "glutSwapBuffers");  old_swap();}```
And we get the flag: `OOO{b3tt3rTh4nFort9t3}`
![flag.png](https://raw.githubusercontent.com/TFNS/writeups/master/2020-05-18-DefconQualification/babymaze/screenshots/flag.png)
- SakiiR