[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/h3_time/time
--2021-03-07 12:51:05-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/h3_time/time
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/h3_time/time [following]
--2021-03-07 12:51:05-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/h3_time/time
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.108.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8864 (8.7K) [application/octet-stream]
Saving to: ‘time’
time 100%[================================================================>] 8.66K --.-KB/s in 0.006s
2021-03-07 12:51:06 (1.41 MB/s) - ‘time’ saved [8864/8864]
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ file time
time: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4972fe3e2914c74bc97f0623f0c4643c40300dab, not stripped
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ chmod +x time
First let's take a look at the binary with pwn checksec as well as running it:
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ pwn checksec time; ./time
[*] '/home/nothing/binexp/3/time/time'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
Welcome to the number guessing game!
I'm thinking of a number. Can you guess it?
Guess right and you get a flag!
Enter your number: 1234
Your guess was 1234.
Looking for 34981616.
Sorry. Try again, wrong guess!
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ ./time
Welcome to the number guessing game!
I'm thinking of a number. Can you guess it?
Guess right and you get a flag!
Enter your number: 4321
Your guess was 4321.
Looking for 994945792.
Sorry. Try again, wrong guess!
So here we see that we have a 64bit binary. When we run it, it prompts us to guess a number. Let's check what ghidra finds on that binary:
undefined8 main(void)
{
time_t tVar1;
long in_FS_OFFSET;
uint local_18;
uint local_14;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
tVar1 = time((time_t *)0x0);
srand((uint)tVar1);
local_14 = rand();
puts("Welcome to the number guessing game!");
puts("I\'m thinking of a number. Can you guess it?");
puts("Guess right and you get a flag!");
printf("Enter your number: ");
fflush(stdout);
__isoc99_scanf(&DAT_00400bbc,&local_18);
printf("Your guess was %u.\n",(ulong)local_18);
printf("Looking for %u.\n",(ulong)local_14);
fflush(stdout);
if (local_14 == local_18) {
puts("You won. Guess was right! Here\'s your flag:");
giveFlag();
}
else {
puts("Sorry. Try again, wrong guess!");
}
fflush(stdout);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
Here we see something interesting, first of all the rand function which should give a random number, as well as the scanf call with the %u format string stored in local_18. Basically the main function creates a random number, then prompts us for some number to store into local_18, and then it checks if the 2 numbers are the same. If they are we enter the giveFlag function:
void giveFlag(void)
{
FILE *__stream;
long in_FS_OFFSET;
char local_118 [264];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
memset(local_118,0,0x100);
__stream = fopen("/home/h3/flag.txt","r");
if (__stream == (FILE *)0x0) {
puts("Flag file not found! Contact an H3 admin for assistance.");
}
else {
fgets(local_118,0x100,__stream);
fclose(__stream);
puts(local_118);
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
Here we see that this function reads and prints out the flag file from /home/h3/flag.txt What we need to figure out is what the output of the rand function will be. Thing is, the output of the ran dunction is not actually random. The output is based off a value called a 'seed' which it uses to determine what number sequence to generate. SO if we can get the same seed, we can get rand to generate the same sequence of numbers. Looking at the decompiled code, we see the following:
tVar1 = time((time_t *)0x0);
srand((uint)tVar1);
Here we see tVar1 gets the current time as a seed, therefore we can write a C program that uses the current time as a seed, and output a digit and redirect the output to the target:
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ vim exploit.c
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
int main()
{
uint32_t rand_num;
srand(time(0)); //seed with current time
rand_num = rand();
uint32_t ans;
printf("%d\n", rand_num);
}
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ gcc exploit.c -o exploit
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ ./exploit
1779237112
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ ./exploit
1476399991
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
→ ./exploit | ./time
Welcome to the number guessing game!
I'm thinking of a number. Can you guess it?
Guess right and you get a flag!
Enter your number: Your guess was 1333337650.
Looking for 1333337650.
You won. Guess was right! Here's your flag:
${g0tt3m_boyz}
And that's it ! We managed to print the flag.
text
text