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
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! 🙂