Added writeups
This commit is contained in:
105
echo_chamber.md
Normal file
105
echo_chamber.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Echo Chamber
|
||||
|
||||
Hello there! Welcome to the write-up for **Echo Chamber**. This is a classic "pwn" challenge that demonstrates a very common but powerful vulnerability: the **Format String Vulnerability**.
|
||||
|
||||
In this challenge, we are given a compiled binary. When we don't have the original source code, we use tools like **Ghidra** to decompile the binary and see what the program is doing "under the hood."
|
||||
|
||||
---
|
||||
|
||||
## 1. Initial Reconnaissance
|
||||
|
||||
When we run the program, it asks for some input and "echoes" it back:
|
||||
|
||||
```text
|
||||
Welcome to the Echo Chamber!
|
||||
Give me a phrase, and I will shout it back: Hello!
|
||||
You said: Hello!
|
||||
```
|
||||
|
||||
The developer's description gives us a hint:
|
||||
> "The developer claims it's perfectly secure because 'it doesn't execute any code, it just prints text.'"
|
||||
|
||||
This is a classic "famous last words" situation in security! Let's look at the decompiled code to see why.
|
||||
|
||||
## 2. Analyzing the Decompiled Code (Ghidra)
|
||||
|
||||
Opening the binary in Ghidra, we find the `vuln()` function. Here is the pseudo-code it gives us:
|
||||
|
||||
```c
|
||||
void vuln(void)
|
||||
{
|
||||
char acStack_a0 [64]; // Our input buffer
|
||||
char local_60 [72]; // Where the flag is stored
|
||||
FILE *local_18;
|
||||
|
||||
local_18 = fopen64("flag.txt","r");
|
||||
if (local_18 == (FILE *)0x0) {
|
||||
puts("Flag file is missing!");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// 1. The flag is read into local_60
|
||||
fgets(local_60,0x40,local_18);
|
||||
fclose(local_18);
|
||||
|
||||
puts("Welcome to the Echo Chamber!");
|
||||
printf("Give me a phrase, and I will shout it back: ");
|
||||
|
||||
// 2. Our input is read into acStack_a0
|
||||
fgets(acStack_a0,0x40,(FILE *)stdin);
|
||||
|
||||
printf("You said: ");
|
||||
// 3. VULNERABILITY: Our input is passed directly to printf!
|
||||
printf(acStack_a0);
|
||||
putchar(10);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
Do you see that line `printf(acStack_a0);`? This is our "Golden Ticket."
|
||||
|
||||
## 3. The Vulnerability: Format Strings
|
||||
|
||||
In C, `printf` expects its first argument to be a **format string** (like `"%s"` or `"Hello %s"`). If a developer passes user input directly to `printf`, the user can provide their own format specifiers.
|
||||
|
||||
When `printf` sees a specifier like `%p` (print pointer) or `%x` (print hex), it looks for the next argument on the **stack**. If we don't provide any arguments, `printf` will just start leaking whatever is already on the stack!
|
||||
|
||||
### Where is the flag?
|
||||
Looking at the Ghidra output, notice that both `acStack_a0` (our input) and `local_60` (the flag) are **local variables**. This means they are both stored on the stack right next to each other.
|
||||
|
||||
## 4. Exploiting the "Echo"
|
||||
|
||||
If we send a string of format specifiers like `%p %p %p %p %p %p...`, we can trick `printf` into printing the contents of the stack. Since the flag is sitting on the stack, it will eventually be printed!
|
||||
|
||||
Try providing this as input:
|
||||
`%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p`
|
||||
|
||||
The program will respond with a series of hex addresses. Some of these values will actually be the ASCII characters of our flag.
|
||||
|
||||
### Little Endianness
|
||||
When you see the hex values, remember that modern systems use **Little Endian** byte ordering. This means the bytes are stored in reverse order.
|
||||
|
||||
For example, if you see `0x7b67616c66`, and you convert those bytes from hex to ASCII:
|
||||
- `66` = `f`
|
||||
- `6c` = `l`
|
||||
- `61` = `a`
|
||||
- `67` = `g`
|
||||
- `7b` = `{`
|
||||
|
||||
The value `0x7b67616c66` represents `flag{` in reverse!
|
||||
|
||||
## 5. Putting it all Together
|
||||
|
||||
To solve the challenge:
|
||||
1. Connect to the service.
|
||||
2. Send many `%p` specifiers to leak the stack.
|
||||
3. Identify the hex values that look like printable text (starting with `0x...` and containing ASCII values).
|
||||
4. Reverse the bytes (Endianness) and convert them to characters.
|
||||
5. Combine the parts to find the flag!
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
Even if a program "just prints text," it's not safe if it uses `printf` incorrectly. The fix is simple: always use `printf("%s", buffer);`. This ensures the input is treated as a literal string, not as code or instructions for the function.
|
||||
|
||||
Happy Hacking!
|
||||
|
||||
Reference in New Issue
Block a user