Skip to main content

Always Secure Your localhost Servers

Recently I was surprised to learn that web browsers allow any site you visit to make requests to resources on localhost (and that they will happily allow unreported mixed-content). If you'd like to test this out, run an HTTP server on port 8080 (for instance with python -m http.server 8080) and then visit this page.

You should see "Found: HTTP (8080)" listed and that's because the Javascript on that page made an HTTP GET request to your local server to determine that it was running. Chances are it detected other services as well (for instance if you run Tor or Keybase locally).

There are two implications from this that follow:
  1. Website owners could potentially use this to collect information about what popular services are running on your local network.
  2. Malicious actors could use this to exploit vulnerabilities in those services.
Requests made this way are limited in certain ways since they're considered opaque, meaning that the web page isn't able to read the response body or headers. But even with these restrictions a request can do all kinds of nasty things unless the local server is properly secured.

For instance, at one point Redis was vulnerable to these types of attacks because its protocol is text-over-TCP (just like HTTP) so any web page you visit could craft an HTTP POST request that mimics normal Redis commands. There's an excellent write-up on this vulnerability here (which has since been fixed).

Similarly, if you run the popular gaming platform Steam on Linux, at the time of writing this the main application opens a TCP listener on port 57343. I have no idea what that port is for but I do know that visiting this web page while Steam is open will cause the entire UI to freeze, as well as most games, until the tab is closed: [WARNING: don't blame me if it crashes your game] https://wybiral.github.io/steam-block/

This works because the favicon on that page is actually an HTTP GET request to the TCP server which never closes, thus blocking their listener. It may even be possible to attack the server in other ways with specifically crafted requests (I was able to crash the application using a large enough payload).

These types of vulnerabilities are widespread mostly because application developers assume that the server will only be locally-accessible and not available to every website the user visits. Hopefully this is enough proof to justify why better security measures need to be taken with local servers even if you don't intend to forward the ports you're using to the outside world.

So, as developers what can we do to prevent this kind of attack?

For HTTP and WebSocket servers you can add authentication, CSRF protection, and restrict access based on the page origin (which browsers should include in the request headers).

For TCP servers it could be harder depending on your application. You can detect HTTP-like request headers and block the connection (this seems to be what Redis is doing now). Or require some sort of handshake/authentication that the browser won't perform and reject connections based on that.

As far as preventing fingerprinting and service discovery this way... I'm not entirely sure what the best way to prevent this would be other than ungracefully failing the requests (which a lot of HTTP frameworks probably don't make easy). If anyone has any ideas here feel free to drop me a tweet @davywtf.

    Popular posts from this blog

    DIY Solar Powered LoRa Repeater (with Arduino)

    In today's video I be built a solar powered LoRa signal repeater to extend the range of my LoRa network. This can easily be used as the basis for a LoRa mesh network with a bit of extra code and additional repeaters. Even if you're not into LoRa networks all of the solar power hardware in this video can be used for any off-the-grid electronics projects or IoT nodes!  

    A Lesson in LoRa Module P2P Standards (or the Lack Thereof)

    I got a handful of LoRa modules from Reyax a while back, the RYLR896 model based on Semtech SX1276 chips. Instead of using an SPI interface they operate over UART using a small set of AT commands. This made them easier to work with since I didn't have to dig too deeply into a bunch of SPI registers and Semtech specs and they communicate between one another really well. My Espruino JS module for them is available here , which I've used in a few of my YouTube videos. And more recently I've written a MicroPython module for them here .   (A pair of Reyax RYLR896  modules) But, always being on the lookout for different boards and platforms I eventually ended up with a few Maduino LoRa boards. These are cool because they have an Arduino-compatible ATmega328 and the same Semtech LoRa chip (via an RFM95) both integrated on one board. They weren't compatible with Espruino or MicroPython though, and they used the SPI interface instead of AT commands so I knew I would need to lo...