Playing Hacks and Stuffs!
Hi h4cky0u
here, I participated with team M3V7R
and we placed first on the Africa scoreboard
I tackled mostly the pwn and rev challenges and this writeup contains the solution to them all
We are given the source code and binary
Reading the source code, in the main function
This would:
main
function addressThe program has a win function which would print the flag
We simply just need to jump to function
Here’s my solve script
Running it, we get the flag
Same as the previous challenge we are also provided with the source code and binary
Starting from the main function we see it calls the call_functions
function
Here’s what the function does
So first it:
This also has a win function
Our goal is to jump there yet again
But this time we are not given any memory leaks, and checking the protection enabled on the binary we get this
This means we need to leak some memory address
There’s an obvious format string bug since it uses printf
on our controlled buffer without using a format specifier
We will leverage that to get memory leaks
To calculate the offset where a pointer to the elf section is on the stack i’ll use gdb
Using gdb-pwndbg
and setting a breakpoint at b *call_functions+80
Here’s how the stack looks like
We can see at offset 19 holds a pointer to the elf
section
Now we calculate the offset of that address to the pie base
This means if we get the address at offset 19 and subtract it with 0x1441
that would give the pie base
And with that we can easily just jump to the win function address
Here’s my solve
Running it works
We are given an ssh instance to connect to and also an alternative command to copy the flaghasher
binary from the remote host to our local host
Let us first take a look at what the binary does on the remote instance
Running it we see it computes the md5 hash of the /root/flag.txt
file
We obviously can’t derive the content of the flag from just the hash so we need to some how figure a way around this
To copy the binary to our host we use this command
scp -P 53610 ctf-player@shape-facility.picoctf.net:~/flaghasher .
After the transfer, checking the file type shows it’s a 64 bits binary
Running strings on it we can infer it’s a c++ compiled binary
Using IDA to decompile here’s the main function
The program first prints a message to the terminal and pauses for two seconds. It then defines a command string containing /bin/bash -c 'md5sum /root/flag.txt'
. After that, it escalates privileges by setting both the group ID (gid) and user ID (uid) to 0. Finally, it executes the command using the system function.
This basically just calculates the md5sum
value of the file /root/flag.txt
The issue here is the way the md5sum
binary is being used, it doesn’t specify the full path thus we can hijack the binary so that it executes something else
The first step is to create a malicious md5sum
binary, i just did something really easy
Next we add our current directory to the environment PATH
variable
echo "cat /root/flag.txt > a.txt" > md5sum
chmod +x md5sum
export PATH=/home/ctf-player:$PATH
We can see that on executing the flaghasher
binary we get the flag!
Connecting to the ssh remote instance we get this
Every thing looks all good until i tried finding the suid binary flaghasher
and it shows this error:
-rbash: /dev/null: restricted: cannot redirect output
So it seems we are in a restricted shell called rbash
Looking it up for bypass we find that it can be easily bypassed using the -t "bash --noprofile"
argument
Doing that works
The flaghasher
binary is still present but just in a different directory
Executing it shows the same thing as the previous one
I just reused my previous solve for the path hijack and that worked
We’re given the source code and binary
Here’s the main function
It just calls the echo_valley
function which does this
This simply just keeps receiving our input and printing it out back until we give it exit
before it returns back to main
There’s also a win function
So our goal is simply to somehow call that function
The bug is an obvious format string bug
Looking at the protection on the binary we get this
We see that all protections are enabled
The first thing we would need are memory leaks
I leaked pie
and stack
The reason i leaked a stack address is because i’ll be overwriting the saved rip of the echo_valley
function to the print_flag
function
Setting a breakpoint at echo_valley+218
here’s what is on the stack
At offset 20 & 21
holds some stack and pie address
And that’s what i leaked
To calculate the saved rip address i set a breakpoint at echo_valley+249
then inputted exit
Now we look at the stack frame
Now we just subtract our leaked stack address with that of the saved rip
It’s just 8
With this we will leverage the format string bug by gaining arb write and our goal is to overwrite the saved rip to the win function
Here’s my solve
Running it works
We are given the source code and binary
First we’ll take a look at the protections on the binary
The binary literally has no protection enabled, okay looks good!
Running it shows this
It has just two main options we can choose from
Since we have the source code let’s take a look at it
First we have a struct called entry_t
that manages the name
and message
of the recipient
typedef struct entry {
char name[8];
char msg[64];
} entry_t;
I’ll look through just the important details
If we decide to add a new recipient it will read into entries[total_entries].name
32 bytes of data
If we decide to send a message it will receive the index of the the entries we want to store the message and read 64 bytes of data into the msg
field of the entry_t
struct
And finally when we choose exit it will ask for our feedback where it reads in up to 32 bytes of data to the feedback array, null terminates at index 7 and returns
Okay cool so now what’s the vulnerability?
Well there are some bugs here such as:
There’s also an overflow in add recipient but i wouldn’t consider it that much of a risk because even though it overwrites the msg
field when we attempt to add a message it will replace what we overwrote with our new message
The out of bound read occurs in the add message function since it doesn’t check if index
is less than 0
But this isn’t so much useful because there’s nothing really important before the entries as you can see from the stack view
Now the main bug which we’ll exploiting is the overflow in feedback
We see it’s defined as a char array of 8 bytes but we’re reading in 32 bytes of data to the array
At first it might look like an easy win here? but if you take a look at the stack view you’ll see this
-000000000000000C char feedback[8];
-0000000000000004 _DWORD total_entries;
+0000000000000000 _QWORD __saved_registers;
+0000000000000008 _UNKNOWN *__return_address;
The offset from the feedback array to the return address is:
This means the first 20 bytes will first fill up the feedback
array, the total_entries
and then the saved rbp
That effectively leaves us with roughly 8 bytes for rip control
What???
How do we pwn this with just 8 bytes of rip control
Things began getting tough at this point
I neglected the fact that NX
was disabled at first and that means the stack is rwx
During the time spent on trying to solve this challenge (about 5 hours or so) i attempted:
Then i decided to take a look at the rop gadgets again and got this
The one of interest is the jmp rax
Taking a look at the registers when it’s about to return shows this (set bp at vuln+485)
We see that rax
is a controllable buffer and that represents our feedback
array!
This means we can place shellcode as the feedback then set rip to the jmp rax
gadget effectively giving us shellcode execution
Phew! But now we know that, how can we spawn a shell?
Remember that the entries are actually stored on the stack! This means we can make rsp
point to entries[0]
which would hold our shellcode!
I calculated the offset between the current stack with where entries[0]
is stored to be 0x2e4
So with a sub rsp
instruction + a jmp rsp
instruction i pivoted the stack to our execve shellcode and got code execution!
Here’s my solve
Running it works
I’m yet to write about this but i solved all challenges in this category, I’m a bit tired but all my solves are here
The Binary Instrumentation 1 & 2 isn’t there because it’s windows rev which i solved by debugging on my windows host
But the idea behind the challenge 1 is that it will resolve some winapi functions and then executes a region of memory which holds some shellcode that later calls a function that is meant to print the flag
The issue is that it’s going to take a lot of time since it uses a sleep variant to make the program pause execution
Rather than patching the function call i just scrolled down a bit and saw the base64 encoded flag which is supposed to be printed to stdout after the sleep is done so i just decoded and i got the flag
This one really took me quite a lot of time (3 hours) the reason is i’m not so much familiar with windows reversing
So i really spent lot of time in the debugger
But the challenge itself basically also resolves some api and then executes some shellcode
The issue with this one is when it tries to perform some winapi call it fails for some reason :(
But before it exists i created a dump of the new memory region it creates
And decompiling it i got the flag, but you can as well just scroll down in the memory dump and you’ll see a suspicious base64 looking string
The other challenge which gave me issue was perplexed though to reverse it wasn’t so difficult but for some reason i wasn’t getting the last bytes to be printable
It really frustrated me and i ended up brute forcing using gdb scripting
This is my rant here:
And finally
GG to my teammates!