Binary Exploitation
stack overflow, string format vulnerability
string format vulnerability, one byte overwrite
ret2shellcode
u get me write (400 pts)
Description
Surely one gets call wont get me fired right?
Solution
Given executable with following protection
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: NoNow decompile using IDA
The code is pretty simple and the vulnerability is easy to find which is gets. In this challenge we're not given libc so we need to find correct libc later.
First, we can do overflow in gets function and get control of RIP, but the problem is where should we go? Because there is no PIE, so we can go to any address in ELF because we know the static address of executable. But because the target is to read flag on system we need to gain RCE or read file on remote server.
My idea to gain RCE is by utilizing function in libc, which can be system/execve/one gadget/etc. So we need to leak libc address first. Let's take a look on assembly of function main.
There is printf call and gets, function gets will return the address of buffer in RAX. So if we set the RIP to 0x00000000040117B after retn function, the RAX still carry the address of buffer from gets. If we input %p in buffer so we can get string format vulnerability in printf because we can control the RDI of printf which moved from RAX.

Now we've confirmed that we can leak using printf, next we need to find useful value to leak. In x64 there are order of argument that we can use as reference
RSI
RDX
RCX
R8
R9
Values on stack
By examining above register and value on stack we found interesting value in RCX and $rsp+(19*0x8)
After leak we will call gets function again, but if our current payload rbp is set to 0x41414141.

To make gets working again we need to set a valid rbp value with writable region and we can easily find it through vmmap.
Above address is good candidate for RBP, let's update our solver.
Last, we can use one gadget to gain RCE

There are some constrains in each one gadget, most of it is required rbp-n writeable. So before jump to one gadget we need to set a valid rbp and we can utilize gadget from ELF which pop rbp; ret; . Following is final solver we used

Singular Hole (441 pts)
Description
surely one singular hole is easier to handle than many holes
Solution
Given executable with following protection
Decompile it with IDA
From above function we know 2 obvious vulnerabilities
string format vuln in printf (main)
We can utilize this vulnerability to read or write value
one byte overwrite in hole function
We can utilize this vulnerability to overwrite value (one byte)
Using printf we gonna try to leak useful values. Let's check register and stack
1st Leak = 0x00007fff6bf48188 (stack address)
rbp+0x8 is 0x7fff6bf48068, so we can leak $rbp+0x8 by substracting 1st leak with 288
0x7fff6bf48068 stored RIP after main function (value = 0x76471f02a1ca)
By overwriting value in 0x7fff6bf48068 we can change flow after main function call
2nd Leak = 0x76471f02a1ca (__libc_start_call_main+122)
so we can leak libc base by subtracting it with 122 and address of __libc_start_call_main
In this challenge we've limitation of printf which only 16 bytes, so it will hard to one shot overwrite with this limitation. Using hole function is same, it only overwrite 1 byte.
So my first idea is utilizing either printf or hole to make infinite looping, because we know the address $rbp+0x8 and we know the value is __libc_start_call_main+122 we can try to make the program looping again to calling main function again by changing the RIP to __libc_start_call_main+57. It only needs one byte overwrite, but if we use format p64(target) + format string payload it will exceeded 16 bytes.
To overcome this issue, the idea is utilizing the second fgets to store the address of $rbp+0x8 then just use %137x%k$hhn for k is location of $rbp+0x8. Because we can do infinite looping then we can utilize one byte overwrite to write any data we want.
If we take a look again on the code, actually we can do overwrite 3 bytes at a time (one loop) with following flow
two bytes overwrite in printf
one byte overwrite in hole
By combining above idea we can craft following exploit
use infinite looping to write following data
rbp+(0x8*2) = binsh
rbp+(0x8*3) = ret
rbp+(0x8*4) = system
use 3 bytes overwrite in last loop (because it has 3 bytes different)
rbp+0x8 = pop rdi; ret

another solution is by using stack pivot.
into the void (458 pts)
Description
void
Solution
This challenge was solved by @dmcr7 and i tried to reproduce it again after competition. Following is protection of the executable.
Let's decompile using IDA
The code is simple, so there is buffer overflow in buf. My friend noticed that this challenge should be able to be exploited using SROP (never heard about this before lol). Looking at following reference basically we can get RCE by utilizing rt_sigreturn syscall. Let's take a look on assembly of main function
Before leave instruction we can see that there is mov eax, 0xf and throug the reference we know that rt_sigreturn syscall is 0xf. So we can assume that this gadget should be used to trigger rt_sigreturn syscall later.
To achieve RCE we need "/bin/sh" string and there is no string like that in executable and we don't find any leak from the executable. But in this challenge we've overflow and imported read function, so basically we can write to anywhere we want. Let's find suitable region for writing "/bin/sh".
Buffer for read function need to be placed in RSI and we also found that there is POP RSI gadget in executable. Besides that there is another useful gadget which is POP RBP and we'll use it as our payload also.
Following is the whole idea of exploitation
write /bin/sh to writeable section (0x4040b0)
use POP RSI gadget to set 0x4040b0 as buffer
reuse 0x4040b0 as RBP
Because our last gadget is mov eax, 0xf and it followed by leave and retn we need a valid RBP to control the next execution
So before using last gadget, let's use POP RBP and fill RBP with 0x4040b0 then we can set RIP for last execution is on 0x4040b8
write last execution address to writeable section (0x4040b8)
write fake sigreturnframe to writeable section (0x4040b8 + 0x8)
Last limitation is there is no syscall in executable, but if we take a look on read function we can see that there is syscall instruction on it.

As we can see that address in GOT is only has different one byte with known syscall instruction, through the same overwrite flow we can overwrite GOT with our target which is 0x5f.

holes (483 pts)
Description
The worms have begun digging again. docker image is ubuntu:24.04
Note: Use the remote! The provided binary is only part of the challenge.
Solution
Given executatble with following protection
In this challenge we're given a service that can overwrite 2 bytes from the whole executable. During competition we focus on overwriting the code and ended up not being able to craft exploit with our defined vulnerability which is string format vulnerability.
After competition, we found that some team solved it by disabling NX. So let's try to reproduce it
Disabling NX only need 1 byte, so we have another 1 byte that can be used. Let's find the suitable code to make this challenge become a ret2shellcode challenge.
Our input stored in rbp+s, so if we take a look on code after _fgets there is only one function that process rbp+s which is printf. Knowing this information so we can try to modify code to either jmp rsi or jmp rax before mov eax, 0.
Before mov eax, 0 looks like there is no instruction that can be changed to jmp eax or jmp rsi. So let's take a look on _printf.
Basically we can see that there is jmp, let's check if it is possible to modify it to jmp rsi.
Looks like it is suitable, so we can modify 0x25 to 0xe6. Now let's create our exploit and patch plan.
First, modify STACK flags to 7 to make it executable.

Second, change jmp cs:printf_ptr to jmp rsi. Following is patch code for local binary
Last, we just need to send shellcode to the binary to gain RCE.

Last updated