FlagCasino
4 minutes to read
We have a binary called casino
:
$ file casino
casino: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7618b017ef4299337610a90a0a6ccb7f9efc44a4, for GNU/Linux 3.2.0, not stripped
Decompilation
If we open it in Ghidra, we will see this main
function:
undefined8 main(void)
{
int iVar1;
char local_d;
uint local_c;
puts("[ ** WELCOME TO ROBO CASINO **]");
puts(
" , ,\n (\\____/)\n (_oo_)\n (O)\n __||__ \\)\n []/______\\[] /\n / \\______/ \\/\n / /__\\\n(\\ /
____\\\n---------------------"
);
puts("[*** PLEASE PLACE YOUR BETS ***]");
local_c = 0;
while( true ) {
if (0x1d < local_c) {
puts("[ ** HOUSE BALANCE $0 - PLEASE COME BACK LATER ** ]");
return 0;
}
printf("> ");
iVar1 = __isoc99_scanf(&DAT_001020fc,&local_d);
if (iVar1 != 1) break;
srand((int)local_d);
iVar1 = rand();
if (iVar1 != *(int *)(check + (long)(int)local_c * 4)) {
puts("[ * INCORRECT * ]");
puts("[ *** ACTIVATING SECURITY SYSTEM - PLEASE VACATE *** ]");
/* WARNING: Subroutine does not return */
exit(-2);
}
puts("[ * CORRECT *]");
local_c = local_c + 1;
}
/* WARNING: Subroutine does not return */
exit(-1);
}
Although it is a simple code, let’s rename and retype some variables to make the code easier to read:
int main() {
int r;
char c;
uint i;
puts("[ ** WELCOME TO ROBO CASINO **]");
puts(" , ,\n (\\____/)\n (_oo_)\n (O)\n __||__ \\)\n []/______\\[] /\n / \\______/ \\/\n / /__\\\n(\\ /____\\\n---------------------");
puts("[*** PLEASE PLACE YOUR BETS ***]");
i = 0;
while (true) {
if (29 < i) {
puts("[ ** HOUSE BALANCE $0 - PLEASE COME BACK LATER ** ]");
return 0;
}
printf("> ");
r = __isoc99_scanf(" %c", &c);
if (r != 1) break;
srand((int) c);
r = rand();
if (r != check[(int) i]) {
puts("[ * INCORRECT * ]");
puts("[ *** ACTIVATING SECURITY SYSTEM - PLEASE VACATE *** ]");
exit(-2);
}
puts("[ * CORRECT *]");
i++;
}
exit(-1);
}
The program takes characters from user input. For each character, it initializes the PRNG with srand((int) c)
and checks that rand()
equals check[(int) i]
:
printf("> ");
r = __isoc99_scanf(" %c", &c);
if (r != 1) break;
srand((int) c);
r = rand();
if (r != check[(int) i]) {
puts("[ * INCORRECT * ]");
puts("[ *** ACTIVATING SECURITY SYSTEM - PLEASE VACATE *** ]");
exit(-2);
}
puts("[ * CORRECT *]");
Notice that check
is an int
array of 30
elements. We know this because the while
loop will stop when i
is greater than 29
:
if (29 < i) {
puts("[ ** HOUSE BALANCE $0 - PLEASE COME BACK LATER ** ]");
return 0;
}
Solution
Since we are looking for a flag, we know that it starts with HTB{
:
$ ./casino
[ ** WELCOME TO ROBO CASINO **]
, ,
(\____/)
(_oo_)
(O)
__||__ \)
[]/______\[] /
/ \______/ \/
/ /__\
(\ /____\
---------------------
[*** PLEASE PLACE YOUR BETS ***]
> H
[ * CORRECT *]
> T
[ * CORRECT *]
> B
[ * CORRECT *]
> {
[ * CORRECT *]
> a
[ * INCORRECT * ]
[ *** ACTIVATING SECURITY SYSTEM - PLEASE VACATE *** ]
Now we need to find a way to know the next character. One approach is to try all printable characters until we find the one that outputs [ * CORRECT * ]
. But this is so dumb, let’s find another way.
We can take the expected values of check
and find which seed will make rand()
output those values. We can see these values in Ghidra:
check XREF[3]: Entry Point(*), main:00101216(*),
main:0010121d(*)
00104080 be 28 4b int[30]
24 05 78
f7 0a 17
00104080 [0] 244B28BEh, AF77805h, 110DFC17h, 7AFC3A1h,
00104090 [4] 6AFEC533h, 4ED659A2h, 33C5D4B0h, 286582B8h,
001040a0 [8] 43383720h, 55A14FCh, 19195F9Fh, 43383720h,
001040b0 [12] 19195F9Fh, 747C9C5Eh, F3DA237h, 615AB299h,
001040c0 [16] 6AFEC533h, 43383720h, F3DA237h, 6AFEC533h,
001040d0 [20] 615AB299h, 286582B8h, 55A14FCh, 3AE44994h,
001040e0 [24] 6D7DFE9h, 4ED659A2h, CCD4ACDh, 57D8ED64h,
001040f0 [28] 615AB299h, 22E9BC2Ah
It’s probably better to look at the solution script:
#include <stdlib.h>
#include <stdio.h>
#define MAX 30
int check[MAX] = {0x244b28be, 0xaf77805, 0x110dfc17, 0x7afc3a1, 0x6afec533, 0x4ed659a2, 0x33c5d4b0, 0x286582b8, 0x43383720, 0x55a14fc, 0x19195f9f, 0x43383720, 0x19195f9f, 0x747c9c5e, 0xf3da237, 0x615ab299, 0x6afec533, 0x43383720, 0xf3da237, 0x6afec533, 0x615ab299, 0x286582b8, 0x55a14fc, 0x3ae44994, 0x6d7dfe9, 0x4ed659a2, 0xccd4acd, 0x57d8ed64, 0x615ab299, 0x22e9bc2a};
int main() {
char flag[MAX];
char c;
int r;
int i;
for (c = 0x20; c < 0x7f; c++) {
srand(c);
r = rand();
for (i = 0; i < MAX; i++) {
if (check[i] == r) {
flag[i] = c;
}
}
}
puts(flag);
return 0;
}
Flag
We just need to compile the code and run it to get the flag:
$ gcc solve.c
$ ./a.out
HTB{r4nd_1s_sup3r_pr3d1ct4bl3}