Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

I created a perl script to convert the log file to a pcap file with syslog messages. Available here: https://gist.github.com/mdeweerd/47ea977028259ee9a6b6fd8f2356c65d#file-logtopcap-pl .

Version when writing this self-anwser:

#!/bin/perl
# SCRIPT TO CONVERT TEXT LOG TO PCAP FILE
# READS DATES FROM LOG FILE
# WHEN READING FROM PIPE (UNTESTED) WITHOUT DATES,
# THE CURRENT TIMESTAMP IS USED.
#
# 0, 1 or 2 parameters to the script:
#  - When 0 parameters: uses STDIN, and writes to file.pcap.
#  - When 1 parameters: Depends on extension: either .pcap file or log file.
#  - When 2 parameters: One of the files should have the .pcap extension
use File::PCAP::Writer;
use Time::HiRes qw(gettimeofday);
use Time::Piece;
use Date::Parse;
use Socket;

use List::Util qw(sum);
use POSIX qw(locale_h);
use locale;

# Make sure text is in English.
setlocale(LC_CTYPE);

my $fname='file.pcap'; # Default output file name
my $logname='';        # Default log file, when '', use STDIN

# Check CLI paramaters
if(scalar(@ARGV)==2) {
   if( $ARGV[0] =~ /.pcap$/ ) {
      $fname=$ARGV[0];
      $logname=$ARGV[1];
   } elsif( $ARGV[1] =~ /.pcap$/ ) {
      $fname=$ARGV[1];
      $logname=$ARGV[0];
   }

} elsif(scalar(@ARGV)==1) {
   if( $ARGV[0] =~ /.pcap$/ ) {
      $fname=$ARGV[0];
   } else {
      $logname=$ARGV[0];
   }
}

# Delete the output file
unlink($fname);
# Open the output file
my $fpw = File::PCAP::Writer->new( {
    fname => $fname,
    dlt => 1,
} );

# Open the input file
my $fh;
my $timeFromFile=0;
if($logname ne '') {
   open( $fh => $logname) || die "Cannot open $logname: $!";
   $timeFromFile=1;
} else {
   $fh=*STDIN;
   $logname="STDIN";
}

# Inform about what we are doing
print "Convert $logname to $fname";

# Do conversion
my $t=0;
my $usec;
while( my $line = <$fh> ) {
    chomp;
    if(length($line)==0||$line=~/^\s*$/) {
        next;
    }


    #print $t;
    if($line=~/^\[([^[]*?)\]\s?(.*)/) {
       my $timestr=$1;
       $line=$2;
       $t=str2time($1,"+0000");
       #print $timestr." ".$t;
       $usec = 0;
       $timeFromFile=1;
    } elsif(!$timeFromFile) {
       ($t, $usec) = gettimeofday();
    }
    $line="<134>".$line;
    #$line=gmtime($t)->strftime("%M %b %H:%M:%S")." ".$line;
    #print $line."\n";
    $buf=getPacketForText($line);
    $blen=$plen=length($buf);
    $fpw->packet( 0+$t, $usec, $blen, $plen, $buf );
}
#$fpw->close();


# Helper functions, adapted from  https://stackoverflow.com/questions/51061864/sending-spoofed-syslog-messages-in-perl


sub udp_checksum {
    # thanks to ikegami in post https://stackoverflow.com/questions/46181281/udp-checksum-function-in-perl
    my $packed_src_addr = shift;  # As 4-char string, e.g. as returned by inet_aton.
     my $packed_dst_addr = shift;  # As 4-char string, e.g. as returned by inet_aton.
    my $udp_packet      = shift;  # UDP header and data as a string.

    my $sum = sum(
    IPPROTO_UDP,
    length($udp_packet),
    map({ unpack('n*', $_) }
        $packed_src_addr,
        $packed_dst_addr,
        $udp_packet."\0",  # Extra byte ignored if length($udp_packet) is even.
        ),
    );

    while (my $hi = $sum >> 16) {
        $sum = ($sum & 0xFFFF) + $hi;
    }

    return ~$sum & 0xFFFF;
}

sub getPacketForText {
    $data=shift;

    $src_host = "localhost";
    $dst_host = 127.0.0.1;
    $src_port = 33333;
    $dest_port = 514;
    $cksum = 0; #initialise, we will sort this later

    #$udp_len is the udp packet length in the udp header. Its just 8 plus the length of the data
    #for this test harness we will set the data here too
    #$data = "<132> %FWSM-3-106010: Deny inbound tcp src outside:215.251.218.222/11839 dst inside:192.168.1.1/369";
    #$data="<132> May 22 08:59:12 pf: 00:00:35.159056 rule 3/0(match): block in on em2: (tos 0x0, ttl 128, id 6204, offset 0, flags [none], proto UDP (17), length 235)";
    $udp_len = 8+length($data);

    $udp_proto = 17; #17 is the code for udp


    #Prepare the udp packet, needed for the checksum to happen, then get the checksum (horrifically complicated, just google it)
    $udp_packet = pack("nnnna*", $src_port,$dest_port,$udp_len, $cksum, $data);
    $cksum = udp_checksum(inet_aton($src_host),inet_aton($dst_host),$udp_packet);

    $zero_cksum = 0; 

    #test harness checks about host IPs
    my $dst_host = (gethostbyname($dst_host))[4]; my $src_host = (gethostbyname($src_host))[4];

    # Now lets construct the IP packet
    my $ip_ver = 4;
    my $ip_len = 5;
    my $ip_ver_len = $ip_ver . $ip_len;
    my $ip_tos = 00;
    my ($ip_tot_len) = $udp_len + 20;
    my $ip_frag_id = 19245;
    my $ip_frag_flag = "010";
    my $ip_frag_oset = "0000000000000";
    my $ip_fl_fr = $ip_frag_flag . $ip_frag_oset;
    my $ip_ttl = 30;

    #H2H2nnB16C2na4a4 for the IP Header part#nnnna* for the UDP Header part.
    #To understand these, see the manual of pack function and IP and UDP Header formats
    #IP checksum ($zero_cksum is calculated by the kernel. Dont worry about it.)

    my ($pkt) = pack("H*","0000000000000000000000000800")
  #.pack('H2H2nnB16C2na4a4nnnna*',
  .pack('H2H2nnB16C2na4a4nnnna*',
    $ip_ver_len,$ip_tos,$ip_tot_len,$ip_frag_id,
    $ip_fl_fr,$ip_ttl,$udp_proto,$zero_cksum,$src_host,
    $dst_host,$src_port,$dest_port,
    $udp_len, $cksum, $data);

    ##bit that makes the socket and sends the packet
    ##socket(RAW, AF_INET, SOCK_RAW, 255) || die $!; setsockopt(RAW, 0, 1, 1);
    ##my ($destination) = pack('Sna4x8', AF_INET, $dest_port, $dst_host);
    ##send(RAW,$pkt,0,$destination);

    replace by return
    return $pkt;
}

I created a perl script to convert the log file to a pcap file with syslog messages. Available here: https://gist.github.com/mdeweerd/47ea977028259ee9a6b6fd8f2356c65d#file-logtopcap-pl .

After conversion, I just need to drag & drop the pcap files to wireshark to see them all.

Version when writing at the time of this self-anwser:post - I'll probably add the possibility to apply a timeshift:

#!/bin/perl
# SCRIPT TO CONVERT TEXT LOG TO PCAP FILE
# READS DATES FROM LOG FILE
# WHEN READING FROM PIPE (UNTESTED) WITHOUT DATES,
# THE CURRENT TIMESTAMP IS USED.
#
# 0, 1 or 2 parameters to the script:
#  - When 0 parameters: uses STDIN, and writes to file.pcap.
#  - When 1 parameters: Depends on extension: either .pcap file or log file.
#  - When 2 parameters: One of the files should have the .pcap extension
use File::PCAP::Writer;
use Time::HiRes qw(gettimeofday);
use Time::Piece;
use Date::Parse;
use Socket;

use List::Util qw(sum);
use POSIX qw(locale_h);
use locale;

# Make sure text is in English.
setlocale(LC_CTYPE);

my $fname='file.pcap'; # Default output file name
my $logname='';        # Default log file, when '', use STDIN

# Check CLI paramaters
if(scalar(@ARGV)==2) {
   if( $ARGV[0] =~ /.pcap$/ ) {
      $fname=$ARGV[0];
      $logname=$ARGV[1];
   } elsif( $ARGV[1] =~ /.pcap$/ ) {
      $fname=$ARGV[1];
      $logname=$ARGV[0];
   }

} elsif(scalar(@ARGV)==1) {
   if( $ARGV[0] =~ /.pcap$/ ) {
      $fname=$ARGV[0];
   } else {
      $logname=$ARGV[0];
   }
}

# Delete the output file
unlink($fname);
# Open the output file
my $fpw = File::PCAP::Writer->new( {
    fname => $fname,
    dlt => 1,
} );

# Open the input file
my $fh;
my $timeFromFile=0;
if($logname ne '') {
   open( $fh => $logname) || die "Cannot open $logname: $!";
   $timeFromFile=1;
} else {
   $fh=*STDIN;
   $logname="STDIN";
}

# Inform about what we are doing
print "Convert $logname to $fname";

# Do conversion
my $t=0;
my $usec;
while( my $line = <$fh> ) {
    chomp;
    if(length($line)==0||$line=~/^\s*$/) {
        next;
    }


    #print $t;
    if($line=~/^\[([^[]*?)\]\s?(.*)/) {
       my $timestr=$1;
       $line=$2;
       $t=str2time($1,"+0000");
       #print $timestr." ".$t;
       $usec = 0;
       $timeFromFile=1;
    } elsif(!$timeFromFile) {
       ($t, $usec) = gettimeofday();
    }
    $line="<134>".$line;
    #$line=gmtime($t)->strftime("%M %b %H:%M:%S")." ".$line;
    #print $line."\n";
    $buf=getPacketForText($line);
    $blen=$plen=length($buf);
    $fpw->packet( 0+$t, $usec, $blen, $plen, $buf );
}
#$fpw->close();


# Helper functions, adapted from  https://stackoverflow.com/questions/51061864/sending-spoofed-syslog-messages-in-perl


sub udp_checksum {
    # thanks to ikegami in post https://stackoverflow.com/questions/46181281/udp-checksum-function-in-perl
    my $packed_src_addr = shift;  # As 4-char string, e.g. as returned by inet_aton.
     my $packed_dst_addr = shift;  # As 4-char string, e.g. as returned by inet_aton.
    my $udp_packet      = shift;  # UDP header and data as a string.

    my $sum = sum(
    IPPROTO_UDP,
    length($udp_packet),
    map({ unpack('n*', $_) }
        $packed_src_addr,
        $packed_dst_addr,
        $udp_packet."\0",  # Extra byte ignored if length($udp_packet) is even.
        ),
    );

    while (my $hi = $sum >> 16) {
        $sum = ($sum & 0xFFFF) + $hi;
    }

    return ~$sum & 0xFFFF;
}

sub getPacketForText {
    $data=shift;

    $src_host = "localhost";
    $dst_host = 127.0.0.1;
    $src_port = 33333;
    $dest_port = 514;
    $cksum = 0; #initialise, we will sort this later

    #$udp_len is the udp packet length in the udp header. Its just 8 plus the length of the data
    #for this test harness we will set the data here too
    #$data = "<132> %FWSM-3-106010: Deny inbound tcp src outside:215.251.218.222/11839 dst inside:192.168.1.1/369";
    #$data="<132> May 22 08:59:12 pf: 00:00:35.159056 rule 3/0(match): block in on em2: (tos 0x0, ttl 128, id 6204, offset 0, flags [none], proto UDP (17), length 235)";
    $udp_len = 8+length($data);

    $udp_proto = 17; #17 is the code for udp


    #Prepare the udp packet, needed for the checksum to happen, then get the checksum (horrifically complicated, just google it)
    $udp_packet = pack("nnnna*", $src_port,$dest_port,$udp_len, $cksum, $data);
    $cksum = udp_checksum(inet_aton($src_host),inet_aton($dst_host),$udp_packet);

    $zero_cksum = 0; 

    #test harness checks about host IPs
    my $dst_host = (gethostbyname($dst_host))[4]; my $src_host = (gethostbyname($src_host))[4];

    # Now lets construct the IP packet
    my $ip_ver = 4;
    my $ip_len = 5;
    my $ip_ver_len = $ip_ver . $ip_len;
    my $ip_tos = 00;
    my ($ip_tot_len) = $udp_len + 20;
    my $ip_frag_id = 19245;
    my $ip_frag_flag = "010";
    my $ip_frag_oset = "0000000000000";
    my $ip_fl_fr = $ip_frag_flag . $ip_frag_oset;
    my $ip_ttl = 30;

    #H2H2nnB16C2na4a4 for the IP Header part#nnnna* for the UDP Header part.
    #To understand these, see the manual of pack function and IP and UDP Header formats
    #IP checksum ($zero_cksum is calculated by the kernel. Dont worry about it.)

    my ($pkt) = pack("H*","0000000000000000000000000800")
  #.pack('H2H2nnB16C2na4a4nnnna*',
  .pack('H2H2nnB16C2na4a4nnnna*',
    $ip_ver_len,$ip_tos,$ip_tot_len,$ip_frag_id,
    $ip_fl_fr,$ip_ttl,$udp_proto,$zero_cksum,$src_host,
    $dst_host,$src_port,$dest_port,
    $udp_len, $cksum, $data);

    ##bit that makes the socket and sends the packet
    ##socket(RAW, AF_INET, SOCK_RAW, 255) || die $!; setsockopt(RAW, 0, 1, 1);
    ##my ($destination) = pack('Sna4x8', AF_INET, $dest_port, $dst_host);
    ##send(RAW,$pkt,0,$destination);

    replace by return
    return $pkt;
}

I created a perl script to convert the log file to a pcap file with syslog messages. Available here: https://gist.github.com/mdeweerd/47ea977028259ee9a6b6fd8f2356c65d#file-logtopcap-pl .

After conversion, I just need to drag & drop the pcap files to wireshark to see them all.

Version at the time of this post - I'll probably add the possibility to apply a timeshift:

#!/bin/perl
# SCRIPT TO CONVERT TEXT LOG TO PCAP FILE
# READS DATES FROM LOG FILE
# WHEN READING FROM PIPE (UNTESTED) WITHOUT DATES,
# THE CURRENT TIMESTAMP IS USED.
#
# 0, 1 or 2 parameters to the script:
#  - When 0 parameters: uses STDIN, and writes to file.pcap.
#  - When 1 parameters: Depends on extension: either .pcap file or log file.
#  - When 2 parameters: One of the files should have the .pcap extension
use File::PCAP::Writer;
use Time::HiRes qw(gettimeofday);
use Time::Piece;
use Date::Parse;
use Socket;

use List::Util qw(sum);
use POSIX qw(locale_h);
use locale;

# Make sure text is in English.
setlocale(LC_CTYPE);

my $fname='file.pcap'; # Default output file name
my $logname='';        # Default log file, when '', use STDIN

# Check CLI paramaters
if(scalar(@ARGV)==2) {
   if( $ARGV[0] =~ /.pcap$/ ) {
      $fname=$ARGV[0];
      $logname=$ARGV[1];
   } elsif( $ARGV[1] =~ /.pcap$/ ) {
      $fname=$ARGV[1];
      $logname=$ARGV[0];
   }

} elsif(scalar(@ARGV)==1) {
   if( $ARGV[0] =~ /.pcap$/ ) {
      $fname=$ARGV[0];
   } else {
      $logname=$ARGV[0];
   }
}

# Delete the output file
unlink($fname);
# Open the output file
my $fpw = File::PCAP::Writer->new( {
    fname => $fname,
    dlt => 1,
} );

# Open the input file
my $fh;
my $timeFromFile=0;
if($logname ne '') {
   open( $fh => $logname) || die "Cannot open $logname: $!";
   $timeFromFile=1;
} else {
   $fh=*STDIN;
   $logname="STDIN";
}

# Inform about what we are doing
print "Convert $logname to $fname";

# Do conversion
my $t=0;
my $usec;
while( my $line = <$fh> ) {
    chomp;
    if(length($line)==0||$line=~/^\s*$/) {
        next;
    }


    #print $t;
    if($line=~/^\[([^[]*?)\]\s?(.*)/) {
       my $timestr=$1;
       $line=$2;
       $t=str2time($1,"+0000");
       #print $timestr." ".$t;
       $usec = 0;
       $timeFromFile=1;
    } elsif(!$timeFromFile) {
       ($t, $usec) = gettimeofday();
    }
    $line="<134>".$line;
    #$line=gmtime($t)->strftime("%M %b %H:%M:%S")." ".$line;
    #print $line."\n";
    $buf=getPacketForText($line);
    $blen=$plen=length($buf);
    $fpw->packet( 0+$t, $usec, $blen, $plen, $buf );
}
#$fpw->close();


# Helper functions, adapted from  https://stackoverflow.com/questions/51061864/sending-spoofed-syslog-messages-in-perl


sub udp_checksum {
    # thanks to ikegami in post https://stackoverflow.com/questions/46181281/udp-checksum-function-in-perl
    my $packed_src_addr = shift;  # As 4-char string, e.g. as returned by inet_aton.
     my $packed_dst_addr = shift;  # As 4-char string, e.g. as returned by inet_aton.
    my $udp_packet      = shift;  # UDP header and data as a string.

    my $sum = sum(
    IPPROTO_UDP,
    length($udp_packet),
    map({ unpack('n*', $_) }
        $packed_src_addr,
        $packed_dst_addr,
        $udp_packet."\0",  # Extra byte ignored if length($udp_packet) is even.
        ),
    );

    while (my $hi = $sum >> 16) {
        $sum = ($sum & 0xFFFF) + $hi;
    }

    return ~$sum & 0xFFFF;
}

sub getPacketForText {
    $data=shift;

    $src_host = "localhost";
    $dst_host = 127.0.0.1;
    $src_port = 33333;
    $dest_port = 514;
    $cksum = 0; #initialise, we will sort this later

    #$udp_len is the udp packet length in the udp header. Its just 8 plus the length of the data
    #for this test harness we will set the data here too
    #$data = "<132> %FWSM-3-106010: Deny inbound tcp src outside:215.251.218.222/11839 dst inside:192.168.1.1/369";
    #$data="<132> May 22 08:59:12 pf: 00:00:35.159056 rule 3/0(match): block in on em2: (tos 0x0, ttl 128, id 6204, offset 0, flags [none], proto UDP (17), length 235)";
    $udp_len = 8+length($data);

    $udp_proto = 17; #17 is the code for udp


    #Prepare the udp packet, needed for the checksum to happen, then get the checksum (horrifically complicated, just google it)
    $udp_packet = pack("nnnna*", $src_port,$dest_port,$udp_len, $cksum, $data);
    $cksum = udp_checksum(inet_aton($src_host),inet_aton($dst_host),$udp_packet);

    $zero_cksum = 0; 

    #test harness checks about host IPs
    my $dst_host = (gethostbyname($dst_host))[4]; my $src_host = (gethostbyname($src_host))[4];

    # Now lets construct the IP packet
    my $ip_ver = 4;
    my $ip_len = 5;
    my $ip_ver_len = $ip_ver . $ip_len;
    my $ip_tos = 00;
    my ($ip_tot_len) = $udp_len + 20;
    my $ip_frag_id = 19245;
    my $ip_frag_flag = "010";
    my $ip_frag_oset = "0000000000000";
    my $ip_fl_fr = $ip_frag_flag . $ip_frag_oset;
    my $ip_ttl = 30;

    #H2H2nnB16C2na4a4 for the IP Header part#nnnna* for the UDP Header part.
    #To understand these, see the manual of pack function and IP and UDP Header formats
    #IP checksum ($zero_cksum is calculated by the kernel. Dont worry about it.)

    my ($pkt) = pack("H*","0000000000000000000000000800")
  #.pack('H2H2nnB16C2na4a4nnnna*',
  .pack('H2H2nnB16C2na4a4nnnna*',
    $ip_ver_len,$ip_tos,$ip_tot_len,$ip_frag_id,
    $ip_fl_fr,$ip_ttl,$udp_proto,$zero_cksum,$src_host,
    $dst_host,$src_port,$dest_port,
    $udp_len, $cksum, $data);

    ##bit that makes the socket and sends the packet
    ##socket(RAW, AF_INET, SOCK_RAW, 255) || die $!; setsockopt(RAW, 0, 1, 1);
    ##my ($destination) = pack('Sna4x8', AF_INET, $dest_port, $dst_host);
    ##send(RAW,$pkt,0,$destination);

    # replace by return
    return $pkt;
}