Talos Vulnerability Report

TALOS-2019-0775

KCodes NetUSB unauthenticated remote kernel arbitrary memory read vulnerability

June 14, 2019
CVE Number

CVE-2019-5016

Summary

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.

Tested Versions

NETGEAR Nighthawk AC3200 (R8000) Firmware Version V1.0.4.2810.1.54 (11/7/18) - NetUSB.ko 1.0.2.66 NETGEAR Nighthawk AC3000 (R7900) Firmware Version V1.0.3.810.0.37 (11/1/18) - NetUSB.ko 1.0.2.69

Product URLs

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/ReadySHAREUSBPrinter.aspx

CVSSv3 Score

10.0 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:H

CWE

CWE-200: Information Exposure

Details

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 Information

Crash output:

If we give an invalid address at any point, we can simply DOS the target:

[  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

Exploit Proof of Concept

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.

Timeline

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

Credit

Discovered by Dave McDaniel of Cisco Talos