Hacker News new | ask | show | jobs
Blizzard games were vulnerable to DNS rebinding attack (bugs.chromium.org)
207 points by csmajorfive 3076 days ago
9 comments

So basically this is a local web server that is used for IPC? Is there a reason to do local IPC over TCP/IP, rather than over named pipes / unix pipes, other than not knowing about the existence of named pipes / unix pipes?
Better library support - the state of HTTP client and server libraries is a lot better than the state of other random RPC libraries, and developers tend to be more familiar with it these days too, so it's likely to get you a better product. And very few HTTP libraries support HTTP over named pipes or UNIX sockets.
That's how it should be done yeah. Developers on Windows seem to be really loose in their use of TCP or UDP for IPC. Even my mouse driver opens up a port on 0.0.0.0.
UDP for IPC seems to be especially crazy to me. I mean sending data over TCP to localhost is basically a memcpy(), what advantages could UDP possibly have in that context?
Guarantee of preserved message boundaries? It’s the reason Unix domain sockets have SOCK_SEQPACKET and Windows named pipes have message mode.
AF_LOCAL (Unix domain) sockets suck for datagram messaging because both end-points need a filesystem name -- you can't have the client end-point be anonymous! The simplest way to have an anonymous client is to send a datagram socketpair to the server in a datagram, this way the server can respond over the socketpair -- but this is complex and significantly slower than if Unix domain datagram sockets just supported anonymous clients.

Meanwhile, if you want to write an IPC library that's portable to WIN32 and POSIX, and just about any OS that supports TCP/IP, then TCP or UDP works very well. In fact, nothing else does. But of course, you have to publish the server port number (and at least a cookie for authentication) for this somewhere (e.g., in /tmp).

While they're certainly the correct way of doing this, named pipes don't provide too many benefits under Win32; other than this little problem and having to assign a port number, of course. They're harder to debug, require a whole new slew of I/O boilerplate ontop of your existing files/TCP/UDP sockets, suffer from a bunch of weirdness when connecting, etc.

They're also pretty slow, but that isn't much of a concern here - browsers do suffer from it though (except for Edge, which uses an undocumented IPC API).

>(except for Edge, which uses an undocumented IPC API).

Do you have any links or things to google for?

At least on Windows, TCP performs better than named pipes.
In the case of Transmission I think it may have been used both as IPC and as a remote connection.
Yeah I think this is the way you access the xml-rpc api.
Spotify does pretty much the same thing
Better cross-platform support?
Disappointing (lack of) response/fix

Would have assumed they'd do better given how polished their consumer products are

Could someone explain in the most accessible language, how a problem like this could even be fixed?
Answer:

The service should check that the `Host` header on every request specifies `localhost`, `127.0.0.1`, or whatever other name is normally used to access it. If the `Host` header specifies a possibly-attacker-controlled name, the service should reject the request.

Explanation:

The problem is that the service is relying on same-origin policy to prevent a web site running in a local browser from making requests to it -- but it does not actually check what origin requests are addressed to. A random web site running in a browser normally cannot make requests to a hostname other than its own. Or, more precisely, it can make requests, but it can't read the responses. The Blizzard updater therefore forces the client to make an initial request whose response contains a secret token, and then subsequent requests contain that token, proving that the client was able to read the response and is therefore not a web site running in a browser.

However, it's perfectly possible to define an evil hostname whose DNS records assign it the IP address 127.0.0.1. Thus requests to this evil hostname would end up reaching the local Blizzard updater service. Unfortunately, the service will happily respond to these requests because it does not pay attention to what hostname the client was requesting.

(To actually exploit this, it's necessary to load an evil web site from the evil host name, which then performs the attack. This means that the evil hostname can't only map to 127.0.0.1, but must also map to the attack server. But a hostname can indeed have multiple DNS records, and the browser will arbitrarily switch between them, allowing an attack to proceed.)

And/or simply implement CORS headers.
No, CORS doesn't apply here. CORS regulates cross-origin requests, but the attack here makes the browser think the requests are same-origin.

(Also, CORS can only be used to permit access that would normally be denied. CORS does not offer any way to deny access that is normally permitted.)

The most likely candidate is that the local server would need to require authentication with credentials that a browser would be unlikely to have. Maybe write a key file to the local filesystem since both the game and the update daemon would have access, but browsers wouldn't without explicitly asking the user.

DNS rebinding vulnerabilities are an authentication/trust problem. Authors assume that 127.0.0.1 (or other private IP blocks) are safe and therefore no additional authentication is required, or that they can rely on the same origin policy to do certain things and, e.g., reject requests with the Origin header set, or as in this case, rely on the same origin policy to restrict updates to the `Authorization` header without a preflight OPTIONS request.

If you want to watch an interesting DefCon talk about the same idea, but attacking routers, this [1] is one the most entertaining/interesting talks I've seen overall.

[1] https://www.youtube.com/watch?v=FV7SQd-3Ytk

Last comment in report:

> Their solution appears to be to query the client command line, get the 32-bit FNV-1a string hash of the exename and then check if it's in a blacklist. I proposed they whitelist Hostnames, but apparently that solution was too elegant and simple.

It looks like they just block specific browsers from doing the exploit, except they have to actively maintain the blacklist, which is sub-optimal.

It looks like is an HTTP API server that's only intended to be used by other programs on the local machine that explicitly know about it, not by any HTTP client that happens to run on the local machine (like a web browser, or an embedded web browser in some random app). They appear to have an authentication step by having the first URL return a token, and requiring you pass the token to all other URLs, but that doesn't actually secure anything.

The easy way to fix it is to introduce some non-HTTP-based way of transmitting this token. For instance, put the token in the user's Blizzard settings directory, so that you have to have read access to the user's files to access the server. Or use some non-network-based interprocess communication mechanism, like named pipes, to do the initial authentication. Or (what we did a couple jobs ago), check what the other end of the socket is, and make sure it's a known-good app (instead of making sure it's not a known-bad app, which is what they're doing).

If you're intentionally embedding a web browser in your app and want it to access this interface, you can have your code pick up the token from a file / from IPC using native code, and then send it into the embedded web browser as a cookie or JavaScript variable or something. Most ways of embedding a web browser in a native app support this.

The reason they fixed it this way is probably because all good ways of fixing it require code changes to every client to use the new mechanism, and there are lots of clients using this interface, and this only requires a code change to the one server. (Even though "client" and "server" are both running on the same machine, patching one binary on everyone's computer is still easier than rebuilding and patching lots of binaries.)

It could also be an attempt at actually integrating with a regular web browser on regular web pages. Perhaps for things like launching a locally installed game to join a multiplayer match by clicking a start button on a match-making website, by triggering the local http server with a cors request? I know that at least spotify has something similar for interfacing with your locally installed desktop client on a regular web page.
If you want to do that, just do a normal cryptographic signature thing - have the website sign requests with a private key that's kept by Blizzard, and have clients check that requests are signed.

(It sounds like that's not what's happening here because the patch checks for browser EXE names and refuses connections from them, but I'm not quite sure.)

No, the much better way to do this is with a custom URI protocol handler, e.g. steam://launch/220, which has no direct interaction between privilege domains (steam and the website).
the DNS rebinding vulnerability exists in applications other than Blizzard games, such as this one in Bittorrent Transmission also reported by Tavis Ormandy....

https://github.com/transmission/transmission/pull/468

Seems to be a quite prevalent issue...

That's because for decades binding to localhost has been taken to mean "only users on the local machine can access this". Now chrome is breaking that assumption through a leaky sandbox, and demanding everyone else change rather than fixing their own security issues.
Can you explain why this is a Chrome-specific issue? I believe that it applies to all web browsers, including Internet Explorer for UNIX (which I do have access to and I can test if you would like me to confirm). I remember this being a vulnerability class with CUPS, which listens on http://localhost:631/, about 10 years ago.

In particular, note that the request is not made to localhost, it's made to a DNS name that simply happens to resolve to 127.0.0.1. Should Chrome and also all other web browsers add a special case for DNS names that resolve to 127.0.0.1?

> I believe that it applies to all web browsers

It does not apply to late versions of Presto, where there was similar treatment of requests to various special-use addresses (after hostname resolution, if applicable) as to requests that result in TLS failures.

I believe, though it's been years since I dealt with this, that this caused various enterprise sites to stop working, many of which assumed they could access intranet hosts (which resolved to private IPs) from remotely hosted websites.

In any event lacking this feature is not evidence of a "leaky sandbox" in Chrome.
How about caching a DNS result for the duration of a tab? Doesn't solve everything but probably good enough. I don't think it'd break many things (sure it may affect some round robin DNS things for long-open pages that are Ajax'ing back home frequently, but that should be minimal)
I could believe that caching "is it localhost / RFC 1918 space or not" for a tab would have a sufficiently low false-positive and false-negative rate to be worth doing. It would still break the average business user who has an email half-written in OWA via an RFC-1918 address, puts their laptop to sleep, and goes home and expects OWA to still work over the public internet, but maybe OWA can figure out a solution there like local storage + document.reload(). It won't solve all the problems but it'll solve many of them.

I think caching all DNS resolutions is going to make a lot of cloud-native websites very sad (e.g., I'd imagine something like Slack would break quickly), and also cause poor performance because you don't get the benefit of a CDN's DNS server telling you that other servers are closer now. A lot of people have long-running tabs.

I don't mind breaking legitimate uses rebinding here. I'm not familiar w/ how OWA changes IPs from private-network to public-network, but I'd say using DNS is the wrong approach. And Chrome can probably detect DNS server change and then evict its known cache anyways. Yeah, a first step is probably just localhost/private-IP specific.
Gmail is not minimal.

There is no quick fix for this.

Hopefully not, that will make local development very hard. Maybe an opt-out for it though, heh
Maybe if the address in the URL bar doesn't resolve to a local address, block access to local addresses.

Might break some enterprise stuff, but better safe than sorry, put an opt out of this behind a flag.

But it does resolve to a local address. The point of rbndr is that it switches between resolving to a remote address and to a local address, which is an entirely legitimate thing - https://owa.example.com for most companies will do exactly this when you move between the corporate network and the public internet.
>But it does resolve to a local address.

Huh? The exploit allows remote website you visit ends up having access to stuff bound to localhost. If a localhost website has access to other localhost stuff, it isn't as much of a big deal.

>The point of rbndr is that it switches between resolving to a remote address and to a local address, which is an entirely legitimate thing - https://owa.example.com for most companies will do exactly this when you move between the corporate network and the public internet.

IMO it's worth blocking it, and only allowing that behind a flag or a custom whitelist that the user maintains. Enterprises can easily do that.

That's false. Any website was able to send requests to 127.0.0.1 since that new thing called always. DNS rebinding has been a known vector for 10+ years.
A mischievous person renting a domainname can list any public or private IP addresses in her A records; and she can list any nameservers, including ones with which she has no relationship. She can list an IP address that the user may be utilising or renting.

I use a fast DNS resolution solution that only queries authoritative servers and stores IPs in constant, perfect hash databases, then in kdb+. No caches. I see the IP addresses that are returned in DNS packets not as ephemeral and inconseqential, but as entries in a database that need to be validated before insert.

If I see some nonsense like 127.0.0.1 in an A record, let alone a public IP address that Im using, it is rejected. I have seen NS records with 127.0.0.1 as well.

Are there DNS rebinding attacks that do not use iframes, Javascript or some other way to trigger automatic lookups without user interaction? In theory perhaps. But every attack I have seen relies on triggering lookups automatically.

Its too bad the popular browsers make automatic requests for resources, automatically follow redirects and do not allow users to disable this default behavior.

However users can make use of less complex HTTP clients that do not make such automatic requests and where redirects can be disabled. These can be used in tandem with the popular browsers to give users more transparency and control.

Also isnt it possible to use SSL/TLS for localhost JSON-RPC? Theoretically couldnt users make use of client certificates?

Just a thought.

Yep, I saw 127.0.0.1 for an A record about 20 years ago.

It was discovered when going to that name outside revealed what seemed to be a copy of our own internal webserver!

What had happened is that our webserver was on the same host as our squid proxy. So we were in fact seeing our own site under the external name.

Protection for this sort of thing is in the default squid conf these days (from memory)

It was a standard joke to point warez.yourdomain.country to 127.0.0.1 - I wouldn't be surprised if there were still some left and could be used for rebinding attacks!
Developer 101: if you want to do a blacklist, do a whitelist instead.
Developer 102 (or perhaps entrance exam): Never return authorization credentials on an unauthenticated HTTP endpoint.
It says Authorization, but this is really more of an anti-CSRF token, not an actual authorization credential, and anti-CSRF tokens are completely legitimate to return over an unauthenticated HTTP endpoint.
I think it depends mostly on the context. If you only want to allow a known subset of items, prefer a whitelist. If you want to avoid a subset of items, prefer a blacklist.
Yeah, but the hard-earned wisdom the parent post is trying to impart is that if you think you want to avoid a subset of items, you're probably wrong.

In an explicitly enumerated category, blacklists and whitelists are logically equivalent and can be used interchangeably. In almost every other case blacklists are insufficient because new items can generally be created, either maliciously or just accidentally as the size of the category grows, which are not on the blacklist but which share whatever bad trait you were hoping to protect against.

I'm sure there are a few exceptions, but generally speaking any problem that can be solved with either a blacklist or a whitelist should use the whitelist, just to be safe. A problem that can't use a whitelist is probably not actually solvable by a blacklist either, and trying to use one is likely to fail in the long run.

That's just the definition of whitelist and blacklist?
My personal heuristic is to always favor the positive collection, which comes from databases where retrieving a set of data is much easier and more efficient than retrieving a set of data EXCEPT THESE. I always figured there was math to back this up.
That's literally just the definition of a whitelist and a blacklist. I think the comment you were replying to was making the point that blacklists generally work poorly in practice and should be reconsidered where at all possible.
Ok i need a whitelist of every url but ones that start with facebook ... go
Its not a whitelist of domains, but of applications allowed to use the agent. Right now Blizzard is blacklisting only the browsers from using it, but that still leaves a bunch of other things like slack and whatnot.

Its indeed a very strange patch from Blizzard. As if they hastily assigned an intern to it and then called it a day.

In case you think this is hard:

    ^(?!facebook).*
This is a blacklist though.
Functionally - yes. By definition - no.

A blacklist won't allow items that match and a whitelist only allows items that match. Blacklists include by default and whitelists exclude by default.

Since the regex needs to match to be included - it is, by definition, a whitelist. It excludes by default anything that doesn't match. It just so happens the net being cast is so wide as to be "all except ____" where a whitelist is usually seen as "none except ____".

I think part of the definition blacklist and whitelist is being a list.
Is that not a black list? ... you are not affirming equality ... you are affirming inequality
How do you define a whitelist? A list of items, that, if matched, are allowed?

That regex will only positively "match" non-facebook items, and will only block facebook if implemented in a whitelist.

I'm just playing the straight man to your joke. Of course it's functionally a blacklist: That's what you asked for.

Sorry cant drop this one yet

My comment is not a joke but a challenge to the parent ... that they are wrong

Yes whitelist are safer ... but whitelists can be cumbersome to generate/maintain and slow you down at runtime ...

On reddit let this slide , but here we have to correct the flawed thinking.

You cant run from engineering problems without consequece

So now we have graduated to course 202: how to make a blacklist safe?

Check context, and restrict access

That's not really a whitelist though.
cat urls | grep -v ^facebook.*

ftfy

...did you intend to allow fbcdn.net (a Facebook-controlled domain)?
The vulnerable update daemon is running all of the time if you've installed any Blizzard game in the past, not just if you currently play Blizzard games?
I believe they've patched it already. But if you haven't updated the updater yet, you're probably still vulnerable.
The posting says that the patch is not very good, but that's a separate issue.
Edge in particular was missing, I don't think that uses "iexplore.exe".
On my machine the Blizzard launcher only checks for updates and updates itself when you run it so if you have it installed but haven't run it in a while you might not have the most recent.
Would having TLS on the localhost endpoint (without client certificates) make the attack more difficult? the browser would have to validate the localhost-returned cert against the attacker.com hostname.
> Any website can simply create a dns name that they are authorized to communicate with, and then make it resolve to localhost.

So if I understand this correctly, websites can now bypass all firewalls and send traffic to any _local_ port at will? It also seems that this same trick would apply to local/intranet IPs (e.g. have domains that redirect to 192.168.0.x) allowing interaction with things like printers. While Blizzard has a bug, it seems to be the browser that has the real vulnerability here.

Edit: The replies have good explanations with more detail why this would be difficult to fix -- the host doesn't have enough context to differentiate between "intended" and "unintended" IPs without a bunch of pernicious edge cases.

Lots of routers are vulnerable to this. One of my favorite DefCon talks [1] (favorite talks in general, actually) goes over this vulnerability.

Generally, I think browsers handle this as well as they can. DNS rebinding preys on a feature that's useful for being able to fall back on redundant servers if a primary fails, which is important.

IIRC from the talk, browsers have implemented policies that prevent rebinding to non-public IP ranges. The talk below touches on how that's not quite sufficient for routers, because they also happen to have a valid public IP, but often don't properly filter or NAT packets from the LAN NIC, leaving them vulnerable because the packets still come from a private IP, so the source-IP-based security lets them through.

[1] https://www.youtube.com/watch?v=FV7SQd-3Ytk

Yes.

They can't send arbitrary traffic, though; they can only send valid HTTP requests, and they don't get access to your cookies (because the hostname doesn't match), so the "only" thing they can do is get access to things that an unauthenticated HTTP client running as you could get access to.

This has been true since almost the first web browsers - XHR wasn't a thing, but you could send GET requests with <img src="http://192.168.0.1/reboot-everything"> or even POST requests with forms (a little easier once JS let you create and submit forms from JS, but certainly doable in pure HTML).

And the problem is there's no way to tell what IP addresses to block. Special-casing 127.0.0.1 is at least a clear enough solution to articulate (though it breaks all sorts of use cases where HTTP to localhost on a custom domain name is intended), but should you also block all the RFC 1918 space? Doesn't that break the vast majorities of companies that have internal websites named wiki.example.com or wiki.corp.example.com? And some companies don't even use RFC 1918 space, they use public IPv4 ranges they own for internal routing.

It gets worse - the other problem here is that IP-based access to resources on the public internet is also vulnerable. If you're at, say, a university which has IP-based access to some journals, any website can send HTTP requests to those journals from the university.

The real right solution here is to avoid IP-based access controls, either on the public internet or on your private network - preferably by not having a private network or at least not trusting it, BeyondCorp style. Every HTTP request that does stuff on your behalf needs to be explicitly authenticated, even if it comes from the private network.

This makes sense, thanks for the explanation. The unfortunate thing is that setting up a local HTTP server is easy, while proper authentication takes more work. Based on the nature of their fix, it seems Blizzard doesn't really want to expend effort on proper authentication. I wonder if browsers could send a special HTTP header indicating the host that initiated the request?
Browsers (and most other HTTP clients) already send the Host: header, and that appears to be a pretty good way to mitigate DNS rebinidng attacks. It does not save you from things like <img src> attacks.

One common use of local HTTP servers is with the express intent of hosting web pages (not just an API endpoint) that's rendered in an embedded web browser widget, or perhaps even in a real web browser, because the web is a pretty good way of showing UIs. A common example is CUPS - if you're on macOS or most desktop Linuxes, http://localhost:631 is likely to bring up the CUPS admin interface. So browsers saying "I'm a browser" is not really the (un)authorization you want - many times you want browsers, or embedded widgets made from browser code, to access them. So if the Host header isn't enough, there isn't really a better answer than proper authentication. (CUPS is pretty hardened now, but one good solution for CUPS would be a command-line utility that generated a session key and printed out a URL like http://localhost:631/?auth=a1b2c3d4.)

I know about the Host header, my thought was more about something like a Redirected-From header which would be set based on things like the host the <img src> or XHR came from. A simple sanity check on that (anything other than localhost is blocked) should suffice.
Agreed. Not that lost ago, there was a post about how a BitTorrent client had a similar issue. Just like in that case, the real vulnerability was in browsers. I wonder how many programs are going to be discovered to be “vulnerable” before people realize that they are missing the point.
What do you think browsers should do instead?
There are a few options. IMO the best is saving ip address that each DNS name is resolved to for every connection for the duration of a windows existence.
That doesn't save you from this attack - you can send the index page and some JS over with a long Cache-Control header, and then trigger the site to get loaded some days later when DNS has been changed to point to localhost. The JS will trigger the same XHR and it will succeed this time.

(.. and now that I think about caching, the various "within the same tab" solutions elsewhere in the comments don't work, because they'd force Gmail / OWA / Slack / etc. to get reloaded with a full cache flush when they change IP addresses, which everyone would hate.)

Can you really reload a website just even if it is rejecting connections just using a long Cache-Control header? I find that hard to believe.

> because they'd force Gmail / OWA / Slack / etc. to get reloaded with a full cache flush when they change IP addresses

I think security issues should be fixed even if the fix imposes an inconvenience. Especially since the inconvenience is basically just a performance issue.

Side note: I don't find the idea that this attack is hard to fix a particularly strong justification for not fixing it. Not being able to connect to arbitrary hosts is deliberately not part of the API exposed to javascript so I think it would be hard to argue that DNS rebinding isn't a bug.

Side note 2: The workaround of "don't trust" localhost doesn't prevent all DNS rebinding based attacks. For instance, if you took out an ad, you could then have a botnet to bypass any ratelimiter.

I call BS, it's the responsibility of the server to check Host headers and implement CORS. All browser provide these security measurements. There are valid use cases for having domain aliases for local ips.
> There are valid use cases for having domain aliases for local ips.

My preferred solution doesn't prevent this, and in fact it is preferred partially because some of the other solutions do.

The vulun is in the fact that the program blindly trusts incoming HTTP based only on a nonce being in both the body and a header.
No, the "vuln" is assuming that only users on the machine can access localhost. This is a completely reasonable assumption, and it is on the browsers for invalidating it.
Yes. This same thing can be used to attack web crawlers: you can cajole the crawler into making web requests to localhost or private networks.
I assume there's a similar patch for the Mac client, but this offers no protection for users running the Windows client via WINE.