<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 CTF 入門 – 1

    VSole2021-10-21 16:39:08

    01簡介

    內核 CTF 入門,主要參考 CTF-Wiki。

    02環境配置

    調試內核需要一個優秀的 gdb 插件,這里選用 gef。

    根據其他師傅描述,peda 和 pwndbg 在調試內核時會有很多玄學問題。

    pip3 install capstone unicorn keystone-engineroppergit clone https://github.com/hugsy/gef.gitechosource `pwd`/gef/gef.py >> ~/.gdbinit
    

    去清華源下載 Linux kernel 壓縮包并解壓:

    curl -O -Lhttps://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.9.8.tar.xzunxz linux-5.9.8.tar.xztar -xf linux-5.9.8.tar
    

    進入項目文件夾,進行 makefile 配置

    cd linux-5.9.8make menuconfig
    

    在其中勾選

    Kernel hacking -> Compile-time checks and compiler options ->Compile the kernel with debug infoKernel hacking -> Generic Kernel Debugging Instruments -> KGDB:kernel debugger
    

    之后保存配置并退出

    開始編譯內核(默認 32 位)

    make -j 8 bzImage
    

    不推薦直接 make -j 8,因為它會編譯很多很多大概率用不上的東西。

    這里有些小坑:

    缺失依賴項。解決方法:根據 make 的報錯信息來安裝依賴項。

    sudo apt-get install libelf-dev
    

    make[1]: *** No rule to make target 'debian/certs/debian-uefi-certs.pem',needed by 'certs/x509_certificate_list'. Stop.解決方法:將 .config 中的 CONFIG_SYSTEM_TRUSTED_KEYS 內容置空,然后重新 make。

    ## Certificates for signature checking#CONFIG_SYSTEM_TRUSTED_KEYS=""#置空,不要刪除當前條目
    

    等出現了以下信息后則編譯完成:

    Setup is 15420 bytes (padded to 15872 bytes).System is 5520 kBCRC 70701790Kernel: arch/x86/boot/bzImage is ready (#2)
    

    最后在啟動內核前,先構建一個文件系統,否則內核會因為沒有文件系統而報錯:

    Kernel panic - not syncing: VFS: Unable to mount root fs onunknown-block(0,0)
    

    首先下載一下 busybox 源代碼:

    wgethttps://busybox.net/downloads/busybox-1.34.1.tar.bz2tar -jxf busybox-1.34.1.tar.bz2
    

    之后配置 makefile:

    cd busybox-1.34.1make menuconfigmake -j 8
    

    在 menuconfig 頁面中,

    Setttings 選中 Build static binary (no shared libs), 使其編譯成靜態鏈接的文件(因為 kernel 不提供 libc)需要注意的是,靜態編譯與鏈接需要額外安裝一個依賴項 glibc-static。使用以下命令安裝:

    # redhat/centos系列安裝:sudo yum install glibc-static# debian/ubuntu系列安裝sudo apt-get install libc6-dev
    

    在 Linux System Utilities 中取消選中 Supportmounting NFS file systems on Linux < 2.6.23 (NEW)

    當前版本默認沒有選中該項,因此可以跳過。

    編譯完成后,使用 make install命令,將生成文件夾_install,該目錄將成為我們的 rootfs。

    接下來在 _install 文件夾下執行以創建一系列文件:

    mkdir -p  procsys dev etc/init.d
    

    之后,在 rootfs 下(即 _install 文件夾下)編寫以下 init 掛載腳本:

    #!/bin/shecho"INIT SCRIPT"mkdir /tmpmount -t proc none /procmount -t sysfs none /sysmount -t devtmpfs none /devmount -t debugfs none /sys/kernel/debugmount -t tmpfs none /tmpecho -e "Boot took $(cut -d' ' -f1/proc/uptime) seconds"setsid /bin/cttyhack setuidgid 1000 /bin/sh
    

    最后設置 init 腳本的權限,并將 rootfs 打包:

    chmod +x ./init# 打包命令find . | cpio -o --format=newc >../../rootfs.img# 解包命令# cpio -idmv < rootfs.img
    

    busybox的編譯與安裝在構建 rootfs 中不是必須的,但還是強烈建議構建 busybox,因為它提供了非常多的有用工具來輔助使用 kernel。

    使用 qemu 啟動內核。以下是 CTF wiki 推薦的啟動參數:

    #!/bin/shqemu-system-x86_64 \    -m 64M\   -nographic \    -kernel./arch/x86/boot/bzImage \   -initrd  ./rootfs.img \    -append"root=/dev/ram rw console=ttyS0 oops=panicpanic=1 nokaslr" \    -smpcores=2,threads=1 \    -cpukvm64
    

    本著減少參數設置的目的,這是筆者的啟動參數:

    qemu-system-x86_64 \-kernel ./arch/x86/boot/bzImage \-initrd ./rootfs.img \-append "nokaslr"
    

    減少啟動的參數個數,可以讓我們在入門時,暫時屏蔽掉一些不必要的細節。

    這里只設置了三個參數,其中:

    -kernel 指定內核鏡像文件 bzImage 路徑

    -initrd 設置內核啟動的內存文件系統

    -append "nokaslr" 關閉 Kernel ALSR 以便于調試內核注意:nokaslr 可 千萬千萬千萬別打成 nokalsr 了。就因為這個我調試了一個下午的 kernel……

    是的 CTF Wiki 上的 nokaslr 也是錯的,它打成了 nokalsr (xs)

    啟動好后就可以使用內置的 shell 了。

    03

    內核驅動的編寫與調試

    1.構建過程

    這里我們在 linux kernel 項目包下新建了一個文件夾:

    linux-5.9.8 $ mkdir mydrivers
    

    之后在該文件夾下放入一個驅動代碼ko_test.c,代碼照搬的 CTF-wiki:

    #include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>MODULE_LICENSE("DualBSD/GPL");staticint ko_test_init(void){printk("This is a testko!\n");return0;}staticvoid ko_test_exit(void){printk("ByeBye~\n");}module_init(ko_test_init);module_exit(ko_test_exit);
    

    代碼編寫完成后,放入一個 Makefile文件:

    # 指定聲稱哪些內核模塊obj-m += ko_test.o
    # 指定內核項目路徑KDIR =/usr/class/kernel_pwn/linux-5.9.8
    all:# -C 參數指定進入內核項目路徑# -M 指定驅動源碼的環境,使 Makefile 在構建模塊之前返回到驅動源碼目錄,并在該目錄中生成驅動模塊$(MAKE) -C $(KDIR) M=$(PWD) modules
    clean:rm -rf *.o *.ko *.mod.* *.symvers *.order
    

    注意點:

    1.Makefile 文件名中的首字母 M 一定是大寫,否則會報以下錯誤:

    scripts/Makefile.build:44:/usr/class/kernel_pwn/linux-5.9.8/mydrivers/Makefile: No such file or directorymake[2]: *** No rule to make target '/usr/class/kernel_pwn/linux-5.9.8/mydrivers/Makefile'.  Stop.
    

    2.Makefile 中 obj-m 要與剛剛的驅動代碼文件名所對應,否則會報以下錯誤:

    make[2]: *** No rule to make target '/usr/class/kernel_pwn/linux-5.9.8/mydrivers/ko_test.o', needed by '/usr/class/kernel_pwn/linux-5.9.8/mydrivers/ko_test.mod'.  Stop.
    

    3.如果make時遇到以下錯誤:

    makefile:6: *** missing separator.  Stop.
    

    則使用 vim 打開Makefile,鍵入 i 以進入輸入模式,然后替換掉 make 命令前的前導空格為 tab,最后鍵入 :wq 保存修改。

    最后使用 make 即可編譯驅動。完成后的目錄內容如下所示:

    這里我們只關注 ko_test.ko

    $ tree                 .├── ko_test.c├── ko_test.ko├── ko_test.mod├── ko_test.mod.c├── ko_test.mod.o├── ko_test.o├── Makefile├── modules.order└── Module.symvers
    0 directories, 9 files
    

    2.運行過程

    將新編譯出來的 *.ko 文件復制進 rootfs 文件夾(busybox-1.34.1/_install)下,

    之后修改 busybox-1.34.1/_install/init 腳本中的內容:

    這里需要提權 /bin/sh,目的是為了使用 root 權限啟動 /bin/sh,使得擁有執行 dmesg 命令的權限。

    #!/bin/shecho "INIT SCRIPT"mkdir /tmpmount -t proc none /procmount -t sysfs none /sysmount -t devtmpfs none /devmount -t debugfs none /sys/kernel/debugmount -t tmpfs none /tmp+ insmod /ko_test.ko # 掛載內核模塊echo -e "Boot took $(cut -d' ' -f1/proc/uptime) seconds"setsid /bin/cttyhack setuidgid 1000 /bin/sh+ setsid /bin/cttyhack setuidgid 0 /bin/sh # 修改uid gid 為0 以提權/bin/sh 至root。+ poweroff -f # 設置 shell 退出后則關閉機器
    

    重新打包 rootfs 并運行 qemu,之后鍵入 dmesg 命令即可看到 ko_test 模塊已被成功加載:

    正常情況下,執行 qemu 會彈出一個小框 GUI。若想像上圖一樣將啟動的界面變成當前終端,則需在 qemu 啟動時額外指定參數:

    -nographic-append"console=ttyS0"
    

    3.調試過程

    a.attach qemu

    調試時最好使用 root 權限執行 /bin/sh,相關修改方法已經在上面說明,此處暫且不表。

    在啟動 qemu 時,額外指定參數 -gdb tcp::1234 (或者等價的-s),之后 qemu 將做好 gdb attach 的準備。如果希望 qemu 啟動后立即掛起,則必須附帶 -S 參數。

    同時,調試內核時,為了加載 vmlinux 符號表,必須額外指定 -append"nokaslr"以關閉 kernel ASLR。這樣符號表才能正確的對應至內存中的指定位置,否則將無法給目標函數下斷點。

    qemu啟動后,必須另起一個終端,鍵入 gdb -q -ex"target remote localhost:1234",即可 attach 至 qemu上。

    gdb attach 上 qemu 后,可以加載 vmlinux 符號表、給特定函數下斷點,并輸入 continue 以執行至目標函數處。

    # qemu 指定 -S 參數后掛起,此時在gdb鍵入以下命令gef> add-symbol-file vmlinuxgef> b start_kernelgef> continue
    [Breakpoint 1, start_kernel () atinit/main.c:837]......
    

    對于內核中的各個符號來說,我們也可以通過以下命令來查看一些符號在內存中的加載地址:

    # grep <symbol_name> /proc/kalsymsgrep prepare_kernel_cred  /proc/kallsymsgrep commit_creds /proc/kallsymsgrep ko_test_init /proc/kallsyms
    

    坑點1:之前筆者編寫了以下 shell 腳本:

    # 其他設置[...]# **后臺**啟動qemuqemu-system-x86_64 [other args] &# 直接在當前終端打開 GDBgdb -q -ex "targetremote localhost:1234"
    

    但在執行腳本時,當筆者在 GDB 中鍵入 Ctrl+C 時, SIGINT 信號將直接終止 qemu 而不是掛起內部的 kernel。因此,gdb必須在另一個終端啟動才可以正常處理 Ctrl+C。

    正確的腳本如下:

    # 其他設置[...]# **后臺**啟動qemuqemu-system-x86_64 [other args] &# 開啟新終端,在新終端中打開 GDBgnome-terminal -e 'gdb-q -ex "target remote localhost:1234"'
    

    坑點2:對于 gdb gef插件來說,最好不要使用常規的target remote localhost:1234語句(無需root權限)來連接遠程,否則會報以下錯誤:

    gef?  target remote localhost:1234Remote debugging using localhost:1234warning: No executable has been specified andtarget does not supportdetermining executable automatically.  Try using the "file"command.0x000000000000fff0 in ?? ()[ Legend: Modified register | Code | Heap | Stack| String ]──────────────────────────────────── registers────────────────────────────────────[!] Command 'context' failed to execute properly, reason: 'NoneType' object has no attribute 'all_registers'
    

    與之相對的,使用效果更好的 gef-remote 命令(需要root權限)連接 qemu:

    # 一定要提前指定架構set architecturei386:x86-64gef-remote --qemu-mode localhost:1234
    

    坑點3:如果 qemu 斷在 start_kernel時 gef 報錯:

    [!] Command 'context' failed to execute properly, reason: max() arg is an empty sequence
    

    直接單步 ni 一下即可。

    b.attach drivers

    1)常規步驟

    首先,將目標驅動加載進內核中:

    insmod <driver_module_name>
    

    之后,通過以下命令查看 qemu 中內核驅動的 text 段的裝載基地址:

    # 查看裝載驅動lsmod# 獲取驅動加載的基地址grep <target_module_name> /proc/modules
    

    在 gdb 窗口中,鍵入以下命令以加載調試符號:

    add-symbol-file mydrivers/ko_test.ko<ko_test_base_addr> [-s <section1_name> <section1_addr>] ...
    

    注,與 vmlinux 不同,使用add-symbol-file 加載內核模塊符號時,必須指定內核模塊的 text 段基地址。

    因為內核位于眾所周知的虛擬地址(該地址與 vmlinux elf 文件的加載地址相同),但內核模塊只是一個存檔,不存在有效加載地址,只能等到內核加載器分配內存并決定在哪里加載此模塊的每個可加載部分。因此在加載內核模塊前,我們無法得知內核模塊將會加載到哪塊內存上。故將符號文件加載進 gdb 時,我們必須盡可能顯式指定每個 section 的地址。

    需要注意的是,加載符號文件時,越多指定每個 section 的地址越好。否則如果只單獨指定了 .text 段的基地址,則有可能在給函數下斷點時斷不下來,非常影響調試。

    如何查看目標內核模塊的各個 section 加載首地址呢?請執行以下命令:

    grep "0x" /sys/module/ko_test/sections/.*
    

    2)例子

    一個小小例子:調試 ko_test.ko 的步驟如下:

    首先在 qemu 中的 kernel shell 執行以下命令

    # 首先裝載 ko_test 進內核中insmod /ko_test.ko# 查看當前 ko_test 裝載的地址grep ko_test /proc/modulesgrep "0x" /sys/module/ko_test/sections/.*
    

    輸出如下:

    記錄下這些地址,之后進入 gdb 中,先按下 Ctrl+C 斷下 kernel,然后鍵入以下命令:

    # 將對應符號加載至該地址處add-symbol-file mydrivers/ko_test.ko  0xffffffffc0002000 \                    -s .rodata.str1.10xffffffffc000304c \                    -s .symtab        0xffffffffc0007000 \                    -s .text.unlikely0xffffffffc0002000# 下斷點b ko_test_initb ko_test_exit# 使其繼續執行continue
    

    最后回到 qemu 中,在 kernel shell 中執行以下命令:

    # 卸載 ko_testrmmod ko_tes
    

    此時 gdb 會斷到 ko_test_exit 中:

    如果在卸載了ko_test后,又重新加載 ko_test,

    insmod ko_test
    

    則 gdb 會立即斷到 ko_test_init 中:

    這可能是因為指定了 nokaslr,使得相同驅動多次加載的基地址是一致的。

    上面調試 kernel module 的 init 函數方法算是一個小 trick,它利用了 noaslr 環境下相同驅動重新加載的基地址一致 的原理來下斷。但最為正確的調試 init 函數的方式,還是得跟蹤 do_init_module 函數的控制流來獲取基地址。以下是一系列相關操作步驟:

    跟蹤 do_init_module 函數是因為它在 load_module 函數中被調用。load_module函數將在完成大量的內存加載工作后,最后進入do_init_module 函數中執行內核模塊的 init 函數,并在其中進行善后工作。

    load_module函數將被作為 SYSCALL函數的 init_module調用。

    首先讓 kernel 跑飛,等到 kernel 加載完成,shell 界面顯示后,gdb 按下 ctrl + C 斷下,給 do_init_module函數下斷。該函數的前半部分將會執行內核模塊的 init 函數:

    /* * This iswhere the real work happens. * * Keep ituninlined to provide a reliable breakpoint target, e.g. for the gdb * helpercommand 'lx-symbols'. */staticnoinline int do_init_module(struct module *mod){  [...]  /* Start the module */  if(mod->init != NULL)    ret =do_one_initcall(mod->init);   // <- 此處執行 ko_test_init 函數  if(ret < 0) {    gotofail_free_freeinit;  }  [...]}
    

    gdb 鍵入 continue 再讓 kernel 跑飛。之后kernel shell 中輸入 insmod/ko_test.ko裝載內核模塊,此時gdb會斷下。在 gdb 中查看 mod->init 成員即可查看到 kernel moduleinit 函數的首地址。

    要想看到當前 kernel module 的全部 section 地址,可以在 gdb 中鍵入以下命令

    # 查看當前 module 的 sections 個數p mod->sect_attrs->nsections# 查看第 3 個section 信息p mod->sect_attrs->attrs[2]
    

    有了當前內核模塊的全部 section 名稱與基地址后,就可以按照之前的方法來加載符號文件了。

    c.啟動腳本

    配環境真是一件麻煩到極點的事情,不過目前就到此為止了?

    筆者將一系列啟動命令整合成了一個 shell 腳本,方便一鍵運行:

    #! /bin/bash
    # 判斷當前權限是否為 root,需要高權限以執行 gef-remote --qemu-modeuser=$(env | grep "^USER" | cut -d "="-f 2)if [ "$user" != "root"  ]thenecho"請使用root 權限執行"exitfi
    # 復制驅動至 rootfscp ./mydrivers/*.ko busybox-1.34.1/_install
    # 構建 rootfspushdbusybox-1.34.1/_installfind . | cpio -o --format=newc >../../rootfs.imgpopd
    # 啟動 qemuqemu-system-x86_64 \-kernel ./arch/x86/boot/bzImage \-initrd ./rootfs.img \-append "nokaslr" \-s  \-S&
    # -s :等價于-gdb tcp::1234,指定qemu 的調試鏈接# -S :指定 qemu 啟動后立即掛起
    # -nographic                # 關閉QEMU 圖形界面# -append "console=ttyS0"   # 和 -nographic 一起使用,啟動的界面就變成了當前終端
    gnome-terminal -e 'gdb-x mygdbinit'
    

    gdbinit 內容如下:

    set architecturei386:x86-64add-symbol-file vmlinuxgef-remote --qemu-mode localhost:1234
    b start_kernelc
    
    qemugdb命令
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    QEMU逃逸系列
    2022-12-01 09:19:27
    qemu用于模擬設備運行,而qemu逃逸漏洞多發于模擬pci設備中,漏洞形成一般是修改qemu-system代碼,所以漏洞存在于qemu-system文件內。而逃逸就是指利用漏洞從qemu-system模擬的這個小系統逃到主機內,從而在linux主機內達到命令執行的目的。
    00 前言在 HWS2021 入營選拔比賽的時候,遇到了一道 QEMU 逃逸的題目,那個時候就直接莽上去分析了一通,東拼西湊的把 EXP 寫了出來。但是 QEMU 逃逸這部分的內容實在是比較復雜,而且涉及到了很多我完全沒有了解過的知識,所以一直鴿到了現在。System mode:系統模式,在這種模式下,QEMU 可以模擬出一個完整的計算機系統。
    Kernel pwn CTF 入門 – 1
    2021-10-21 16:39:08
    01簡介內核 CTF 入門,主要參考 CTF-Wiki。02環境配置調試內核需要一個優秀的 gdb 插件,這里選用 gef。
    ARM PWN基礎教程
    2022-07-27 17:29:43
    在CTF比賽中,我們所能接觸到的大部分都是x86 x86_64架構的題目,而在我開始接觸IOT方向的研究以后發現智能設備所用到的則是ARM和MIPS架構為主。本篇文章在介紹前置知識的基礎上通過CTF的ARM架構類型題帶讀者更好的入門ARM PWN的世界。
    Kernel PWN從入門到提升
    2023-03-23 10:17:57
    所以我決定用此文章結合一道不錯的例題盡可能詳細的來講一下kernel pwn從入門過渡到較高難度的部分,供想要學習kernel pwn的小伙伴們參考。文件系統kernel題一般都會給出一個打包好的文件系統,因此需要掌握常用到的打包/解包命令
    感覺算是一個很不錯的IoT固件分析入門教程,今天收到《路由器0day》后在路上粗略地看了下目錄,除了沒有涉及到硬件外,這個教程差不多把固件分析的起始工作都涉及到了。
    漏洞復現根據官方公告,找到存在漏洞的二進制文件。官方公告:先用binwalk -Me DIR815A1_FW102b06.bin命令解壓固件包,再根據“漏洞描述”中的關鍵詞service.cgi進行查找:找到了所匹配的二進制文件htdocs/cgibin,將其拖進IDA中先進行靜態分析。
    關于MIPS架構的寄存器及指令集請自行查閱資料,這里就不多作介紹了。,則也會在prologue處保存下來,并在epilogue處取出。流水線指令集相關特性MIPS架構存在“流水線效應”,簡單來說,就是本應該順序執行的幾條命令卻同時執行了,其還存在緩存不一致性(cache
    但是最終因為我們的主機名與固件中的主機名不同所以無法獲取到IP地址。這里我們可以通過hostname命令查看本機名,然后以我的本機名為例修改squashfs-root/etc/hosts中的內容echo?驗證成功,可以看到程序崩潰信息。但是要構造ROP還需要一些gadget,使用ropper搜索
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类