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.)
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.
> 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).