babygame01


July 7, 20256 minutes

En este challenge no se proporciona código

Renombrar variables y entender el código

main

undefined4 main(void)

{
  int iVar1;
  undefined4 uVar2;
  int in_GS_OFFSET;
  int local_aac;
  int local_aa8;
  char local_aa4;
  undefined local_aa0 [2700];
  int local_14;
  undefined *local_10;
  
  local_10 = &stack0x00000004;
  local_14 = *(int *)(in_GS_OFFSET + 0x14);
  init_player(&local_aac);
  init_map(local_aa0,&local_aac);
  print_map(local_aa0,&local_aac);
  signal(2,sigint_handler);
  do {
    do {
      iVar1 = getchar();
      move_player(&local_aac,(int)(char)iVar1,local_aa0) ;
      print_map(local_aa0,&local_aac);
    } while (local_aac != 0x1d);
  } while (local_aa8 != 0x59);
  puts("You win!");
  if (local_aa4 != '\0') {
    puts("flage");
    win();
    fflush(_stdout);
  }
  uVar2 = 0;
  if (local_14 != *(int *)(in_GS_OFFSET + 0x14)) {
    uVar2 = __stack_chk_fail_local();
  }
  return uVar2;
}

La variable local_aa0 tiene pinta que es la que almacena el mapa del juego

  undefined local_aa0 [2700];
  undefined mapa [2700];

En iVar1 se guarda el input de donde queremos ir

iVar1 = getchar();
move = getchar();

En la función move_player() se le pasa el movimiento, mapa y local_aac que tiene pinta de ser la posición del jugador.

move_player(&local_aac,(int)(char)move,mapa);
move_player(&pos_player,(int)(char)move,mapa);

Más abajo se valida si hemos llegado a la posición objetivo, de caso contrario sigue el bucle del programa. Por como está escrito parece que pos_player es la x, y local_aa8 es la y

    } while (pos_player != 0x1d);
  } while (local_aa8 != 0x59);

¿Pero entonces porque en move_player se pasa solo la x? Lo que puede ser es que pos_player sea una lista de 2 ints int pos_player[2]; asi que local_aa8 = pos_player[1] y pos_player = pos_player[0]

    } while (pos_player[0] != 0x1d);
  } while (pos_player[1] != 0x59);

move_player

void move_player(int *pos_player,char move,int mapa)

{
  int iVar1;
  
  if (move == 'l') {
    iVar1 = getchar();
    player_tile = (undefined)iVar1;
  }
  if (move == 'p') {
    solve_round(mapa,pos_player);
  }
  *(undefined *)(*pos_player * 0x5a + mapa + pos_play er[1]) = 0x2e;
  if (move == 'w') {
    *pos_player = *pos_player + -1;
  }
  else if (move == 's') {
    *pos_player = *pos_player + 1;
  }
  else if (move == 'a') {
    pos_player[1] = pos_player[1] + -1;
  }
  else if (move == 'd') {
    pos_player[1] = pos_player[1] + 1;
  }
  *(undefined *)(*pos_player * 0x5a + mapa + pos_play er[1]) = player_tile;
  return;
}
  • Si el carácter previamente introducido es l, nos piden otro carácter que va a ser el icono de la posición del jugador (default: @).
  • Si se había puesto una p, llama a solve_round, que básicamente te lleva automáticamente a la posición final
  • Con w,a,s,d simplemente hace el movimiento en esa dirección
  if (move == 'w') {
    *pos_player = *pos_player + -1;
  }
  else if (move == 's') {
    *pos_player = *pos_player + 1;
  }
  else if (move == 'a') {
    pos_player[1] = pos_player[1] + -1;
  }
  else if (move == 'd') {
    pos_player[1] = pos_player[1] + 1;
  }

En la función print_mapa se imprime el mapa, justamente se hace un bucle de 30*90 que es igual a 2700 que es el tamaño que tenía mapa, así que podemos confirmar que realmente esa variable era el mapa

void print_map(int mapa,undefined4 pos_player)

{
  int y;
  int x;
  
  clear_screen();
  find_player_pos(mapa);
  find_end_tile_pos(mapa);
  print_flag_status(pos_player);
  for (y = 0; y < 30; y = y + 1) {
    for (x = 0; x < 90; x = x + 1) {
      putchar((int)*(char *)(x + mapa + y * 0x5a));
    }
    putchar(10);
  }
  fflush(_stdout);
  return;
}

find_player_pos

void find_player_pos(int mapa)

{
  int y;
  int x;
  
  y = 0;
  do {
    if (29 < y) {
      return;
    }
    for (x = 0; x < 90; x = x + 1) {
      if (*(char *)(x + mapa + y * 90) == player_tile) {
        printf("Player position: %d %d\n",y,x);
        return;
      }
    }
    y = y + 1;
  } while( true );
}

Itera todo el mapa para buscar si en la array mapa hay un carácter igual a player_tile, player_tile es el carácter del jugador (default: @). Si lo encuentra entiende que el jugador está en esa posición. Esto significa que el player_tile se escribe directamente en la array mapa, en su posición correspondiente, después de hacer un movimiento. Volviendo a la función move_player(), al final del todo hay

*(undefined *)(*pos_player * 0x5a + mapa + pos_player[1]) = player_tile;

Esto escribe el carácter que representa al jugador (default: @) en la posición literal en la array 2d mapa, de forma simplificada sería como, mapa[227] = player_tile

Se está tomando los valores de pos_player directamente como posiciones dentro de mapa, pero en move_player al presionar cualquier tecla, se suma pos_player[x] + 1 o pos_player[x] - 1, pero en ningún momento se valida si se excede el rango de la array.

Explotación

Entonces si nos ponemos en la posición 0 de la array mapa y vamos dándole a la a para ir atrás de array podemos llegar a sobreescribir otras variables.

Para comprobar esto me puse de player_tile la letra A

lA

Y me fui a arriba a la izquierda del mapa y fui retrocediendo posición por posición y en la cuarta a, el valor de “player has flag” paso a 65

Player has flag: 65

Justamente A en ascii es 65

$ ascii -d
    0 NUL    16 DLE    32      48 0    64 @    80 P    96 `   112 p 
    1 SOH    17 DC1    33 !    49 1    65 A    81 Q    97 a   113 q 
    2 STX    18 DC2    34 "    50 2    66 B    82 R    98 b   114 r 
    3 ETX    19 DC3    35 #    51 3    67 C    83 S    99 c   115 s 
    4 EOT    20 DC4    36 $    52 4    68 D    84 T   100 d   116 t 
    5 ENQ    21 NAK    37 %    53 5    69 E    85 U   101 e   117 u 
    6 ACK    22 SYN    38 &    54 6    70 F    86 V   102 f   118 v 
    7 BEL    23 ETB    39 '    55 7    71 G    87 W   103 g   119 w 
    8 BS     24 CAN    40 (    56 8    72 H    88 X   104 h   120 x 
    9 HT     25 EM     41 )    57 9    73 I    89 Y   105 i   121 y 
   10 LF     26 SUB    42 *    58 :    74 J    90 Z   106 j   122 z 
   11 VT     27 ESC    43 +    59 ;    75 K    91 [   107 k   123 { 
   12 FF     28 FS     44 ,    60 <    76 L    92 \   108 l   124 | 
   13 CR     29 GS     45 -    61 =    77 M    93 ]   109 m   125 } 
   14 SO     30 RS     46 .    62 >    78 N    94 ^   110 n   126 ~ 
   15 SI     31 US     47 /    63 ?    79 O    95 _   111 o   127 DEL

Así que hemos puesto player_tile en la variable que almacena lo que se printea como “player has flag”

Volviendo a main

  do {
    do {
      move = getchar();
      move_player(&pos_player[0],(int)(char)move,mapa) ;
      print_map(mapa,&pos_player[0]);
    } while (pos_player[0] != 29);
  } while (pos_player[1] != 89);
  puts("You win!");
  if (has_flag != '\0') {
    puts("flage");
    win();
    fflush(_stdout);
  }

Lo que necesitamos para llegar a win es llegar a “x = 29”, “y = 89” y que has_flag sea distinto de 0

Payload final:

wwwwaaaaaaaap
  • wwwwaaaa: Llegar hasta map[0]
  • aaaa: Retroceder hasta la variable has_flag y sobreescribirla
  • p: Usar la opción para que se solucione solo y llege hasta “x = 29” y “y = 89”
You win!
flage
PICOCTF{FLAGGG}