Space Heroes 2023: Rope Dancer
A circus has just opened near you and you would love to work there as a rope dancer. Perhaps you could access their recruitment criteria to maximize your chances of being selected?
Last updated
A circus has just opened near you and you would love to work there as a rope dancer. Perhaps you could access their recruitment criteria to maximize your chances of being selected?
Last updated
Stack Pivot from a limited initial buffer to a large .bss section, followed by SROP under a constraint environment of limited gadgets available.
ROPedancer is a job application program that allows 3 consecutive user inputs. The 1st question is a yes or no question, which we can safely ignore.
A buffer overflow occurs in the email field, leading to a segfault. The second input contains a larger buffer but reads in 400 characters exactly without an obvious BOF.
Not a single protection, so we don't have to worry about any info leaks other than LIBC.
The decompilation is almost unreadable because the entire program is written in assembly. However, we know that there is a small buffer overflow that occurs in the email field (16 bytes), which allows us to fit a small ROP chain there.
On our next input, we have a significantly larger buffer (500 bytes), but BOF does not occur here.
From the assembly above, we know that 500 bytes are allocated in motivation_letter
. This is coincidentally the start of the .bss section in our binary.
Our goal is to now build a small ROP chain in our initial buffer to stack pivot into the larger .bss section. Let's try to find a stack pivoting gadget using Ropper. This shows that we can control the value of rsp
if we have a precursor way to manipulate rbp
.
Turns out pop rbp
gadget exists! So we basically fulfill the whole requirement for Stack Pivoting.
We have just found a way to pivot into a large buffer via Stack Pivoting. Now let's focus on constructing an ROP chain that leads to a shell or flag.
Looking at the available gadgets, we have a very limited option of pop-based gadgets. The pop rdi
gadget used to perform a ret2libc
attack is no-where to be seen.
Since the program is written entirely in assembly, there's no dynamic linking of LIBC libraries, and therefore there won't be any GOT entries populated. At this point, ret2libc
is practically impossible as there's not a single GOT entry that we can leak to calculate the LIBC base + the lack of gadgets.
Under a constrained environment where there are a limited number of useful gadgets, we can utilize SROP to control all register values at once by spoofing a fake sigreturn frame. The downside of this is SROP requires a huge buffer size (>300 bytes). This is perhaps why the challenge was designed to have a 500 bytes buffer in the .bss section.
As Sigreturn is considered a special type of syscall, we would still require few gadgets to control the rax
registers where the syscall number is stored, outside of the spoofed sigreturn frame.
Putting everything together, we have a 3 stage payload:
Stage 1: Using the smaller buffer to fit an initial ROP chain to pivot to the larger .bss section.
Stage 2: Write /bin/sh
to the start of .bss section, followed by calling syscall 0xf
to invoke SIGROP.
Stage 3: Setup the sigreturn frame to call execve(/bin/sh)
.
Flag: Hero{1_w4nN4_b3_4_R0P3_D4nC3r_s0_b4d!!!}