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) sets rdx (r13) and rsi (r12). In addition, it sets edi at c6, but this doesn’t help us set the full rdi, so we need to use a pop rdi gadget later.
  • Popper gadget at d9 sets rbx, rbp, r12, r13, r14, and r15.

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 and rsi.
    • Bypass call instruction. The call [R15 + RBX*0x8] at c8 is problematic, because we don’t want to call anything before having rdi ready (which will have to happen after the whole gadget). Therefore, we need to find an address of a pointer to _init or _fini using x/50x &_DYNAMIC in gdb so that the call doesn’t segfault or modify registers.
    • Beware of rbx and r14 comparison at d0. Initialize them such that the jnz instruction doesn’t branch.
    • Make sure there’s a garbage value on stack since rsp is incremented by the gadget at d5.
    • The caller gadget doesn’t have a return address, so we continue onto the popper gadget again. Just put some garbage on the stack.
  • Return to a pop rdi; ret gadget to set rdi.
  • Return to the target function. Voila!
                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined __libc_csu_init()
             undefined         AL:1           <RETURN>
                             __libc_csu_init                                 XREF[4]:     Entry Point(*), 
                                                                                          _start:00401dfa(*), 
                                                                                          _start:00401dfa(*), 004ab2f0(*)  
        00403350 f3 0f 1e fa     ENDBR64
 
 
								 ** some instructions **
 
 
                             LAB_0040339d                                  
        0040339d 4c 8d 3d        LEA        R15,[__frame_dummy_init_array_entry]
                 5c 4c 0b 00
        004033a4 4c 8d 35        LEA        R14,[__do_global_dtors_aux_fini_array_entry]
                 65 4c 0b 00
        004033ab e8 50 dc        CALL       _init
                 ff ff
        004033b0 4d 29 fe        SUB        R14,R15
        004033b3 49 c1 fe 03     SAR        R14,0x3
        004033b7 74 1c           JZ         LAB_004033d5
        004033b9 31 db           XOR        EBX,EBX
        004033bb 0f 1f 44        NOP        dword ptr [RAX + RAX*0x1]
                 00 00
                             LAB_004033c0                                    
        004033c0 4c 89 ea        MOV        RDX,R13
        004033c3 4c 89 e6        MOV        RSI,R12
        004033c6 89 ef           MOV        EDI,EBP
        004033c8 41 ff 14 df     CALL       qword ptr [R15 + RBX*0x8] <-- 1
        004033cc 48 83 c3 01     ADD        RBX,0x1
        004033d0 49 39 de        CMP        R14,RBX                   <-- 2
        004033d3 75 eb           JNZ        LAB_004033c0 		      <-- 3
                             LAB_004033d5                         
        004033d5 48 83 c4 08     ADD        RSP,0x8                   <-- 4
        004033d9 5b              POP        RBX
        004033da 5d              POP        RBP
        004033db 41 5c           POP        R12
        004033dd 41 5d           POP        R13
        004033df 41 5e           POP        R14
        004033e1 41 5f           POP        R15
        004033e3 c3              RET                                  <-- 5