babygame01

July 7, 20256 minutes
No source code is provided for this challenge
Renaming variables and understanding the code
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;
}
The local_aa0 variable looks like the one that stores the game map
undefined local_aa0 [2700];
undefined mapa [2700];
iVar1 stores the input for where we want to go
iVar1 = getchar();
move = getchar();
The move_player() function is given the move, the map and local_aac, which looks like the player’s position.
move_player(&local_aac,(int)(char)move,mapa);
move_player(&pos_player,(int)(char)move,mapa);
Further down, it checks whether we’ve reached the target position; otherwise the program loop continues. Judging by how it’s written, pos_player seems to be x and local_aa8 seems to be y
} while (pos_player != 0x1d);
} while (local_aa8 != 0x59);
But then why is only x passed to move_player? It could be that pos_player is a list of 2 ints, int pos_player[2];, so local_aa8 = pos_player[1] and 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;
}
- If the character entered earlier is l, it asks for another character, which will be the icon at the player’s position (default: @).
- If p was entered, it calls solve_round, which basically takes you to the final position automatically
- With w, a, s, d it simply moves in that direction
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;
}
print_map
The print_map function prints the map. It loops over 30*90, which is 2700, the size of the map, so we can confirm that variable really was the map
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 );
}
It iterates over the whole map looking for a character in the map array equal to player_tile, which is the player’s character (default: @). If it finds it, it assumes the player is at that position. This means player_tile is written directly into the map array, at the corresponding position, after each move. Going back to the move_player() function, at the very end there is
*(undefined *)(*pos_player * 0x5a + mapa + pos_player[1]) = player_tile;
This writes the character representing the player (default: @) at the literal position in the 2D map array; in simplified form, it would be something like map[227] = player_tile
The values of pos_player are used directly as positions inside the map, but in move_player, pressing any key adds pos_player[x] + 1 or pos_player[x] - 1, and at no point does it check whether the array bounds are exceeded.
Exploitation
So if we go to position 0 of the map array and keep pressing a to move backwards through the array, we can end up overwriting other variables.
To check this, I set the letter A as player_tile
lA
Then I went to the top left of the map and moved back one position at a time, and on the fourth a, the value of “player has flag” changed to 65
Player has flag: 65
And A in ASCII is exactly 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
So we have written player_tile into the variable that stores what gets printed as “player has flag”
Going back to 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);
}
To reach win we need to get to “x = 29”, “y = 89” and have has_flag be non-zero
Final payload:
wwwwaaaaaaaap
- wwwwaaaa: Get to map[0]
- aaaa: Move back to the has_flag variable and overwrite it
- p: Use the option that solves the round automatically and reaches “x = 29” and “y = 89”
You win!
flage
PICOCTF{FLAGGG}