How-To: Mapping Server Hits with ESP8266 and WS2812

It has never been easier to build displays for custom data visualization than it is right now. I just finished one for my office — as a security researcher I wanted a physical map that will show me from where on the planet my server is being attacked. But the same fabrication techniques, hardware, and network resources can be put to work for just about any other purpose. If you’re new to hardware, this is an easy to follow guide. If you’re new to server-side code, maybe you’ll find it equally interesting.

I used an ESP8266 module with a small 128×32 pixel OLED display connected via an SSD1306 controller. The map itself doesn’t have to be very accurate, roughly knowing the country would suffice, as it was more a decorative piece than a functional one. It’s a good excuse to put the 5 meter WS2812B LED strip I had on the shelf to use.

The project itself can be roughly divided into 3 parts:

  1. Physical and hardware build
  2. ESP8266 firmware
  3. Server-side code

It’s a relatively simple build that one can do over a weekend. It mashes together LED strips, ESP8266 wifi, OLED displays, server-side code, python, geoip location, scapy, and so on… you know, fun stuff.

The Hardware

First of all, you need a map. Obvious, right? But your map type choosing can be an important part of the project and can greatly simplify or complicate the whole setup. The issue here is the map projection type. There are many map projection types and some are more straightforward to use on this project than others. The ideal one is the equirectangular projection where latitude and longitude can be mapped in a linear fashion. I used the timezone map from the CIA because, well, it’s kind of cool. But it’s not ideal, since it seems to be a Mercator projection variation (without the poles) hence the latitude increments are not the same everywhere as the map area gets exaggerated near the poles.

I used two standard A4 sheets for a 50cm by 24cm map. The LEDs go behind the sheets, so the thickness of the paper is important if you want the lights to really stand out. I had 5 meters of LED strip with a spacing around 16mm that I cut into 10 individual strips. I positioned each strip to match the LED spacing so I have a perfect grid. This covers the full longitude range and latitudes between 65 and -35. It’s not perfect but likely covers around 95% of the internet connected world. It’s a compromise.

I choose to power the LED strip straight from the ESP8266 5V pin of the board, powered from the USB cable. It’s 300 LEDs in total and I was pretty sure that, even without doing the math, it would definitely not work. When I was prototyping, I tried that anyway and to my surprise, more than half the LEDs still lit. I figured that if I need all LEDs on than I had bigger problems to deal with because then my server would be under heavy attack! At least, that’s what I told myself as an excuse for being too lazy to add a decent power supply (and more wires). If you’re making a display that uses many of the pixels at one time, you need to use an external power supply.

In the end, I cut a small rectangular hole in the map so I can see the OLED screen with the last 4 IPs that were recently active. It kills a bit of the retro style but curiosity took the best of me. This was after all was done, so the hole looks really crappy.

Tip: LED strips are great, with only 3 wires, VCC, GND and DATA and each pixel is individually addressed. The DATA line is also directional, if you are cutting and soldering them to make a board, be carefull not to change the direction of the DATA line. Doing so will cause that strip, and all subsequent ones, to stop working. Just saying, it’s not that I was distracted and did that in the first try… (I was, I did…)

The Firmware

The ESP8266 programming was not very complicated. The setup initializes the WiFi, the OLED and the LED strips. The main loop pools the server periodically for latest IPs and geolocation and then translates that data into a particular LED with a color that relates to the time elapsed since that IP address was seen by the server, like a heat map by time. Starts red, fades to orange, yellow, green and blue until it disappears after 10 minutes.

The LED strip library used was NeoPixelBus and it’s worth mentioning that there are some limitations for the ESP8266. The ESP8266 requires hardware support to be able to send the data to the LED strip. Due to this and the restrictions on which pins are used by each hardware peripheral, our only options are GPIO1, GPIO2, or GPIO3. The most reliable seems to be GPIO3 for DMA supported operation, unfortunately this is also RDX0. The normal feature of this pin is the serial receive hence using this DMA method will not allow you to receive serial data. For this project that’s not an issue but it’s worth noting.

The OLED library used was Adafruit_SSD1306. This ESP8266 module documentation is not great and it took some time to figure out how to make it work. The ‘secret’, besides mapping the proper SDA, SCL and reset lines, is to call the Wire.begin() function in the setup before initializing the Adafruit library.

 #define OLED_RESET 4
#define OLED_SDA 2
#define OLED_SCL 14 void setup()
{
//OVERRIDE Wire.begin from Adafruit_SSD1306 to make this work on ESP8266 TTGO OLED
Wire.begin( OLED_SDA, OLED_SCL);
...

The Server-Side

The server-side code was made in python, using python-geoip geolite2 and scapy libraries. Scapy is a really great tool and library. If you’re into network testing, in particular pentesting, you probably know it. If you are not, Scapy is a packet manipulation tool written in Python by Philippe Biondi. It can forge or decode packets, send them on the wire, capture them, and match requests and replies. It can also handle tasks like scanning, tracerouting, probing, unit tests, attacks, and network discovery. Netcat has been dubbed the “TCP/IP swiss army knife”, Scapy is like that but for packets.

To start sniffing traffic one can do:

 def custom_action(packet): srcip = packet[0][1].src gps = geolite2.lookup(srcip) ... sniff(filter="ip", prn=custom_action, count=128) 

This tells Scapy to sniff for IP traffic and, for each packet, run the custom action. In this example it will use the geolite2 library to lookup the IP. The python-geoip-geolite2 package includes GeoLite2 data created by MaxMind and its use is pretty straightforward. Since the GeoLite2 database is local, there is no need for the server to make outside requests to identify the IP location. (it’s also not very accurate, better options do exist). Filtering IP traffic is not enough to identify attacks, legitimate traffic also appears. The server is private, so let’s just say a big part of it is “unwanted” traffic. If we wanted to take it further, we could connect the map to our favorite NIDS. But then again, this was a weekend project…

I also used the SimpleHTTPServer package to set up a webserver on a custom port so that the ESP8266 can connect to it and fetch the latest results, since they are regularly written to a file. To use a different port other than 80, one should use a handler, like so:

 Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)
httpd.serve_forever() 

This will create an HTTP open port at PORT and serve whatever files are in the current directory where the python code is called. This is, of course, not very safe from a security perspective.

The Results

In the end I was happy with the results. I took a lot of shortcuts and simplifications but hey, it’s a hack. There are many things to improve if I ever get to do version 2, which is clearly on my mind and involves an entire wall. Now, you can say that for the accuracy and resolution, an LCD screen is clearly much better. And it’s true. But maybe it’s not as fun. I usually ask myself how can I do XYZ project, and not why. That’s how I end up with the workshop full of stuff and a bunch of, let’s be honest, pretty much useless projects.

My take away is the enjoyment I get from making them and what I learn along the way. And there’s a blinking lights bonus in this one. Here’s a time-lapse of around 6 mins (1 sec per frame):

I fondly call China “the permanent light show”. The full source code for both firmware and server side can be found at GitHub, just in case you want to try it yourself and improve on it.

If this project isn’t your thing, don’t worry, we covered a lot of different and interesting other led strip projects in the past too.