Select Page

TL;DR: Returning to <__libc_csu_init+93> gives us control on %rsp. Additionally, returning to <__libc_csu_init+95> and <__libc_csu_init+97> let us control %rbp and %rsi, respectively, with shorter payloads.

  1. Introduction
  2. More “hidden” gadgets
  3. Conclusion


1. Introduction

In my last post, I found out that the last two bytes of __libc_csu_init is actually a pop rdi; ret gadget. After that I felt that I had already learned enough about this function/technique, so I decided to move on…until I encountered yet another good article on __libc_csu_init() today.

Let’s first take even another look at our two gadgets inside __libc_csu_init():

 0x0000000000400880 <+64>: mov rdx,r15
0x0000000000400883 <+67>: mov rsi,r14
0x0000000000400886 <+70>: mov edi,r13d
0x0000000000400889 <+73>: call QWORD PTR [r12+rbx*8]
 0x000000000040089a <+90>: pop rbx
0x000000000040089b <+91>: pop rbp
0x000000000040089c <+92>: pop r12
0x000000000040089e <+94>: pop r13
0x00000000004008a0 <+96>: pop r14
0x00000000004008a2 <+98>: pop r15
0x00000000004008a4 <+100>: ret

As we know, by using these two gadgets above we can control %rbx%rdx%rsi%rdi%rbp, %r12%r13%r14 and %r15. It turns out, though, by returning to <__libc_csu_init+93>, we can also control %rsp. Additionally, returning to <__libc_csu_init+95> and <__libc_csu_init+97> allow us to control %rbp and %rsi with shorter payload, respectively. Let’s see:


2. More “hidden” gadgets

First, let’s take a look at the hex representation of pop r12:

peilin@PWN:~/expr/__libc_csu_init$ cat > pop_r12.s
pop %r12
peilin@PWN:~/expr/__libc_csu_init$ as -o pop_r12.o pop_r12.s
peilin@PWN:~/expr/__libc_csu_init$ objdump -d –disassembler-options=intel-mnemonic pop_r12.o

pop_r12.o: file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
0: 41 5c pop r12

As shown above, pop r12 is 41 5c in hex. Then as you might have already guessed:

peilin@PWN:~/expr/__libc_csu_init$ cat > pop_rsp.s
pop %rsp
peilin@PWN:~/expr/__libc_csu_init$ as -o pop_rsp.o pop_rsp.s
peilin@PWN:~/expr/__libc_csu_init$ objdump -d –disassembler-options=intel-mnemonic pop_rsp.o

pop_rsp.o: file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
0: 5c pop rsp

pop rsp is 5c in hex. Simple enough.

This is why when we “traditionally” return to <__libc_csu_init+92> we get:

 0x55555555466c <__libc_csu_init+92>: pop r12
0x55555555466e <__libc_csu_init+94>: pop r13
0x555555554670 <__libc_csu_init+96>: pop r14
0x555555554672 <__libc_csu_init+98>: pop r15
0x555555554674 <__libc_csu_init+100>: ret

…and now when we return to <__libc_csu_init+93> we get:

 0x55555555466d <__libc_csu_init+93>: pop rsp
0x55555555466e <__libc_csu_init+94>: pop r13
0x555555554670 <__libc_csu_init+96>: pop r14
0x555555554672 <__libc_csu_init+98>: pop r15
0x555555554674 <__libc_csu_init+100>: ret

Similarly, pop r13 is 41 5d in hex, while pop rbp is 5d. pop r14 is 41 5e in hex, while pop rsi is 5e, which means if you return to <__libc_csu_init+95> and <__libc_csu_init+97>, you will get:

 0x55555555466f <__libc_csu_init+95>: pop rbp
0x555555554670 <__libc_csu_init+96>: pop r14
0x555555554672 <__libc_csu_init+98>: pop r15
0x555555554674 <__libc_csu_init+100>: ret

, and

 0x555555554671 <__libc_csu_init+97>: pop rsi
0x555555554672 <__libc_csu_init+98>: pop r15
0x555555554674 <__libc_csu_init+100>: ret

, respectively. They may not seem as interesting as that %rsp one, but having a shorter payload is nice, isn’t it?


3. Conclusion

By splicing instructions up, we’ve discovered even more “hidden” gadgets inside __libc_csu_init(). Time to move on! 🙂