Fuzzing BGP with Node.js

Computer programs are seldom (never?) bug-free. After more than a few lines of code, it can be difficult to verify that a program or network service will always behave predictably, especially if given spurious input. Sometimes the best way to shake out the bugs is to seriously mistreat your code. Enter a technique called “fuzzing”.

What is Fuzzing?

If you’re a software developer, it’s better to find bugs in your code before your users do. If you’re a system or network administrator, you’ll want to have as much confidence as possible in the hardware and software you deploy. Best to take some time up front, prior to deployment in a production environment, to ensure the resiliency of your efforts. One way to do that is through fuzzing.

Fuzz testing — also known as fuzzing — is a method of providing invalid, unexpected, or random data as input to a computer program or network service in an attempt to find (and fix or exploit) weaknesses in the underlying software or firmware. Even with the most heavily-secured and rigorously-tested systems, fuzzing can sometimes expose vulnerabilities or memory leaks that otherwise would have gone unnoticed… at least until someone with malicious intent discovers them or a malfunctioning network device or service starts sending you malformed data.

Core of the Internet

As an example of an important item to test, consider the lowly network router. (No offense intended to any self-aware network routers out there.)

If major network routers become compromised, mayhem and/or evilness often ensues. Many Internet routers use the Border Gateway Protocol, or BGP, to communicate with other routers in order to route network traffic from place to place.

BGP has been around a while, and has remained at version 4 of the protocol since 2006. What would happen if we sent invalid BGP OPEN requests using a version number of 5, 0, or 255? In the spirit of experimentation, I decided to write a simple client application in Node.js that could “fuzz” the BGP version field — a single-byte field in the BGP packet header.

The Code

An example client and server are below. You can use the server so the client has an endpoint to connect with. In the real world, you might run the client against an actual router, but I don’t suggest you do so unless you have permission.

NOTE: The example server needs to run as a privileged user in order to listen on the default TCP port for BGP (179), so you will need to use sudo if you want to do that.

Requirements

  • Node.js (to run the examples)
  • Wireshark or tcpdump (to inspect the resulting packet capture)
  • Administrative access if you want to run the dummy server on TCP port 179

Running the Examples

  1. Start the server. By default, it will listen on the loopback address, 127.0.0.1, TCP port 179.
    sudo node ./server.js
  2. Start a packet capture on port 179, using your loopback interface (lo0 on Mac, lo on Linux), saving the captured packets to a file called bgp.pcap.
    sudo tshark -i lo0 -w bgp.pcap 'port 179'
  3. Run the client. By default, it will connect to the loopback address, 127.0.0.1, TCP port 179. The source port will be a random client port, so sudo is not necessary here.
    node ./client.js
  4. Stop the packet capture and inspect the resulting bgp.pcap file using a tool like Wireshark, tshark, or tcpdump.

So What?

In reality, the code below is fairly limited and there are tons of better tools to perform fuzz testing. But if you have the curiosity and time, it can be a great learning experience to dig into network protocols using these techniques.

In the next post, we will analyze the pcap and show how to write a custom Wireshark dissector in Lua.

BGP Version Fuzzer Client

// usage: node ./client.js [remoteHost] [remotePort]

var net    = require('net')
  , client = new net.Socket();

var remoteHost = process.argv[2] || '127.0.0.1'
  , remotePort = process.argv[3] || 179;

function bgp_open(version) {
  var length = 29
    , offset = 0
    , buf    = new Buffer(length);

  var types  = {
            "open": 1,
          "update": 2,
    "notification": 3,
       "keepalive": 4,
    "routerefresh": 5
  };

  // Marker - 16 bytes
  // Set all bits to 1 for compatibility
  buf.fill(255, offset, offset + 16);
  offset += 16;

  // Length - 2 bytes
  buf.writeInt16BE(length, offset);
  offset += 2;

  // Type - 1 byte
  buf.writeInt8(types.open, offset);
  offset += 1;

  // Version - 1 byte
  // this is what we're fuzzing
  buf.writeInt8(version, offset, true);
  offset += 1;

  // Autonomous System (AS) Number - 2 bytes
  buf.writeInt16BE(1, offset);
  offset += 2;

  // Hold Down Time - 2 bytes
  buf.writeInt16BE(1, offset);
  offset += 2;

  // BGP Identifier - 4 bytes
  buf.writeInt32BE(168430090, offset); // 10.10.10.10
  offset += 4;

  // Optional Parameter Length - 1 byte
  buf.writeInt8(0, offset);

  // If we had parameters:
  // offset += 1;
  // more buffer writes...

  return buf;
}

client.connect(remotePort, remoteHost, function() {
  // when connected, send BGP OPEN messages
  // using version numbers 0 through 255

  var version;
  for (version = 0; version < 256; version++) {
    client.write(bgp_open(version));
  }
  client.end();
});

client.on('close', function() {
  client.destroy();
  process.exit(0);
});

BGP Version Fuzzer Server

// usage: node ./server.js [listenAddress] [listenPort]

var net    = require('net')
  , server = net.createServer();

var serverHost = process.argv[2] || '127.0.0.1'
  , serverPort = process.argv[3] || 179;

server.listen(serverPort, serverHost);