ARKAVIDIA Quals
Last updated
Last updated
WWW 0
Binary Exploitation
996 pts
3
Namanya bukan "WWW 3" karena takutnya koch zafir udah bikin :D
Author: msfir
nc 20.195.43.216 8002
we're given an elf binary with protection as follows
thankfully we're also given the source code and it's quite small
in the dockerfile we can see that the challenge is hosted in alpine
upon further inspection it was then revealed that alpine uses musl for its libraries instead of the usual glibc which will affect the behaviour of our exploitation later on.
additionally, in the middle of the competition the author thankfully provided us with the debug library since the one within the docker doesn't contain debug symbol.
while POSIX specifies that positional parameters (like %1$p
) should be supported, musl deliberately does not implement them.
and after spamming as many %p
as I can into a 32 bytes buffer, nothing came out was from the lib's memory addresses. So the easiest way is to leak using the %s
and inserting one of the GOT's entry address into the stack as it guaranteed to contain one of the function address from the lib's memory region.
also good thing to note is that it seems the binary only loads the linker which acts both as the linker and the standard library (I think...) that implements most of the IO and other functions.
after some googling and reading, I found a similar FSOP technique to gain execution after exit.
from the above blog quote:
However, I offer another solution here: we can use FSOP in musl. The
FILE
struct inmusl
is similar to that inglibc
. The difference is thatmusl
does not use vtable, it keeps the pointers in the data structure. What we can do is to use the arbitrary allocation to overwrite itswrite
pointer withsystem
and replace itsflag
withE;sh;\x00
. Whenputs
is called, thewrite
function will be called with itsflag
as the first argument. After the overwite, it becomessystem("E;sh;\x00")
and gives us a shell. (“E;” is here because the original flag is 0x45(“E”), I want to preserve theflag
)
further googling leads me to this blog that details about the FSOP techniques in gaining code execution.
if you're familiar with glibc's FSOP, this one is simpler and have less security checks on them.
first is the FILE
structure, in musl they don't have a vtable rather the methods are directly within the structure itself as we can see in the definition below
on exit the, it will perform some cleanup on these files
so depending on the checks it will either calls write
or seek
to gain shell we need to write /bin/sh
to the start of the structure and overwrite one of the method then trigger said method.
which the current challenge present its problem, as it either requires a huge write buffer or multiple writes in order to be successfully executed.
if you notice from the source code, it continuously flushes stdout
which will empties both of rpos
and rend
.
this means seek
can only be triggered if we corrupt stdin and not the others.
meanwhile, write
will not be able to be triggered at all as wpos
and wbase
are also null when examined at exit
so we can only overwrites the stdin
's seek
to control execution flow. I then think we're able to gain multiple writes if we're able to go back to main
multiple times.
However this deemed to be fruitless as when we exit the first time, the main thread gets locked from the call to __libc_exit_fini();
and was never released. as the program exit for the second time, it will be a deadlock and will hang indefinitely.
so since we're able to control execution flow, I thought of using one_gadget
, however the tool fails to run on the library, I then tried every offset within execvpe
and system
addresses but nothing seems to work.
another national competition named Cyber Jawara held its qualifier back in January, there zafirr also created a WWW challenge which is similar in concept to this. both uses scanf
to take inputs and flushes stdout
but not stdin
the current author (msfir) solves that challenge by creating a fake file within the stdin's buffer in the heap and then uses the 8 write bytes to link the file such that when exit is called, the cleanup function will traverse all of the files and this includes the fake file which fully customised to gain shell.
in this challenge the same concept is applied, just with musl instead.
in glibc's implementation, all opened files are linked in a global head variable. in musl's implementation standard's IO are stored in global variable and not linked to subsequent opened files.
to examine the global head variable which contains linked files we need to examine the return value of __ofl_lock()
as can be seen, the head contains null pointer as the program doesn't load any of the or open any files. we can then write to this address of where our fake file will be located.
as for our fake file, it will be located within the stdin
's buffer and is pointed by rpos
and rend
.
further examining the code execution we can confirm that it will call close_file
with the crafted fake file as its argument and subsequently will call write
that has been substituted with system
.
here's the exploit being ran againts the remote server
here's the full exploit script:
Flag: ARKAV{maaf_koch_zafir_ini_challnya_cuma_terinspirasi_kok_bukan_plagiat_hehe}