HTTP-PCAP in Node

Node is a wonder hacking platform and in this blog post I will show you how to quickly utilise the internal HTTP parser to extract HTTP sessions from PCAP files, which I am sure you already know is very useful in a wide range of scenarios - from hacking WiFi networks, to doing responder stuff, etc. Let's dive in!

Our journey starts with pcap for node so that we can read those pesky packet files - but you can also do active sniffing as well. To get the ball rolling we need to install pcap in a new folder like this:

/path/to/work/dir $ npm install pcap

If you are lucky and the bindings compile without any problems you should have the pcap library ready.

const pcap = require('pcap')

Let's setup a basic script which will allow us to read all packets. Keep in mind that because HTTP is based on top of TCP we need to piece together the network streams. Luckily for us we don't need a lot of knowledge how this work as there is a TCPTracker helper object that we can utilise for this task.

const sess = pcap.createOfflineSession('./file/to/read')
const trck = new pcap.TCPTracker()

sess.on('packet', (rawPacket) => {
  var packet = pcap.decode.packet(rawPacket)

  trck.track_packet(packet)
})

trck.on('session', (session) => {
  // TODO: do something with the session
})

This is very standard and not very interesting. Now this is where Node's powerful streaming HTTP parser comes very handy. We will access the parser from the bindings and also extract the methods dictionary which we will use later. We also need to get the callback names so that we can intercept events.

const binding = process.binding('http_parser')
const HTTPParser = binding.HTTPParser
const methods = binding.methods

const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0
const kOnBody = HTTPParser.kOnBody | 0

So far so good. The last part is to plug the parser into the session so that we can consume the HTTP streams out of the PCAP TCP sessions. The whole action takes place inside the session event on the TCPTracker.

trck.on('session', (session) => {
  session.req = new HTTPParser(HTTPParser.REQUEST)
  session.res = new HTTPParser(HTTPParser.RESPONSE)

  session.req[kOnHeadersComplete] = (
    versionMajor,
    versionMinor,
    headers,
    method,
    url,
    statusCode,
    statusMessage,
    upgrade,
    shouldKeepAlive
  ) => {
    let head = `${methods[method]} ${url} HTTP/${versionMinor}.${versionMinor}\r\n`

    for (let i = 0; i < headers.length; i += 2) {
      head += `${headers[i]}: ${headers[i + 1]}\r\n`
    }

    head += '\r\n'

    session.req.buf = new Buffer(head)
  }

  session.req[kOnBody] = (b, start, len) => {
    session.req.buf = Buffer.concat([
      session.req.buf,
      b.slice(start, start + len),
    ])
  }

  session.res[kOnHeadersComplete] = (
    versionMajor,
    versionMinor,
    headers,
    method,
    url,
    statusCode,
    statusMessage,
    upgrade,
    shouldKeepAlive
  ) => {
    let head = `HTTP/${versionMinor}.${versionMinor} ${statusCode} ${statusMessage}\r\n`

    for (let i = 0; i < headers.length; i += 2) {
      head += `${headers[i]}: ${headers[i + 1]}\r\n`
    }

    head += '\r\n'

    session.res.buf = new Buffer(head)
  }

  session.res[kOnBody] = (b, start, len) => {
    session.res.buf = Buffer.concat([
      session.res.buf,
      b.slice(start, start + len),
    ])
  }

  session.on('data send', (session, data) => {
    session.req.execute(data)
  })

  session.on('data recv', (session, data) => {
    session.res.execute(data)
  })

  session.on('end', (session) => {
    session.req.finish()
    session.res.finish()

    // TODO: do something with session.req and session.res
  })
})

And we are done! See, that was kind of easy. We are using the same technique to deliver a brand new tool which will help you do all kinds of funky stuff utilising our complete gamma of information security tools.

Needless to say, our Twitter feed will give you the latest updates. Stay tuned!