#!/usr/bin/perl
########################################################
#
# Program: isasniffer.pl v0.00001
# Programmer: Void Main
# Born on Date: 15 September, 2004
#
# Requires Perl, MySQL and Net::RawIP Perl module.
# See: http://security.royans.net/info/rawip/
# I run v0.1 of the Net::RawIP module.
#
# This script should run on a Linux machine that has a
# network span on the client side of your ISA firewalls
# It currently does a couple of things:
#
# 1) Watches MS Proxy "Hello" packets and records
#    all programs seen accessing the network in a
#    MySQL database.
#
# 2) Sniff out korgo infected machines and Netsky.Z
#    infected machines and log them to a MySQL database.
#    This should probably be a separate program but
#    what the hey.
#
# Currently only logs these attributes:
# First Seen Time, Last Seen Time, IP Address,
# Machine Name, User Name, Program Name
#
# To Do:
# I would also like to record intended destination
# address and destination port, however this thing
# is currently watching thousands of clients and
# if they become infected with some sort of scanner
# it would probably overload the database fast and
# in a hurry. I find that this thing serves a very
# interesting purpose just the way it stands.
#
# NOTE: This code is really ugly and poorly written.
#       I didn't actually plan this program out but
#       rather hacked in things as needed. I should
#       rewrite it and if I did I would cut out the
#       duplicate subroutine, get rid of the virus
#       table and do everything in appz. However,
#       I am lazy, and this is working for me so
#       it's on the back burner.
#
########################################################

# Set the interface you want to sniff:
$interface = "eth1";
# Grab UDP traffic destined for port 1745 (MS Proxy)
$filter    = "proto \\udp and (dst port 1745)";
# Seconds before resetting internal prog list queue
$interval  = "60";
# PID file
$pidfile   = "/var/run/isasniffer.pid";

use DBI;
use Net::RawIP;
use Socket;

# Create pid file
open(RUN,">$pidfile");
print RUN "$$";
close(RUN);

my $dbname   = "isasniffer";
my $virustbl = "virus";
my $appztbl  = "appz";
my $dsn      = "DBI:mysql:$dbname";
my $dbuser   = "root";
my $dbpass   = "";
my $db       = DBI->connect( $dsn, $dbuser, $dbpass );
my $sql      = "";

$a           = new Net::RawIP;
$tm          = time();
print "------ " . localtime() . " ------\n";

# Start sniffing based on filter
$pcap=$a->pcapinit($interface,$filter,1500,30);

# Loop continuously reading filtered packets
loop $pcap,-1,\&inspectpkt,\@a;

##########################
# subroutine inspectpkt
##########################
sub inspectpkt {

  # Get src/dst addr and ports from packet along with payload
  $a->bset(substr($_[2],14));
  ($ipsrc,$ipdst,$source,$dest,$data) = $a->get({ ip=>[qw(saddr daddr)],tcp=>[qw(data source dest)]});

  # Get command type from packet
  ($cmd) = unpack("x24 H4",$data);

  ##############################################
  # MSproxy "Connect" packet (For Korgo)
  ##############################################
  if ($cmd eq "071e") {

    # and only interested in packets destined for port 445
    ($msdport) = unpack("x46 H4",$data);
    if ($msdport eq "01bd") {

      ####################################################
      # Host must be infected with Korgo, get the app
      ####################################################

      $inf = '~Korgo';
      &logvirus;

    }

  ##############################################
  # MSproxy "UDP Associate" packet (For Netsky)
  ##############################################
  } elsif ($cmd eq "0705") {

    ($proggy) = unpack("x142 A16",$data);
    $proggy =~ s/\0//g;
    if ($proggy =~ /Jammer2nd\.exe/) {

      ####################################################
      # Host must be infected with Netsky.Z, get the app
      ####################################################

      $inf = '~Netsky.Z';
      &logvirus;

    }
 
  ###################################
  # MSproxy "Hello" packet
  ###################################
  } elsif ($cmd eq "0500") {

    &logappz;

  }

}

sub logvirus {

  ($proggy) = unpack("x142 A16",$data);
  $proggy =~ s/\0//g;
  $ipaddr = inet_ntoa(pack("N",$ipsrc));

  # Get time and reset list if timer expires
  $now = time();
  if (($now - $interval) > $tm) {
     $tm = $now;
     print "------ " . localtime() . " ------\n";
     @inflst = '';
  }

  # Only report infected host once per timer period
  if (!grep(/$ipaddr/,@inflst)) {

    push(@inflst,$ipaddr);

    @nmbarr = '';
    open(NMB,"nmblookup -A $ipaddr | grep '<03>' | cut -f1 -d'<' | tr -d ' ' | tr -d '\t'|");
    @nmbarr = <NMB>;
    close(NMB);
    chomp(@nmbarr);
    $hostname = $nmbarr[0];
    $username = $nmbarr[1];

    @nmbarr = '';
    open(NMB,"nmblookup -A $ipaddr | grep '<20>' | cut -f1 -d'<' | tr -d ' ' | tr -d '\t'|");
    @nmbarr = <NMB>;
    close(NMB);
    chomp(@nmbarr);
    $hostname = $nmbarr[0];

    @dialarr = '';
    if ($ipaddr =~ /^10\.4[67]\.0\./ || $ipaddr =~ /^10\.254\.4[67]\./) {
      open(DIAL,"/root/bin/getdialuser.php $ipaddr|");
      @dialarr = <DIAL>;
      close(DIAL);
      $username = $dialarr[0];
    }

    print "$ipaddr,$hostname,$username,$proggy,$inf\n";
    $sql  = "SELECT COUNT(*) from $virustbl ";
    $sql .= "where ip = '$ipaddr' ";
    $sql .= "and hn = '$hostname' ";
    $sql .= "and un = '$username' ";
    $sql .= "and inf = '$inf' ";
    $sql .= "and app = '$proggy'";
    my $sth = $db->prepare($sql);
    $sth->execute();
    my $numrows = $sth->fetchrow_array;
    $sth->finish();

    if ($numrows > 0) {
      $sql  = "UPDATE $virustbl set dt = now() ";
      $sql .= "where ip = '$ipaddr' ";
      $sql .= "and hn = '$hostname' ";
      $sql .= "and un = '$username' ";
      $sql .= "and inf = '$inf' ";
      $sql .= "and app = '$proggy'";
    } else {
      $sql  = "INSERT INTO $virustbl (ip";
      if ($hostname) { $sql .= ",hn"; }
      if ($username) { $sql .= ",un"; }
      if ($inf)      { $sql .= ",inf"; }
      if ($proggy)   { $sql .= ",app"; }
      $sql .= ",dt,fs) VALUES ('$ipaddr'";
      if ($hostname) { $sql .= ",'$hostname'"; }
      if ($username) { $sql .= ",'$username'"; }
      if ($inf)      { $sql .= ",'$inf'"; }
      if ($proggy)   { $sql .= ",'$proggy'"; }
      $sql .= ",now(),now())";
    }
    $sth = $db->prepare($sql);
    $sth->execute();
    $sth->finish();

  }

}

# This is really almost an exact duplicate
# of the above logvirus subroutine and should
# actually be combined into one. I'm too
# lazy to do it now though....

sub logappz {

  $pktlen = length($data);
  $strlen = $pktlen - 206 - 22;

  if ($strlen > 0) {

    $clidata = '';
    ($clidata) = unpack("x208 A$strlen",$data);
    $clidata =~ s/\0/\,/g;
    ($username,$gbg,$proggy,$hostname) = split(",",$clidata);
    $ipaddr = inet_ntoa(pack("N",$ipsrc));

    # Get time and reset list if timer expires
    $now = time();

    if (($now - $interval) > $tm) {
       $tm = $now;
       @appzlst = '';
    }

    $cmdstr = $ipaddr . $clidata;
    # Only report infected host once per timer period
    if (!grep(/$cmdstr/,@appzlst)) {

      push(@appzlst,$cmdstr);
#      print "$ipaddr,$hostname,$username,$proggy\n";
      $sql  = "SELECT COUNT(*) from $appztbl ";
      $sql .= "where ip = '$ipaddr' ";
      $sql .= "and hn = '$hostname' ";
      $sql .= "and un = '$username' ";
      $sql .= "and app = '$proggy'";
      my $sth = $db->prepare($sql);
      $sth->execute();
      my $numrows = $sth->fetchrow_array;
      $sth->finish();

      if ($numrows > 0) {
        $sql  = "UPDATE $appztbl set dt = now() ";
        $sql .= "where ip = '$ipaddr' ";
        $sql .= "and hn = '$hostname' ";
        $sql .= "and un = '$username' ";
        $sql .= "and app = '$proggy'";
      } else {
        $sql  = "INSERT INTO $appztbl (ip";
        if ($hostname) { $sql .= ",hn"; }
        if ($username) { $sql .= ",un"; }
        if ($proggy)   { $sql .= ",app"; }
        $sql .= ",dt,fs) VALUES ('$ipaddr'";
        if ($hostname) { $sql .= ",'$hostname'"; }
        if ($username) { $sql .= ",'$username'"; }
        if ($proggy)   { $sql .= ",'$proggy'"; }
        $sql .= ",now(),now())";
      }
      $sth = $db->prepare($sql);
      $sth->execute();
      $sth->finish();

    }

  }

}

unlink($pidfile);
