Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distinct private networks can attack each other #28

Open
thejh opened this issue Nov 12, 2020 · 7 comments
Open

Distinct private networks can attack each other #28

thejh opened this issue Nov 12, 2020 · 7 comments

Comments

@thejh
Copy link

thejh commented Nov 12, 2020

cc @mikewest @letitz

I don't understand the IP address classification rules. Why are link-local addresses "local" while private IPv4 addresses are "private"? In a normal, dual-homed home network, you'd address devices on the network through fe80::* addresses for IPv6 (classified as "local") but through 192.168.*.* or whatever for IPv4 (classified as "private"). If semantically, "local" is supposed to mean "this machine" while "private" is supposed to mean "the local network", then the current classification of IPv6 addresses is broken.

Also note that with IPv6, there are one (or potentially even more) global addresses using which devices on the local network are reachable at any given time. (Potentially multiple ones with the same prefix for privacy reasons, but I believe there can also be ones with different prefixes if the ISP recently delegated a new IPv6 prefix to the router, or if there are multiple routers with upstream connectivity.) And ISPs will sometimes delegate a /64, other times an entire /48 (from which the user can then carve multiple /64 networks for different private network segments). (And which IPv6 addresses belong to the local network can change dynamically if the user connects to a wifi or whatever.)

And a completely different can of worms is that if the user e.g. configures their device such that it will connect both to some open wifi network and some corporate wifi network, a private IP address isn't even semantically same-origin with itself across time, because in one case it is some trusted corporate server while in the other case it's routed through an attacker-controlled network.)

With static network config

So here's a list of brainstormed things that I think might help make these rules vaguely solid under the assumption that we're not moving between networks:

  • Clarify that fe80::/10 in a DNS response must always error out, as explained in RFC 4472, section 2.1:

    Link-local addresses should never be published in DNS (whether in forward or reverse tree), because they have only local (to the connected link) significance

  • If the browser wants to permit the use of link-local addresses such as http://[fe80::1%eth0]:80/ (which are only valid on a single machine, because they incorporate information about the client's network interface names), classify fe80:: as "private" such that "local" refers exclusively to services running on the local machine. This should fix the classification of "private" vs everything else.
    • Chrome does not currently support this, from what I can tell. Firefox doesn't seem to support it either. E.g. curl seems to support it, though. It would be a useful feature if someone wants to have stable browser bookmarks referencing IPv6 devices on the local network.
    • With the caveat that VMs running on the local machine would still not be classed as "private" even if nothing on the local network can talk to them directly. So VMs might not get the protection they deserve. This might be fixed if we isolated "private" addresses by network interface - see the section on network switching.
      • Same thing if there are services on the local machine that bind to global addresses but are isolated using firewall rules, but we probably don't care about that too much. We can't really reason about local firewall rules, and nobody should be doing this if they don't want the service to be reachable from the network.
  • When initiating a connection to a machine with a global address, first query the local routing table for that address; if the address is routed without a router, it should probably be classified as "private".
    • Unless the interface is directly connected to the public internet without any local router. The best heuristic I can think of for this is "do we have a non-private IPv4 address".
    • This would probably still fail unsafe if the user e.g. plugged a layer 3 wifi AP into their home router and the current machine is on the wifi network; in that scenario, the wifi AP and the main router would have separate /64 address prefixes. We could try to detect such situations by using an IPv4 traceroute to detect at which TTL the edge from the local network to the internet is, then hope that the route to the internet has the same length for IPv6?
    • Arguably we could still theoretically get into a situation where the router knows more global prefixes for the local network than we do. We could try to detect that by sending a packet with TTL 2 (which will be the first machine of the ISP on a typical home network; but see previous bullet point) and checking whether that results in an ICMP Time Exceeded message from the same gateway as when trying to contact some IPv6 address that is known to be publicly routed.

With network switching

If we are switching between networks (one of which is potentially malicious, especially if the client device is a laptop or smartphone that is configured to connect to open wifi networks), things around "private" addresses immediately get really ugly. Imagine an attacker who forces us to disconnect from the trusted company wifi, causing us to connect to the attacker's spoofed open wifi; then on the open wifi, the attacker navigates some iframe to http://192.168.1.1/, loads malicious script in the frame, then terminates the open wifi and lets us connect back to the trusted company wifi, on which attacker-controlled script now runs in the origin of http://192.168.1.1/.

To handle this properly, it might be necessary to do something along the lines of:

  • The browser associates connections with network identifiers; the network identifier includes at least the interface name over which the packets will be routed and, if it is a wifi interface, something like the SSID.
    • This would ideally need an interface from the OS that says "synchronously kill this socket if its routing changes or the interface is about to associate with a different network".
  • When "private" addresses try to interact with each other across network identifiers, the same checks as for public -> private interactions apply.
  • Network identifiers are part of the origin. Even if protocol, host, port, and IP address all match, documents will be cross-origin if their network identifiers don't match. Cookie stores and such are also split on network identifiers.
  • If a request is made where protocol+host+port+ip match, but the network identifiers don't match, the request should be BLOCKED unless BOTH of the involved origins have opted in - a page loaded over a trusted network should not send XHR requests with CSRF tokens to an untrusted network or load scripts from an untrusted network, no matter what headers a server on the untrusted network sets.

If this is considered out of scope, there should probably at least be a note somewhere that points out this scenario.

@thejh
Copy link
Author

thejh commented Nov 12, 2020

Oh, and link-local IPv4 addresses should of course also be reclassed as "local".

@letitz
Copy link
Collaborator

letitz commented Nov 12, 2020

Hi there, thanks for the feedback!

Unpacking this a bit... I see you raise a few issues:

  • link-local IPv6 addresses should be considered private, not local
  • I'm not sure I understand your point regarding VMs. Care to elaborate?
  • Detecting the edge of the home network with ICMP
    • That's an interesting proposition, but I'm not sure browsers and web standards are the best home for this
    • Home networks and auto-configuration with IPv6 have long been a thorny problem
    • I'd rather align with IETF specs if possible. Do you know of any?
    • So far I'm considering treating .local and .home as private regardless of IP
    • See also issue Treat *.local. as link-local. #4
  • Detecting IP address collisions
    • I have also given some thought to the coffee shop scenario
    • So far I'd only considered pages cached from a network, accessed on another
    • You bring up a good point that a sufficiently-long-lived page could be loaded on one network, then exploit another
    • Chrome can detect network changes, so we can try to leverage that?
    • Not sure how much to discuss this in the spec: call it out as a potential issue and let browser devs decide how much to invest in fixing that loophole?

@thejh
Copy link
Author

thejh commented Nov 12, 2020

  • I'm not sure I understand your point regarding VMs. Care to elaborate?

Let's say I set up a VM on my laptop for development of some webapp. To be able to access the VM's webserver from the host OS, I don't just give the VM NAT-based connectivity, but put the VM on a virtual network interface, let the host route 192.250.123.0/24 to that interface, and assign 192.250.123.2 to the VM.

The specified algorithm will then allow devices on the local network to interact with the webapp in the development VM. If the local network is untrusted, that may potentially be a small issue.

As an example, see Chrome OS with Crostini. On the Pixelbook I have here, I can run an HTTP server inside the Crostini VM, and it shows up as 100.115.92.201 on the host. Which actually isn't even an RFC1918 address, but an RFC6598 address, which is supposed to be "IPv4 address space designated for Service Provider use with the purpose of facilitating CGN deployment"; so with the current draft spec, I think the Crostini VM would count as "public", and random websites would still be able to navigate to it and load resources from it?

  • Detecting the edge of the home network with ICMP

    • That's an interesting proposition, but I'm not sure browsers and web standards are the best home for this

Yeah, dunno.

  • Home networks and auto-configuration with IPv6 have long been a thorny problem

Ah, I'm not aware of any broader issues here. If anything, I'd expect IPv6 to mostly make things better, given that it removes the need for ugly STUN hackery and such...

  • I'd rather align with IETF specs if possible. Do you know of any?

Nope, sorry.

  • So far I'm considering treating .local and .home as private regardless of IP

I guess that's a reasonable approach, although it probably doesn't help much if an attacker can determine the actual IP addresses of devices?

  • See also issue Treat *.local. as link-local. #4

  • Detecting IP address collisions

    • I have also given some thought to the coffee shop scenario
    • So far I'd only considered pages cached from a network, accessed on another
    • You bring up a good point that a sufficiently-long-lived page could be loaded on one network, then exploit another

If there is an active attacker in wifi range, the network switching could happen in close succession.

  • Chrome can detect network changes, so we can try to leverage that?

That would probably help to some degree, although it would likely not be 100% solid due to the lack of an API that would ensure that no more packets (potentially containing secret tokens) can go out after a network switch.

  • Not sure how much to discuss this in the spec: call it out as a potential issue and let browser devs decide how much to invest in fixing that loophole?

Maybe? I don't think there's a nice solution that works without a bunch of ugly plumbing here. (Short of requiring HTTPS, but that'd be beyond the scope here.)

@sleevi
Copy link
Contributor

sleevi commented Nov 12, 2020

  • I'm not sure I understand your point regarding VMs. Care to elaborate?

As an example, see Chrome OS with Crostini. On the Pixelbook I have here, I can run an HTTP server inside the Crostini VM, and it shows up as 100.115.92.201 on the host. Which actually isn't even an RFC1918 address, but an RFC6598 address, which is supposed to be "IPv4 address space designated for Service Provider use with the purpose of facilitating CGN deployment"; so with the current draft spec, I think the Crostini VM would count as "public", and random websites would still be able to navigate to it and load resources from it?

Filed #30 for this

@letitz
Copy link
Collaborator

letitz commented Feb 23, 2021

Ok, so the initial concern around incorrect classification of IPv6 addresses should be fixed by #30.

What remains is the ability for an attacker to force wifi switches, inject malicious content in the private address space, and profit from the network confusion. This is actually reminiscent of attacks I'd considered where the malicious content survived network changes in the cache.

I think this would indeed be addressed by keying the private network request detection logic on some other piece of data than the address space alone - be it a network identifier, or maybe more simply a count of network changes since the browser last restarted. The latter solution in particular seems doable in Chrome, given that we already observe network changes. It might miss some events (IIRC proxy config changes, for example) but should be good enough to mitigate the scenario laid out here.

All that said, I do not think that is as high priority as fixing the main issue: that any random public website can attempt to hack various internal network devices with 0 marginal effort per victim.

What do you think? If you agree, I can rename this issue to focus on cross-network confusion attacks, the like of which you described.

@thejh
Copy link
Author

thejh commented Feb 24, 2021

Yeah, I think renaming the issue is reasonable.

@letitz letitz changed the title IPv6, "local addresses", and network switching Distinct private networks can attack each other Feb 25, 2021
@letitz
Copy link
Collaborator

letitz commented Feb 25, 2021

Thanks! Done.

letitz added a commit that referenced this issue Jun 4, 2021
The previous commit mistakenly included part of this commit in
`index.html` without including the corresponding changes to
`index.src.html`, which is unfortunate. This brings the repo back to a
state where running `make clean && make` should only change the
document revision hash in `index.html`.

Issue: #28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants