The return-to-csu (ret2csu) technique provides a universal gadget for whenever the program image itself does not contain many useful gadgets. See ret2csu for details on how it works in practice.
Requirements
- Program runs on Linux.
- Program is linked against libc.
For addresses mentioned in this guide, refer to the Ghidra disassembly of __libc_csu_init borrowed from the GitHub Gist, reproduced below.
We can extract two useful gadgets from the __libc_csu_init
:
- Caller gadget at
c0
(004033c0
) setsrdx
(r13) andrsi
(r12). In addition, it setsedi
atc6
, but this doesn’t help us set the fullrdi
, so we need to use apop rdi
gadget later. - Popper gadget at
d9
setsrbx
,rbp
,r12
,r13
,r14
, andr15
.
To build a ret2csu payload that calls a function with three arguments (stored in rdi
, rsi
, and rdx
):
- Return to the popper gadget to set registers. Initialize them properly so that the caller gadget does not misbehave.
- Return to the caller gadget to set
rdx
andrsi
.- Bypass call instruction. The
call [R15 + RBX*0x8]
atc8
is problematic, because we don’t want to call anything before havingrdi
ready (which will have to happen after the whole gadget). Therefore, we need to find an address of a pointer to_init
or_fini
usingx/50x &_DYNAMIC
in gdb so that the call doesn’t segfault or modify registers. - Beware of
rbx
andr14
comparison atd0
. Initialize them such that thejnz
instruction doesn’t branch. - Make sure there’s a garbage value on stack since
rsp
is incremented by the gadget atd5
. - The caller gadget doesn’t have a return address, so we continue onto the popper gadget again. Just put some garbage on the stack.
- Bypass call instruction. The
- Return to a
pop rdi; ret
gadget to setrdi
. - Return to the target function. Voila!