3.9 KiB
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:
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:
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=f6c=l61=a67=g7b={
The value 0x7b67616c66 represents flag{ in reverse!
5. Putting it all Together
To solve the challenge:
- Connect to the service.
- Send many
%pspecifiers to leak the stack. - Identify the hex values that look like printable text (starting with
0x...and containing ASCII values). - Reverse the bytes (Endianness) and convert them to characters.
- 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!