#
# $Id: AddSignature.pm 18 2012-09-11 12:17:24Z gomor $
#
package Net::SinFP3::Output::AddSignature;
use strict;
use warnings;
use base qw(Net::SinFP3::Output);
our @AS = qw(
trusted
);
__PACKAGE__->cgBuildIndices;
__PACKAGE__->cgBuildAccessorsScalar(\@AS);
use Data::Dumper;
use Net::SinFP3::Ext::DBI::Signature;
use Net::SinFP3::Ext::DBI::IpVersion;
use Net::SinFP3::Ext::DBI::SystemClass;
use Net::SinFP3::Ext::DBI::Vendor;
use Net::SinFP3::Ext::DBI::Os;
use Net::SinFP3::Ext::DBI::OsVersion;
use Net::SinFP3::Ext::DBI::OsVersionFamily;
use Net::SinFP3::Ext::DBI::OsVersionChildren;
use Net::SinFP3::Ext::DBI::PatternBinary;
use Net::SinFP3::Ext::DBI::PatternTcpFlags;
use Net::SinFP3::Ext::DBI::PatternTcpWindow;
use Net::SinFP3::Ext::DBI::PatternTcpOptions;
use Net::SinFP3::Ext::DBI::PatternTcpMss;
use Net::SinFP3::Ext::DBI::PatternTcpWScale;
use Net::SinFP3::Ext::DBI::PatternTcpOLength;
# For easier access
my $tIpVersion = 'Net::SinFP3::Ext::DBI::IpVersion';
my $tPatternBinary = 'Net::SinFP3::Ext::DBI::PatternBinary';
my $tPatternTcpFlags = 'Net::SinFP3::Ext::DBI::PatternTcpFlags';
my $tPatternTcpWindow = 'Net::SinFP3::Ext::DBI::PatternTcpWindow';
my $tPatternTcpOptions = 'Net::SinFP3::Ext::DBI::PatternTcpOptions';
my $tPatternTcpMss = 'Net::SinFP3::Ext::DBI::PatternTcpMss';
my $tPatternTcpWScale = 'Net::SinFP3::Ext::DBI::PatternTcpWScale';
my $tPatternTcpOLength = 'Net::SinFP3::Ext::DBI::PatternTcpOLength';
my $tSystemClass = 'Net::SinFP3::Ext::DBI::SystemClass';
my $tVendor = 'Net::SinFP3::Ext::DBI::Vendor';
my $tOs = 'Net::SinFP3::Ext::DBI::Os';
my $tOsVersion = 'Net::SinFP3::Ext::DBI::OsVersion';
my $tOsVersionFamily = 'Net::SinFP3::Ext::DBI::OsVersionFamily';
my $tOsVersionChildren = 'Net::SinFP3::Ext::DBI::OsVersionChildren';
sub new {
my $self = shift->SUPER::new(
trusted => 0,
@_,
);
return $self;
}
sub run {
my $self = shift->SUPER::run(@_) or return;
my $global = $self->global;
my $log = $global->log;
my @results = $global->result;
my $db = $global->db;
my $r = $results[0];
if (ref($r) =~ /^Net::SinFP3::Result::PortError$/) {
$log->error("We cannot add a signature for a port in error");
return;
}
my $buf = '';
$buf .= 'P1: '.$r->s1->print."\n" if $r->s1;
$buf .= 'P2: '.$r->s2->print."\n" if $r->s2;
$buf .= 'P3: '.$r->s3->print."\n" if $r->s3;
$buf .= "[+] Results for target: [".$r->ip."]:".$r->port."\n";
if (ref($r) =~ /^Net::SinFP3::Result::Unknown$/) {
$buf .= $r->print;
}
elsif (ref($r) =~ /^Net::SinFP3::Result::Active$/) {
for my $r (@results) {
$buf .= $r->print."\n";
}
}
else {
$log->error("Don't know what to do with this result object [".
ref($r)."]");
return;
}
my $s2 = $r->s2;
if ($s2 && length($s2->O) <= 9) {
$buf .= '[!] WARNING: not enough TCP options for P2 reply, '.
'result may be false'."\n";
}
print $buf."\n";
# Disconnect, give access to this process, it is not using
# the same Perl interface to SQLite
$db->post;
my $dbh = Net::SinFP3::Ext::DBI->connection(
"dbi:SQLite:dbname=".$db->file,
'',
'',
{ AutoCommit => 0 },
);
my $p1 = $r->s1->print;
my $p2 = $r->s2->print;
my $p3 = $r->s3->print;
my @patternsP1 = (split('\s+', $p1))[0..6];
my @patternsP2 = (split('\s+', $p2))[0..6];
my @patternsP3 = (split('\s+', $p3))[0..6];
# Get possible option if a match is similar to this new signature
my %systemClassList = ();
my %vendorList = ();
my %osList = ();
my %osVersionList = ();
my %osVersionFamilyList = ();
my $addOsVersionChildren = 0;
for my $o (@results) {
if ($o->matchType eq "S1S2S3" && $o->matchMask eq "BH0FH0WH0OH0MH0SH0LH0") {
print "[-] A HEURISTIC0 match was found, you may want to add an ".
"OsVersionChildren instead.\n";
_addOsVersionChildren($o) && $addOsVersionChildren++;
}
$systemClassList {$o->systemClass}++;
$vendorList {$o->vendor}++;
$osList {$o->os}++;
$osVersionList {$o->osVersion}++;
$osVersionFamilyList{$o->osVersionFamily}++;
}
# We only wanted to add osVersionChildren
if ($addOsVersionChildren) {
return 1;
}
print "[+] POSSIBLE systemClass : [", join(' ', keys %systemClassList), "]\n";
print "[+] POSSIBLE vendor : [", join(' ', keys %vendorList), "]\n";
print "[+] POSSIBLE os : [", join(' ', keys %osList), "]\n";
print "[+] POSSIBLE osVersion : [", join(' ', keys %osVersionList), "]\n";
print "[+] POSSIBLE osVersionFamily: [", join(' ', keys %osVersionFamilyList), "]\n";
# Add new possible patterns
for my $p (\@patternsP1, \@patternsP2, \@patternsP3) {
_addNewPatternBinary ({ patternBinaryHeuristic0 => $p->[0] });
_addNewPatternTcpFlags ({ patternTcpFlagsHeuristic0 => $p->[1] });
_addNewPatternTcpWindow ({ patternTcpWindowHeuristic0 => $p->[2] });
_addNewPatternTcpOptions({ patternTcpOptionsHeuristic0 => $p->[3] });
_addNewPatternTcpMss ({ patternTcpMssHeuristic0 => $p->[4] });
_addNewPatternTcpWScale ({ patternTcpWScaleHeuristic0 => $p->[5] });
_addNewPatternTcpOLength({ patternTcpOLengthHeuristic0 => $p->[6] });
}
# Get input from user
my $systemClass = _getSystemClass();
my $vendor = _getVendor();
my $os = _getOs();
my $osVersion = _getOsVersion();
my $osVersionFamily = _getOsVersionFamily();
# Add new possible elements
my $nSystemClass = _addNewElementSystemClass ({ systemClass => $systemClass });
my $nVendor = _addNewElementVendor ({ vendor => $vendor });
my $nOs = _addNewElementOs ({ os => $os });
my $nOsVersion = _addNewElementOsVersion ({ osVersion => $osVersion });
my $nOsVersionFamily = _addNewElementOsVersionFamily({ osVersionFamily => $osVersionFamily });
# Get stored IDs
my ($idBinaryP1, $idTcpFlagsP1, $idTcpWindowP1, $idTcpOptionsP1,
$idTcpMssP1, $idTcpWScaleP1, $idTcpOLengthP1) =
_selectPatterns(\@patternsP1);
my ($idBinaryP2, $idTcpFlagsP2, $idTcpWindowP2, $idTcpOptionsP2,
$idTcpMssP2, $idTcpWScaleP2, $idTcpOLengthP2) =
_selectPatterns(\@patternsP2);
my ($idBinaryP3, $idTcpFlagsP3, $idTcpWindowP3, $idTcpOptionsP3,
$idTcpMssP3, $idTcpWScaleP3, $idTcpOLengthP3) =
_selectPatterns(\@patternsP3);
my @idIpVersion = $tIpVersion->search(
ipVersion => $self->global->ipv6 ? 'IPv6' : 'IPv4',
);
my $idIpVersion = $idIpVersion[0]->idIpVersion;
my $idSystemClass = $nSystemClass->idSystemClass;
my $idVendor = $nVendor->idVendor;
my $idOs = $nOs->idOs;
my $idOsVersion = $nOsVersion->idOsVersion;
my $idOsVersionFamily = $nOsVersionFamily->idOsVersionFamily;
# Add the signature
my $sig = Net::SinFP3::Ext::DBI::Signature->insert({
idIpVersion => $idIpVersion,
trusted => $self->trusted ? 1 : 0,
idSystemClass => $idSystemClass,
idVendor => $idVendor,
idOs => $idOs,
idOsVersion => $idOsVersion,
idOsVersionFamily => $idOsVersionFamily,
idS1PatternBinary => $idBinaryP1,
idS1PatternTcpFlags => $idTcpFlagsP1,
idS1PatternTcpWindow => $idTcpWindowP1,
idS1PatternTcpOptions => $idTcpOptionsP1,
idS1PatternTcpMss => $idTcpMssP1,
idS1PatternTcpWScale => $idTcpWScaleP1,
idS1PatternTcpOLength => $idTcpOLengthP1,
idS2PatternBinary => $idBinaryP2,
idS2PatternTcpFlags => $idTcpFlagsP2,
idS2PatternTcpWindow => $idTcpWindowP2,
idS2PatternTcpOptions => $idTcpOptionsP2,
idS2PatternTcpMss => $idTcpMssP2,
idS2PatternTcpWScale => $idTcpWScaleP2,
idS2PatternTcpOLength => $idTcpOLengthP2,
idS3PatternBinary => $idBinaryP3,
idS3PatternTcpFlags => $idTcpFlagsP3,
idS3PatternTcpWindow => $idTcpWindowP3,
idS3PatternTcpOptions => $idTcpOptionsP3,
idS3PatternTcpMss => $idTcpMssP3,
idS3PatternTcpWScale => $idTcpWScaleP3,
idS3PatternTcpOLength => $idTcpOLengthP3,
});
$sig->update;
$sig->dbi_commit;
return 1;
}
sub __getInput {
my ($type) = @_;
print "Enter $type:\n";
my $value;
while (<>) {
chomp;
$value .= $_;
}
die("No $type entered") unless $value;
return $value;
}
sub _getSystemClass { __getInput('systemClass') }
sub _getVendor { __getInput('vendor') }
sub _getOs { __getInput('os') }
sub _getOsVersion { __getInput('osVersion') }
sub _getOsVersionFamily { __getInput('osVersionFamily') }
sub _getOsVersionChildren {
my $list = __getInput('osVersionChildren (comma speparated list)');
my @osVersionChildrenList = split('\s*,\s*', $list);
return \@osVersionChildrenList;
}
sub _selectPatterns {
my ($p) = @_;
my @idPatternBinary = $tPatternBinary->search (patternBinaryHeuristic0 => $p->[0]);
my @idPatternTcpFlags = $tPatternTcpFlags->search (patternTcpFlagsHeuristic0 => $p->[1]);
my @idPatternTcpWindow = $tPatternTcpWindow->search (patternTcpWindowHeuristic0 => $p->[2]);
my @idPatternTcpOptions = $tPatternTcpOptions->search(patternTcpOptionsHeuristic0 => $p->[3]);
my @idPatternTcpMss = $tPatternTcpMss->search (patternTcpMssHeuristic0 => $p->[4]);
my @idPatternTcpWScale = $tPatternTcpWScale->search (patternTcpWScaleHeuristic0 => $p->[5]);
my @idPatternTcpOLength = $tPatternTcpOLength->search(patternTcpOLengthHeuristic0 => $p->[6]);
return (
$idPatternBinary[0]->idPatternBinary,
$idPatternTcpFlags[0]->idPatternTcpFlags,
$idPatternTcpWindow[0]->idPatternTcpWindow,
$idPatternTcpOptions[0]->idPatternTcpOptions,
$idPatternTcpMss[0]->idPatternTcpMss,
$idPatternTcpWScale[0]->idPatternTcpWScale,
$idPatternTcpOLength[0]->idPatternTcpOLength,
);
}
sub __addNewPattern {
my ($table, $element) = @_;
my @search = $table->search(%$element);
if (@search < 1) {
my @values = keys %$element;
my $key = $values[0]; # We fetch the first and only one key
my $value = $element->{$key}; # We fetch first key value
(my $field = $key) =~ s/Heuristic0$//;
print "[+] New pattern: [$value] for field [$field]\n";
print "Enter heuristic1 value (enter CTRL+D on a new line when done):\n";
my $h1;
while (<>) {
chomp;
$h1 .= $_;
}
print "Enter heuristic2 value (enter CTRL+D on a new line when done):\n";
my $h2;
while (<>) {
chomp;
$h2 .= $_;
}
$h1 = $value unless $h1;
$h2 = $value unless $h2;
return __addNewElement($table, {
$field.'Heuristic0' => $value,
$field.'Heuristic1' => $h1,
$field.'Heuristic2' => $h2,
});
}
return $search[0];
}
sub _addNewPatternBinary { __addNewPattern($tPatternBinary, @_) }
sub _addNewPatternTcpFlags { __addNewPattern($tPatternTcpFlags, @_) }
sub _addNewPatternTcpWindow { __addNewPattern($tPatternTcpWindow, @_) }
sub _addNewPatternTcpOptions { __addNewPattern($tPatternTcpOptions, @_) }
sub _addNewPatternTcpMss { __addNewPattern($tPatternTcpMss, @_) }
sub _addNewPatternTcpWScale { __addNewPattern($tPatternTcpWScale, @_) }
sub _addNewPatternTcpOLength { __addNewPattern($tPatternTcpOLength, @_) }
sub __addNewElement {
my ($table, $element) = @_;
my @search = $table->search(%$element);
if (@search < 1) {
my $new = $table->insert($element);
$new->update;
print "[+] Added new element: ",Dumper($element),"\n";
return $new;
}
return $search[0];
}
sub _addNewElementSystemClass { __addNewElement($tSystemClass, @_) }
sub _addNewElementVendor { __addNewElement($tVendor, @_) }
sub _addNewElementOs { __addNewElement($tOs, @_) }
sub _addNewElementOsVersion { __addNewElement($tOsVersion, @_) }
sub _addNewElementOsVersionFamily { __addNewElement($tOsVersionFamily, @_) }
sub _addNewElementOsVersionChildren { __addNewElement($tOsVersionChildren, @_) }
sub _addOsVersionChildren {
my ($result) = @_;
my $idSignature = $result->idSignature;
print "Would you like to add OsVersionChildren for this one? (CTRL+D for not)\n".
"=> [", $result->os, " ", $result->osVersion, "]\n";
my $input = '';
while (<>) {
chomp;
$input .= $_;
}
return unless $input;
my $osVersionChildrenList = _getOsVersionChildren();
my @idOsVersionList = ();
for my $o (@$osVersionChildrenList) {
my $new = _addNewElementOsVersion({ osVersion => $o });
push @idOsVersionList, $new->idOsVersion;
}
for my $o (@idOsVersionList) {
_addNewElementOsVersionChildren({
idSignature => $idSignature,
idOsVersion => $o,
});
}
$tOsVersionChildren->dbi_commit;
return 1;
}
1;
__END__
=head1 NAME
Net::SinFP3::Output::AddSignature - add a signature to SinFP3 database
=head1 DESCRIPTION
Go to http://www.networecon.com/tools/sinfp/ to know more.
=head1 METHODS
=over 4
=item B<new>
Object constructor.
=item B<run>
Run this plugin.
=back
=head1 AUTHOR
Patrice E<lt>GomoRE<gt> Auffret
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2011-2012, Patrice E<lt>GomoRE<gt> Auffret
You may distribute this module under the terms of the Artistic license.
See LICENSE.Artistic file in the source distribution archive.
=cut