oorrww (pwn)
I didn't manage to solve this challenge during the CTF, but upsolved it after looking at others' solutions!
As the challenge title suggests, this challenge involves opening, reading and writing, and likely involves doubles.
Running checksec on the binary, we get the following:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabledThe decompiled main function is given below:
undefined8 main(EVP_PKEY_CTX *param_1)
{
long in_FS_OFFSET;
int local_ac;
undefined local_a8 [152];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
init(param_1);
sandbox();
gifts(local_a8);
for (local_ac = 0; local_ac < 22; local_ac = local_ac + 1) {
puts("input:");
__isoc99_scanf("%lf",local_a8 + (local_ac << 3));
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}sandbox adds seccomp rules. When we do seccomp-tools dump ./oorrww, we see the following:
So we will have difficulty obtaining a shell. Looking at the gifts function:
We get the addresses of the variable local_a8 in the stack, and the address of a libc function. However, the twist is that we are getting the addresses as a float with 16 decimal places.
Afterwards, in main, we are asked to input a float value into local_a8 22 times. Each input will write 8 bytes. This leads to a buffer overflow because for our last input, we will be inputting into the address local_a8[168]. In other words, we can write 24 bytes past local_a8. The first 8 bytes will overwrite the stack canary, the next 8 bytes will overwrite RBP and the next 8 bytes will overwrite RIP.
Since we know the address of the stack, and we have limited space, we can do stack pivoting so we have more space for an ROP chain. Our ROP chain will be put into local_a8.
Start by defining a helper function which converts the given floats to longs, and another helper function which converts a long to a float in bytes. This is facilitated by the Python pack and unpack functions.
We start by receiving the stack address and scanf address, and converting them to longs to work with.
We can now start creating our payload. We will need a "flag.txt" string to use in our sys_open call. We start the payload by inputting the string which will be 8 bytes. Since it needs to be null-terminated, we use another 8 bytes for a block of zeroes.
Now we can start writing the sys_open part of the payload. We want rdi to be a pointer to flag.txt, and rsi and rdx to be 0. Since rdx is already 0, we only need to zero out rsi. We then call sys_open by calling syscall with rax = 2
Next we want to read. Instead of using syscall, I used read to save stack space. This way I don't need to set the rax value. Assuming the file descriptor of the opened flag.txt is 3, we want rdi = 3, rsi = *buf, rdx = count . For the value of rsi which is the buffer address, we ideally want an address that doesn't overlap the part of the stack we use for our ROP chain. For this I chose local_a8_addr - 0x100 since it is sufficiently far away and is still readable and writable.
Next we want to write from local_a8_addr - 0x100. To save space we call puts. We only need rdi to be that address, then call puts.
Now, we need to bypass the canary. We can do this by entering a character such as - which will leave the canary untouched. We then overwrite RBP to local_a8 + 8 so our execution continues right after the 8 bytes of zeroes, and overwrite RIP to be a leave ; ret instruction.
This should give us the flag. The full exploit code is given below.
Last updated