#!/usr/bin/perl
################################################################################
# httpAnalyzer.pl
################################################################################
# This script reads in a pcap network capture to analyze HTTP traffic. It
# will output an HTML file in the current directory that contains the analysis
# of every HTTP transfer recorded in the pcap file. The analysis output file has
# been tested to work in Firefox 3.5+ and may be very large. Please allow it to
# fully load before attempting to interact with it.
#
# This script requires the following modules:
# Net::LibNIDS
# Compress::Zlib
# MIME::Base64
# Digest::MD5
# Additionally, Net::LibNIDS version 0.01 contains a serious bug
# (http://rt.cpan.org/Public/Bug/Display.html?id=52879) that will prevent it
# from working. A patch and a Gentoo ebuild are both available from
# http://modtwo.com
#
# Usage:
# httpAnalyzer.pl capture.pcap
#
# Author: Tom Samstag http://modtwo.com
# Version: 0.1
# Date: 2010-02-01
#
# Copyright 2009 Tom Samstag, modtwo (at) modtwo (dot) com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
use strict;
use warnings;
use English;
use Net::LibNIDS;
use Compress::Zlib;
use MIME::Base64;
use Digest::MD5 qw(md5_hex);
use constant VERSION=>0.1;
if (@ARGV != 1)
{
print STDERR "Usage: $0 pcapFile\n";
exit 1;
}
my $filename = $ARGV[0];
Net::LibNIDS::param::set_filename($filename);
Net::LibNIDS::init(); # processes all parameters
Net::LibNIDS::tcp_callback(\&collector ); # a callback to be called for each packet
open OUTPUT_STREAM, ">$filename.httpAnalyzer.html";
print_output_header();
Net::LibNIDS::run(); # start the collection
print_output_footer();
my %data;
my $transfersCompleted = 0;
sub collector {
my $connection = shift;
if($connection->state == Net::LibNIDS::NIDS_JUST_EST()) {
$connection->server->collect_on; #start tracing data from server
$connection->client->collect_on; #start tracing data from client
}
elsif($connection->state == Net::LibNIDS::NIDS_DATA()) {
my $key = $connection->client_ip . ':' . $connection->client_port . ' -> ' .
$connection->server_ip . ':' . $connection->server_port;
my $server_new = $connection->server->count_new;
my $client_new = $connection->client->count_new;
if($server_new)
{
$data{$key}->{server} .= $connection->server->data;
if($data{$key}->{server} =~ /\A(GET|POST) [^\r\n]* HTTP\/1\.[01][\r\n].*?^\r\n/ms)
{
$data{$key}->{completed_request} = $MATCH;
$data{$key}->{server} = $POSTMATCH;
}
}
elsif($client_new)
{
$data{$key}->{client} .= $connection->client->data;
if ($data{$key}->{client} =~ /\AHTTP\/1\.[01] \d+(?:.*?)^Content-Length: (\d+)[\r\n](:?.*?)^\r\n/ms)
{
my ($responseHeaders, $length) = ($MATCH, $1);
if (length($POSTMATCH) >= $length)
{
my $content = substr($POSTMATCH, 0, $length);
my $request = $data{$key}->{completed_request};
$transfersCompleted++;
print_file_transfer($key, $transfersCompleted, $request, $responseHeaders, $content);
$data{$key}->{client} = substr($POSTMATCH, $length);
$data{$key}->{completed_request} = '';
}
}
}
}
}
sub print_output_header
{
print OUTPUT_STREAM <<'HERE';
HERE
print OUTPUT_STREAM "
HTTP Analysis of $filename
\n";
print OUTPUT_STREAM <<'HERE';
HERE
}
sub print_file_transfer
{
my ($key, $transferNumber, $request, $responseHeaders, $content) = @_;
# extract the content-type from the responseHeaders
my $contentType = '';
chomp ($contentType = $1) if ($responseHeaders =~ /^Content-Type:\s*([^\r\n]*)/m);
# gunzip the content if it was served with Content-Encoding: gzip
$content = Compress::Zlib::memGunzip($content) if ($responseHeaders =~ /^Content-Encoding:\s*gzip\s*$/mi);
my $h2 = '';
if ($key =~ /([0-9.]*):(\d*) -> ([0-9.]*):(\d*)/)
{
$h2 = "$1:$2 - $3:$4";
}
if ($request =~ /^Host:\s*([^\r\n]*)/mg)
{
$h2 .= " ($1)";
}
$request =~ /^\S*\s+([^\r\n\s]*)/;
my $h3 = $1;
my @tabs;
# Request
{
my $data = "
\n";
push @tabs, {name=>"GET Params", data=>$data};
}
# File Info
{
my $size = length($content);
my $md5 = md5_hex($content);
my $download = "DownloadWarning: downloaded files may be malicious.";
my $data = "