#!/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';
Filter:
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"; foreach (split /[\r\n]+/, $request) { /^([^:]*:?)(.*)$/; $data .= "
$1
$2
\n"; } $data .= "
\n"; push @tabs, {name=>"Request", data=>$data}; } # Response { my $data = "
\n"; foreach (split /[\r\n]+/, $responseHeaders) { /^([^:]*:?)(.*)$/; $data .= "
$1
$2
\n"; } $data .= "
\n"; push @tabs, {name=>"Response", data=>$data}; } # GET parameters if ($request =~ /^GET\s+[^\s\r\n\?]*\?([^\r\n\s]*)/i) { my $params = $1; my $data = "
\n"; foreach (split /&/, $params) { /^(.*)=(.*)$/; $data .= "
$1 =
$2
\n"; } $data .= "
\n"; push @tabs, {name=>"GET Params", data=>$data}; } # File Info { my $size = length($content); my $md5 = md5_hex($content); my $download = "Download Warning: downloaded files may be malicious."; my $data = "
\n"; $data .= "
Size =
$size
\n"; $data .= "
MD5 =
$md5
\n"; $data .= "
Download Link
$download
\n"; $data .= "
\n"; push @tabs, { name=>"File Info", data=>$data }; } # Image if ($contentType =~ /^image\/(gif|jpeg|png)/i) { push @tabs, { name=>"Image", data=>"" }; } # Source if ($contentType =~ /^text\/xml\b/i) { my $escapedContent = $content; $escapedContent =~ s/&/&/g; $escapedContent =~ s//>/g; $escapedContent =~ s/ / /g; $escapedContent =~ s/$//mg; push @tabs, { name=>"Source", data=>"

					$escapedContent
				
" }; } # Query if ($contentType =~ /^text\/xml\b/i) { my $escapedContent = $content; $escapedContent =~ s/$/\\/mg; $escapedContent =~ s/\\$//; my $data = "
"; push @tabs, { name=>"Query", data=>$data }; } # Apple PList if ($contentType =~ /^text\/xml\b/i && $content =~ m#http://www.apple.com/DTDs/PropertyList-1.0.dtd#) { $_ = $content; s/<\?.*?\?>//g; s///g; s/<\/?plist.*?>//g; s//{/g; s/<\/dict>/},/g; s//[/g; s/<\/array>/],/g; s//"/g; s/<\/string>/",/g; s///g; s/<\/integer>/,/g; s///g; s/<\/key>/: /g; s/<(true|false)\/>/$1,/g; s/,\s*$//mg; s/ / /g; s/$//mg; push @tabs, { name=>"Apple PList", data=>"

					$_
				
" }; } print OUTPUT_STREAM <<"HERE";

$h2

$h3

\n"; print OUTPUT_STREAM "
\n"; } sub print_output_footer { print OUTPUT_STREAM "

Generated by httpAnalyzer.pl version ".VERSION." available from modtwo.com

"; }