Lucene search

K
hackeroneZeyu2001H1:1632921
HistoryJul 10, 2022 - 6:01 p.m.

Node.js: DNS rebinding in --inspect (insufficient fix of CVE-2022-32212 affecting macOS devices)

2022-07-1018:01:38
zeyu2001
hackerone.com
7

8.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

REQUIRED

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.044 Low

EPSS

Percentile

91.2%

Summary: This is an insufficient fix of CVE-2022-32212, which itself is a fix of CVE-2018-7160. There exists a specific behaviour in browsers on macOS devices when handling the http://0.0.0.0URL that allows an attacker-controlled DNS server to bypass the DNS rebinding protection by resolving hosts in the .local domain.

Description:

In the latest version, only IP addresses and localhost are allowed in the Host header when connecting to the debugger endpoint. IsIPAddress ensures that IPv4 address octets only contain values ranging from 0 to 255, but this allows 0.0.0.0 which indicates an invalid or unroutable target.

In macOS devices, using fetch("http://0.0.0.0")or opening http://0.0.0.0 through a top-level navigation in Chrome and Firefox will cause a DNS request to resolve <Computer Name>.local, where Computer Name is configured in the system preferences (the use of the .local TLD is a known feature of macOS devices). If such a request succeeds, http://0.0.0.0 is routed to the IP address provided in the DNS reply. This would typically be the same as the IP address of the device on the local network, so http://0.0.0.0 will typically route to any application listening on the local interface - working as intended.

An attacker-controlled DNS server can, however, resolve <Computer Name>.local to any arbitrary IP address, and consequently cause the victim’s browser to load arbitrary content at http://0.0.0.0. This allows the attacker to bypass the DNS rebinding protection.

Note: On Windows devices, http://0.0.0.0 is treated as an invalid URL and the request is blocked.

Steps To Reproduce:

General Attack Flow

  1. Victim runs node with --inspect option
  2. Victim visits attacker’s webpage
  3. The attacker’s webpage opens http://0.0.0.0:9229
  4. Victim asks the DNS server for &lt;Computer Name&gt;.local and gets <attacker’s-IP>.
  5. Victim loads webpage http://0.0.0.0:9229 from <attacker’s-IP>.
  6. The webpage http://0.0.0.0:9229 tries to load http://0.0.0.0:9229/json.
  7. Due to a short TTL, the DNS server will be soon asked again about an entry for &lt;Computer Name&gt;.local. This time, the DNS server responds with “127.0.0.1”.
  8. The http://0.0.0.0:9229 website (i.e., the one hosted on <attacker’s IP>) will retrieve http://0.0.0.0:9229/json from 127.0.0.1, including webSocketDebuggerUrl.
  9. Now, the attacker knows the webSocketDebuggerUrl and can connect to it using WebSocket. Note that WebSocket is not restricted by same-origin policy. By doing so, they can gain the privileges of the Node.js instance.

Vulnerable code segment: https://github.com/nodejs/node/blob/d9b71f4c241fa31cc2a48331a4fc28c15937875a/src/inspector_socket.cc#L164-L183

DNS Setup

In this example I used dnsmasq as my DNS server. You can use {F1816108} for a minimal Docker setup that demonstrates the arbitrary resolution of &lt;Computer Name&gt;.local domains. The hosts file should be changed according to the configured Computer Name of the victim device (through system preferences). For example, my Computer Name is Zeyu’s MacBook Pro, which means I had the Zeyus-Macbook-Pro.local domain.

# Resolve &lt;Computer Name&gt;.local to any IP address
1.1.1.1 Zeyus-Macbook-Pro.local

When connecting to http://0.0.0.0, the page is loaded from 1.1.1.1 instead.

{F1816116}

If you observe the network traffic while connecting to http://0.0.0.0, you should see the relevant DNS requests and replies.

{F1816134}

Subsequently &lt;Computer Name&gt;.local can be rebinded to 127.0.0.1, causing the page to be loaded from 127.0.0.1 where the debugger is listening.

{F1816125}

Suggested Remediation

According to IANA, 0.0.0.0/8 is a reserved address range. To prevent this vulnerability, this address range could be blocked.

A way to check for this address range could simply be accum == 0 in the first octet or host.front() == '0' in IsIPAddress

Supporting Material/References:

Impact

Attacker with access to a compromised DNS server or the ability to spoof its responses can gain access to the Node.js debugger, which can result in remote code execution.

8.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

REQUIRED

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.044 Low

EPSS

Percentile

91.2%