CVE-2019-5016
An exploitable arbitrary memory read vulnerability exists in the KCodes NetUSB.ko kernel module which enables the ReadySHARE Printer functionality of at least two NETGEAR Nighthawk Routers and potentially several other vendors/products. A specially crafted index value can cause an invalid memory read, resulting in a denial of service or remote information disclosure. An unauthenticated attacker can send a crafted packet on the local network to trigger this vulnerability.
NETGEAR Nighthawk AC3200 (R8000) Firmware Version V1.0.4.28_10.1.54 (11/7/18) - NetUSB.ko 1.0.2.66 NETGEAR Nighthawk AC3000 (R7900) Firmware Version V1.0.3.8_10.0.37 (11/1/18) - NetUSB.ko 1.0.2.69
http://www.kcodes.com https://www.netgear.com/home/products/networking/wifi-routers/R7900.aspx https://www.netgear.com/home/products/networking/wifi-routers/R8000.aspx https://www.netgear.com/support/product/ReadySHARE_USB_Printer.aspx
10.0 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:H
CWE-200: Information Exposure
Some NETGEAR routers utilize a bespoke kernel module called NetUSB.ko from a Taiwanese company called KCodes. This module is custom-made for each device, but contains similar functionality. The module shares USB devices over TCP, allowing clients to use various vendor-made drivers and software to connect to these devices in such a way that the client machine treats the remote device as a local USB device plugged into their computer. The software used for NETGEAR routers is called NETGEAR USB Control Center that utilizes a driver called NetUSBUDSTcpBus.sys (on Windows) for communications. Many other products use NetUSB.ko. Unfortunately, we are unable to test so many potential devices. A previously disclosed vulnerability in 2015 lead researches to believe a flaw in this very kernel module potentially existed in as many as 92 products across multiple vendors. For this analysis, we utilized the R8000 hardware to test the R8000 version of NetUSB.ko (1.0.2.66) and the R7900 version (1.0.2.69) since both modules are compiled for the same kernel.
When establishing communications to a remote USB device, a handshake must first occur. The handshake works as follows:
The client send bytes “0x5605” followed by 16-bytes of “random” data
00000000 56 05 V.
00000002 81 77 d9 74 f8 9f b4 74 ee 94 02 07 0d b1 38 40 .w.t...t ......8@
The server (router) will encrypt those bytes using the static AES key 5c130b59d26242649ed488382d5eaecc
, which is hard-coded into NetUSB.ko and retrieved by decrypting 0B7928FF6A76223C21A3B794084E1CAD
with the key A2353556541CFE44EC468248064DE66C
.
After encrypting the client data, the encrypted response is sent back to the client along with 16 bytes of “random” data for the client to encrypt and return. 00000000 29 9e 4d 05 f6 d0 9d 98 ae 7f 81 35 39 83 67 13 ).M….. …59.g. // Client data encrypted 00000010 af 60 20 6b fa 52 7d 90 82 a3 82 63 f4 b1 14 c1 .` k.R}. …c…. // Server data to be encrypted and returned
The client responds by encrypting the “random” server data using the same AES key followed by the length of the client’s computer name as four bytes in little-endian format. Following this is the computer name itself and the synch command 0x7, which is also in little-endian format and four bytes in length.
00000012 2c b7 34 c6 32 e3 70 0e 26 bc 66 48 b8 7b c1 1c ,.4.2.p. &.fH.{.. // Encrypted server data
00000022 0c 00 00 00 .... // Computer name length
00000026 57 49 4e 44 4f 57 53 50 43 2d 30 31 WINDOWSP C-01 // Computer name
00000032 07 00 00 00 .... // Sync Command
The handshake is completed when the server responds with 0x17 (0x15 means a device is unavailable):
00000020 17 00 00 00 .... // Connection Established
To actually use the device and establish a remote TCP Bus, a series of opcodes and data are sent to the server. The second byte when establishing such communications is a device number or index into a list of available shared devices on the stack. This byte is not validated and allows a remote attacker to trigger the module to attempt to read arbitrary memory, eventually leading to a remote memory leak. In this example, we are calling ‘getConfigDescriptor’ using opcode 0x2
.
The vulnerable code :
00007E30 LDRB R3, [R4,#1] ; R4 is our command buffer, R3 is set with our index value
00007E34 ADD R3, R6, R3,LSL#2 ; add index*4 to the r6 (sbus address -- holds our session info) this is where we can use a malicious index (0x8a) to instead point this buffer to our hostname in the sbus buffer
00007E38 LDR R5, [R3,#0x2C] ; increment the sbus address by 0x2c
00007E3C CMP R5, #0 ; Ensure value is not NULL
00007E40 BNE loc_7E68 ; Br
00007E68 LDR R7, [R5,#0x44] R5 now points to our hostname buffer + 0x44
[snip]
00007E84 BL getConfigDescriptor ;
[snip]
00003960 getConfigDescriptor
[snip]
00003A48 LDR R3, [R5,#0x1B4] ; dereference [R5+0x44] pointer
00003A4C LDR R3, [R3,R7,LSL#2] ; R7 we control with the 3rd byte in our command, we leave this at 0.
00003A50 LDRB R2, [R3,#2] ; R2 = 3rd byte of [R3]
00003A54 LDRB R3, [R3,#3] ; R3 = 4th byte of [R3]
00003A58 ORR R2, R2, R3,LSL#8 ; R2 = R2 | R3 * 256 -- this is the memcpy size if < 0x9
00003A5C LDR R3, [R5,#0x1B4] ; Again dereference [r5+0x44] pointer
00003A60 CMP R6, R2 ; compare R6 (descriptor length -- hardcoded 0x9) to our R2 value obtained at 0x3a58
00003A64 MOVGE R0, R4
00003A68 MOVLT R0, R4 ; This destination is the data buffer that will be sent back to the client
00003A6C MOVLT R2, R6 ; If R6 < R2 at 0x3a60, use R6 as the count parameter (0x9).
00003A70 LDRGE R1, [R3,R7,LSL#2] ; This will be our source buffer
00003A74 LDRLT R1, [R3,R7,LSL#2] ; This is the pointer we received at 0x3a5c
00003A78 BL memcpy ; We can now control what data is read into this buffer and eventually the memory contents are returned to the client.
Crash output:
[ 116.820000] INFO1624: new connection from 192.168.1.2 : cf5d3a00
[ 117.420000] INFO1EFE: command local:00000017 remote:00000007 final:00000007
[ 117.430000] INFO1F3C: new Tunnel : remote ID = MYHOSTNAME, length = 10
[ 117.440000] INFO14A5: new connection sbus ca8ed400
[ 117.440000] V4 : 0201A8C0
[ 117.630000] kc 144 : dbgd client connect ok cf5d36c0
[ 117.830000] INFO04FB: _fillBuf(): len = 0
[ 117.830000] INFO0471: KTCP_Stop : sbus ca8ed400 name:MYHOSTNAME
[ 117.840000] INFO146F: SoftwareBus_dispatchThread exit
[ 118.050000] INFO1624: new connection from 192.168.1.2 : cf68e000
[ 118.650000] INFO1EFE: command local:00000017 remote:00000007 final:00000007
[ 118.660000] INFO1F3C: new Tunnel : remote ID = PAD�@AA�Ԏ�`֎ʠ�, length = 19
[ 118.660000] INFO14A5: new connection sbus ca8ed400
[ 118.670000] V4 : 0201A8C0
[ 119.050000] Unable to handle kernel paging request at virtual address 41414141
[ 119.060000] pgd = c0004000
[ 119.060000] [41414141] *pgd=00000000
[ 119.060000] Internal error: Oops: 5 [#1] PREEMPT SMP
[ 119.060000] last sysfs file: /sys/kernel/uevent_seqnum
[ 119.060000] module: NetUSB bf111000 162837
[ 119.060000] module: GPL_NetUSB bf10a000 3743
[ 119.060000] module: MultiSsidCntl bf103000 5265
[ 119.060000] module: ip_set_hash_net bf0f7000 21118
[ 119.060000] module: ip_set_hash_ipmark bf0ec000 18532
[ 119.060000] module: ip_set_list_set bf0e5000 6885
[ 119.060000] module: ip_set_hash_netiface bf0d9000 22630
[ 119.060000] module: ip_set_hash_ipmac bf0ce000 19038
[ 119.060000] module: ip_set_hash_mac bf0c6000 9433
[ 119.060000] module: ip_set_hash_ip bf0bb000 18296
[ 119.060000] module: ip_set_hash_netportnet bf0ae000 25242
[ 119.060000] module: ip_set_hash_ipportnet bf0a2000 24446
[ 119.060000] module: ip_set_bitmap_port bf09e000 5721
[ 119.060000] module: ip_set_hash_netport bf092000 22954
[ 119.060000] module: ip_set_hash_ipport bf087000 19156
[ 119.060000] module: ip_set_bitmap_ipmac bf048000 6351
[ 119.060000] module: ip_set_hash_netnet bf026000 24018
[ 119.060000] module: ip_set_hash_ipportip bf01b000 20008
[ 119.060000] module: ip_set_bitmap_ip bf007000 6397
[ 119.060000] module: ip_set bf686000 24837
[ 119.060000] module: ipv6_spi bf675000 39791
[ 119.060000] module: ufsd bf5c7000 627276
[ 119.060000] module: jnl bf5b8000 29052
[ 119.060000] module: acos_nat bf258000 3444282
[ 119.060000] module: dhd bf04b000 236809
[ 119.060000] module: dpsta bf045000 2921
[ 119.060000] module: et bf030000 52421
[ 119.060000] module: igs bf015000 13866
[ 119.060000] module: emf bf00b000 16229
[ 119.060000] module: ctf bf000000 17805
[ 119.060000] Modules linked in: NetUSB(P) GPL_NetUSB MultiSsidCntl(P) ip_set_hash_net ip_set_hash_ipmark ip_set_list_set ip_set_hash_netiface ip_set_hash_ipmac ip_set_hash_mac ip_set_hash_ip ip_set_hash_netportne
t ip_set_hash_ipportnet ip_set_bitmap_port ip_set_hash_netport ip_set_hash_ipport ip_set_bitmap_ipmac ip_set_hash_netnet ip_set_hash_ipportip ip_set_bitmap_ip ip_set ipv6_spi(P) ufsd(P) jnl acos_nat(P) dhd dpsta(P)
et(P) igs(P) emf(P) ctf(P) [last unloaded: ipv6_spi]
[ 119.060000] CPU: 0 Tainted: P (2.6.36.4brcmarm+ #17)
[ 119.060000] PC is at SoftwareBus_reportConfigDescGot+0x90/0x268 [NetUSB]
[ 119.060000] LR is at SoftwareBus_reportConfigDescGot+0x58/0x268 [NetUSB]
[ 119.060000] pc : [<bf118e68>] lr : [<bf118e30>] psr: 20000013
[ 119.060000] sp : ca00ff60 ip : cf937fe0 fp : 00000000
[ 119.060000] r10: 00000000 r9 : 00000000 r8 : 00000000
[ 119.060000] r7 : bf124f1c r6 : ca8ed400 r5 : 414140fd r4 : cf937fe0
[ 119.060000] r3 : ca8ed628 r2 : 00000000 r1 : a0000013 r0 : ca8ed400
[ 119.060000] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 119.060000] Control: 10c53c7d Table: 8a05c04a DAC: 00000017
[ 119.060000] Process NU Work:4696 (pid: 20370, stack limit = 0xca00e270)
[ 119.060000] Stack: (0xca00ff60 to 0xca010000)
[ 119.060000] ff60: 7fffffff ca8866f0 ca8866f4 c03830c4 ffffffff ffffffff ca00e000 04134868
[ 119.060000] ff80: cf937fc0 00000000 ca00ffcc bf124f1c 00000000 00000000 00000000 bf11ec68
[ 119.060000] ffa0: ca8866c0 bf134868 ca00ffcc bf124f70 00000000 ca065f28 ca8866c0 c0078cdc
[ 119.060000] ffc0: ca065f28 00000000 ca8866c0 00000001 00000000 00000000 ca00ffd8 ca00ffd8
[ 119.060000] ffe0: 00000000 ca065f28 c0078c58 c0040b58 00000013 c0040b58 00000000 00000000
[ 119.060000] [<bf118e68>] (PC is at SoftwareBus_reportConfigDescGot+0x90/0x268 [NetUSB])
[ 119.060000] [<bf118e68>] (SoftwareBus_reportConfigDescGot+0x90/0x268 [NetUSB]) from [<bf11ec68>] (SoftwareBusWorkThreadProc+0x90/0xc4 [NetUSB])
[ 119.060000] [<bf11ec68>] (SoftwareBusWorkThreadProc+0x90/0xc4 [NetUSB]) from [<bf124f70>] (_thread_create_helper+0x54/0x114 [NetUSB])
[ 119.060000] [<bf124f70>] (_thread_create_helper+0x54/0x114 [NetUSB]) from [<c0078cdc>] (kthread+0x84/0x8c)
[ 119.060000] [<c0078cdc>] (kthread+0x84/0x8c) from [<c0040b58>] (kernel_thread_exit+0x0/0x8)
[ 119.060000] Code: eb3d9379 e5c45003 e5c45004 ea000033 (e5957044)
[ 119.430000] INFO04FB: _fillBuf(): len = 0
[ 119.440000] ---[ end trace 54b44174edbb4652 ]---
[ 119.440000] Kernel panic - not syncing: Fatal exception
[ 119.450000] [<c0046358>] (unwind_backtrace+0x0/0xe4) from [<c0382d94>] (panic+0x68/0x194)
[ 119.460000] [<c0382d94>] (panic+0x68/0x194) from [<c0043548>] (die+0x194/0x1dc)
[ 119.460000] [<c0043548>] (die+0x194/0x1dc) from [<c00474f8>] (__do_kernel_fault+0x64/0x84)
[ 119.470000] [<c00474f8>] (__do_kernel_fault+0x64/0x84) from [<c00476dc>] (do_page_fault+0x1c4/0x1d8)
[ 119.480000] [<c00476dc>] (do_page_fault+0x1c4/0x1d8) from [<c003f3a4>] (do_DataAbort+0x30/0x98)
[ 119.490000] [<c003f3a4>] (do_DataAbort+0x30/0x98) from [<c0463a8c>] (__dabt_svc+0x4c/0x60)
[ 119.500000] Exception stack(0xca00ff18 to 0xca00ff60)
[ 119.500000] ff00: ca8ed400 a0000013
[ 119.510000] ff20: 00000000 ca8ed628 cf937fe0 414140fd ca8ed400 bf124f1c 00000000 00000000
[ 119.520000] ff40: 00000000 00000000 cf937fe0 ca00ff60 bf118e30 bf118e68 20000013 ffffffff
[ 119.530000] [<c0463a8c>] (__dabt_svc+0x4c/0x60) from [<bf118e68>] (SoftwareBus_reportConfigDescGot+0x90/0x268 [NetUSB])
[ 119.540000] [<bf118e68>] (SoftwareBus_reportConfigDescGot+0x90/0x268 [NetUSB]) from [<bf11ec68>] (SoftwareBusWorkThreadProc+0x90/0xc4 [NetUSB])
[ 119.550000] [<bf11ec68>] (SoftwareBusWorkThreadProc+0x90/0xc4 [NetUSB]) from [<bf124f70>] (_thread_create_helper+0x54/0x114 [NetUSB])
[ 119.560000] [<bf124f70>] (_thread_create_helper+0x54/0x114 [NetUSB]) from [<c0078cdc>] (kthread+0x84/0x8c)
[ 119.570000] [<c0078cdc>] (kthread+0x84/0x8c) from [<c0040b58>] (kernel_thread_exit+0x0/0x8)
[ 119.580000] dump_kmsg: dump list lock is held during panic, skipping dump
[ 119.590000] CPU1: stopping
[ 119.590000] [<c0046358>] (unwind_backtrace+0x0/0xe4) from [<c003f2f0>] (do_IPI+0xfc/0x180)
[ 119.590000] [<c003f2f0>] (do_IPI+0xfc/0x180) from [<c0463ae8>] (__irq_svc+0x48/0xe8)
[ 119.590000] Exception stack(0xcf83df98 to 0xcf83dfe0)
[ 119.590000] df80: 00000000 cd7e0f00
[ 119.590000] dfa0: cf83dfe0 00000000 cf83c000 c04b0bc8 c04d50a8 c04d5220 80000000 413fc090
[ 119.590000] dfc0: 0000001f 00000000 c0508cd8 cf83dfe0 c0040bb0 c0040bb4 60000013 ffffffff
[ 119.590000] [<c0463ae8>] (__irq_svc+0x48/0xe8) from [<c0040bb4>] (default_idle+0x24/0x28)
[ 119.590000] [<c0040bb4>] (default_idle+0x24/0x28) from [<c0040d1c>] (cpu_idle+0x40/0x94)
[ 119.590000] [<c0040d1c>] (cpu_idle+0x40/0x94) from [<80008110>] (0x80008110)
[ 119.870000] NVRAM LOG 16384 33858 50242
[ 120.080000] Rebooting in 3 seconds..Digital core power voltage set to 1.0V
In this example, I do not have any devices connected to the router. I use the device index 0x8a
, which will point to the computer name (placed on the stack during handshake) instead of a valid device index. The address that will be read based on the device number depends on the number of devices on the stack (since the stack will grow with additional devices). However, this value can be incremented or modified as often as the attacker would like until successful exploitation. The opcode (the byte prior to the index) in this example is 0x02
or getConfigDescriptor
.
2019-02-01 - Vendor Disclosure
2019-02-11 - Vendor Acknowledged & inquired about PGP
2019-03-06 - Plain text copy of report sent & advised Netgear also aware of issue
2019-05-08 - Follow up with vendor
2019-05-15 - Vendor advised provided new module to NetGear
2019-06-17 - Public Release
Discovered by Dave McDaniel of Cisco Talos