We covered another scenario of exploiting a binary vulnerable to buffer overflow. This scenario presented a binary that takes user input and compares it to three predetermined strings based on which the binary will either store byte input into a defined memory address, allow the user to store 48 bytes into a variable whose size is 16 byte and lastly execute a system call to return the date.

We exploited the BOF by creating a ROP chain that consists of first the offset, next the gadget address, third a memory address that we can control and store /bin/sh and lastly the memory address of the system call. This was part of HackTheBox HTB-Console Intro to binary exploitation track.. This was part of HackTheBox HTB-Console Intro to Binary Exploitation track.

Initial Analysis

I started by examining the provided binary file. It was a 64-bit executable and a “stripped binary,” meaning debugging information was removed, making it harder to find function addresses like main. I checked the security protections on the binary and found that NX (Non-eXecutable stack) was enabled. This prevents direct code execution on the stack.

Reverse Engineering with Ghidra

I opened the binary in Ghidra to understand its functionality. In the main function, I observed a variable Local 18 declared with 16 bytes. The program takes user input using fgets and stores it in Local 18. A subsequent function call takes Local 18 as an argument.

Inside this function, there were several if-else conditions checking the user input:

  • If the input was “flag”, the program prompted for another input, this time allowing up to 48 bytes to be stored in Local 18. This is where the buffer overflow vulnerability lies, as Local 18 was initially allocated only 16 bytes.
  • If the input was “Hof” (Hall of Fame), the program allowed the user to enter 16 bytes, which were stored at a specific memory address. This address became controllable.
  • Other inputs like “LS” printed predefined strings, and there was a system call that returned the date.

Triggering the Overflow and Finding the Offset

I launched the binary in GDB (GNU Debugger). To trigger the overflow, I first entered “flag”. Then, I provided an input of 50 bytes (though 48 bytes would have been exact) to cause a segmentation fault. By examining the stack pointer and base pointer, and using a pattern search tool, I determined the offset to be 24 bytes. This is the padding needed before overwriting the return address.

Building the ROP Chain

Since NX was enabled, I couldn’t directly execute shellcode on the stack. Instead, my goal was to redirect the execution flow to the winner function. I needed the memory address of the winner function, which I obtained earlier using Radare2 (afl).

I built the ROP chain with the following components:

  • Padding: The 24-byte offset found earlier.
  • Gadget Address: I used the ropgadget tool to find a “pop RDI; ret” gadget. This gadget would pop a value from the stack into the RDI register and then return. The RDI register is typically used to pass the first argument to a function.
  • /bin/sh Address: I needed a memory address that I could control to store the string “/bin/sh”. The “Hof” functionality provided this. By sending “Hof” and then “/bin/sh”, I stored “/bin/sh” at a known, controllable memory address.
  • System Call Address: The program already contained a system() call. I found its address in Ghidra.

Crafting and Sending the Exploit

I wrote an exploit script. The script first connected to the remote target. It sent “Hof” followed by “/bin/sh” to store the command string in the controllable memory location. Then, it sent “flag” to trigger the vulnerable input path.

The final payload was constructed as follows:

  • 24 bytes of padding.
  • The address of the “pop RDI; ret” gadget.
  • The memory address where “/bin/sh” was stored (this would be popped into RDI).
  • The address of the system() call (this would be the return address, so system("/bin/sh") would be executed).

The script sent this payload and then entered interactive mode, giving me a shell on the target. This approach allowed me to bypass the NX protection and gain control of the program’s execution flow to spawn a shell.

Technical Commands Used on the Terminal

  • gdb ./binary_name
  • pattern create 50
  • r
  • pattern search <value_from_stack_pointer_or_base_pointer>
  • ropgadget --binary ./binary_name
  • python exploit_script.py

Video Walkthrough

About the Author

Mastermind Study Notes is a group of talented authors and writers who are experienced and well-versed across different fields. The group is led by, Motasem Hamdan, who is a Cybersecurity content creator and YouTuber.

View Articles