HTB Challenge: pwnshop
We just opened a Pwn Shop, time to pwn all the things!
Last updated
We just opened a Pwn Shop, time to pwn all the things!
Last updated
Exploit a null-byte overwrite to leak PIE base, followed by stack pivoting to execute a ret2libc ROP chain under limited buffer size.
Pwnshop is a typical menu-style challenge that allows us to buy and sell items.
Inspecting the binary protection shows that Partial RELRO is enabled, meaning GOT overwrite is possible. Another thing to take note is PIE is enabled as well, meaning we would need an info leak to retrieve both LIBC and PIE addresses.
There's an obvious buffer overflow in option 1, but we have a limited space of 8 bytes to fit our payload. This means there's a high chance we may need to perform Stack Pivoting. However, there isn't any other buffer size available to us at the moment, other than the current one we have.
According to ir0nstone, pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
is an ideal gadget for Stack Pivoting due to its ability to manipulate rsp
to point to anywhere we have control over. However, recall that a 2nd buffer size that can accommodate our payload isn't present in the binary, so we can't really make use of this gadget.
Despite that, there is another gadget sub rsp, 0x28
that allows us to increase the size of our current buffer. Since the stack grows from High Address to Low Address, an sub rsp, 0x28
instruction means we are expanding/allocating memory on the stack, just like a function prologue.
As shown below, pwntool's find_gadget()
function only supports resolving pop-based gadgets dynamically. Hence, we'll need to hardcode the offset of stack pivot gadget 0x1219
relative to the PIE base, in our script later on.
There isn't any ret2win
function embedded in the program, so we'll need to perform an ret2libc
attack. Before we leak the LIBC address, it is a prerequisite for us to obtain the PIE base first. There are two reasons why we need it:
PIE base is needed to call elf.plt
and elf.got
from the binary during a LIBC leak.
The stack pivot gadget is available to us in the form of an offset. A PIE base is required to calculate its actual address.
Since strings are null-terminated in C, reading exactly 8 bytes into local_28
is going to overwrite the null byte sitting in between variables local_28
and *local_20
. As a result of that, when printf()
is executed, it's going to read past the boundary of local_28
until a null byte is found, then output the whole data as a string. This is the 1st info leak we have control over.
Using 8 A's as our user input successfully produced an info leak.
There are many theories to explain what this secret value may be:
Could it be a canary value that was not detected by checksec? Perhaps a custom canary implementation similar to NahamCON CTF 2023: Weird Cookie challenge?
The answer is: No. There are no signs of canary validation like __stack_chk_fail()
or secret == canary
checks.
Could it be the LIBC leak that we were looking for?
The answer is: No. We can easily decode this value to its hex representation. If it were to be a LIBC leak, the prefix would start with 0x7f
.
Could it be the PIE leak that we were looking for?
The answer is: Yes, since PIE base generally starts with the prefix 0x5
as a general rule of thumb.
Putting everything together, we have a 3 stage payload:
Stage 1: Using Option 2 - Null byte leak to obtain PIE base.
Stage 2: Using Option 1 - BOF + Stack Pivoting to leak LIBC then ROP back to main().
Stage 3: Using Option 1 - BOF + Stack Pivoting to ret2libc.
Flag: HTB{th1s_is_wh@t_I_c@ll_a_g00d_d3a1!}