📄 sinfp.pm
字号:
## $Id: SinFP.pm,v 1.8.2.33.2.38 2006/11/05 17:59:05 gomor Exp $#package Net::SinFP;use strict;use warnings;our $VERSION = '2.04';require Class::Gomor::Array;our @ISA = qw(Class::Gomor::Array);our @AS = qw( verbose target file wait retry h2Match ipv6UseIpv4 offline passive passiveFrame filter doP1 doP2 doP3 pktP1 pktP2 pktP3 sigP1 sigP2 sigP3 ipv6 keepFile db _dump _pIpId _pTcpSrc _pTcpSeq _pTcpAck);our @AA = qw( resultList);our @AO = qw( passiveMatchCallback);__PACKAGE__->cgBuildIndices;__PACKAGE__->cgBuildAccessorsScalar(\@AS);__PACKAGE__->cgBuildAccessorsArray(\@AA);use Carp;use Net::Packet::Env qw($Env);require Net::Packet::Dump;use Net::Packet::Consts qw(:tcp :dump);use Net::Packet::Utils qw(getRandom16bitsInt getRandom32bitsInt);require Net::SinFP::SinFP4;require Net::SinFP::SinFP6;require Net::SinFP::Search;sub passiveMatchCallback { my $self = shift; @_ ? $self->[$Net::SinFP::__passiveMatchCallback] = shift : &{$self->[$Net::SinFP::__passiveMatchCallback]}();}sub new { my $self = shift->SUPER::new( verbose => 0, doP1 => 1, doP2 => 1, doP3 => 1, wait => 3, retry => 3, h2Match => 0, keepFile => 0, offline => 0, passive => 0, ipv6 => 0, ipv6UseIpv4 => 0, resultList => [], @_, ); if (! $self->db) { confess("You MUST specify an open SinFP DB in `db' attribute\n"); } $self->_pIpId ($self->_getInitialIpId); $self->_pTcpSrc($self->_getInitialTcpSrc); $self->_pTcpSeq($self->_getInitialTcpSeq); $self->_pTcpAck($self->_getInitialTcpAck); $SIG{INT} = sub { $self->_signalClean }; $SIG{TERM} = sub { $self->_signalClean }; $self->ipv6 ? bless($self, 'Net::SinFP::SinFP6') : bless($self, 'Net::SinFP::SinFP4');}sub _getInitialIpId { my $ipId = getRandom16bitsInt(); $ipId += 666 unless $ipId > 0; $ipId;}sub _getInitialTcpSrc { my $tcpSrc = getRandom16bitsInt() - 3; $tcpSrc += 1025 unless $tcpSrc > 1024; $tcpSrc;}sub _getInitialTcpSeq { my $tcpSeq = getRandom32bitsInt() - 3; $tcpSeq += 666 unless $tcpSeq > 0; $tcpSeq;}sub _getInitialTcpAck { my $tcpAck = getRandom32bitsInt() - 3; $tcpAck += 666 unless $tcpAck > 0; $tcpAck;}sub getFilter { my $self = shift; $self->passive ? $self->_getFilterPassive : $self->_getFilterActive;}sub getFileName { my $self = shift; $self->passive ? $self->_getFileNamePassive : $self->_getFileNameActive;}sub _getDumpOnlineActive { my $self = shift; Net::Packet::Dump->new( file => $self->file, unlinkOnClean => $self->keepFile ? 0 : 1, overwrite => 1, timeoutOnNext => $self->wait, );}sub _getDumpOnlinePassive { my $self = shift; Net::Packet::Dump->new( file => $self->file, unlinkOnClean => 0, overwrite => 1, timeoutOnNext => 0, noStore => 1, );}sub _getDumpOffline { my $self = shift; Net::Packet::Dump->new( file => $self->file, overwrite => 0, unlinkOnClean => 0, mode => NP_DUMP_MODE_OFFLINE, );}sub getDump { my $self = shift; my $dump; if ($self->offline) { $dump = $self->_getDumpOffline; } else { $self->passive ? do { $dump = $self->_getDumpOnlinePassive } : do { $dump = $self->_getDumpOnlineActive }; } $dump;}sub _passiveMatchPrepare { my $self = shift; my ($frame) = @_; $self->pktP1(undef); $self->pktP3(undef); $self->passiveFrame($frame); $self->pktP2($frame); $self->pktP2->reply($frame);}sub _passiveMatchClean { my $self = shift; $self->passiveFrame(undef); $self->pktP2->reply(undef); $self->pktP2(undef); $self->resultList([]);}sub _startOnlinePassive { my $self = shift; $self->file($self->getFileName); $self->_dump($self->getDump); my $filter = $self->getFilter; $self->filter ? $self->_dump->filter('('.$self->filter.') and '.$filter) : $self->_dump->filter($filter); $self->_dump->start; while (1) { if (my $frame = $self->_dump->next) { $self->_passiveMatchPrepare($frame); $self->passiveMatchCallback; $self->_passiveMatchClean; } }}sub _startOfflinePassive { my $self = shift; $self->_dump($self->getDump); $self->_dump->start; $self->_dump->nextAll; croak("No frames captured\n") unless ($self->_dump->frames)[0]; for my $frame ($self->_dump->frames) { if ($frame->l4->isTcp) { if ($frame->l4->flags == (NP_TCP_FLAG_SYN) || $frame->l4->flags == (NP_TCP_FLAG_SYN|NP_TCP_FLAG_ACK) ) { $self->_passiveMatchPrepare($frame); $self->passiveMatchCallback; $self->_passiveMatchClean; } } } $self->clean; exit(0);}sub _startOfflineActive { my $self = shift; $self->_dump($self->getDump); $self->_dump->start; $self->_dump->nextAll; croak("No frames captured\n") unless ($self->_dump->frames)[0]; my $targetIp = ($self->_dump->frames)[0]->l3->dst; $self->getOfflineProbes($targetIp); croak("No SinFP probe found\n") if (! $self->pktP1 && ! $self->pktP2 && ! $self->pktP3); $self->getResponses;}sub _startOnlineActive { my $self = shift; $self->file($self->getFileName); $self->_dump($self->getDump); $self->buildProbes; my $filter = $self->getFilter; $filter .= ' and tcp and port '.$self->target->port. ' and ('; my $putOr; if ($self->pktP1) { $filter .= 'port '.$self->pktP1->l4->src; $putOr++; } if ($self->pktP2) { $filter .= ' or ' if $putOr; $filter .= 'port '.$self->pktP2->l4->src; $putOr++; } if ($self->pktP3) { $filter .= ' or ' if $putOr; $filter .= 'port '.$self->pktP3->l4->src; $putOr++; } $filter .= ')'; $self->_dump->filter($filter); $self->_dump->start; for (1..$self->retry) { $self->sendProbes; until ($self->_dump->timeout) { if ($self->_dump->next) { $self->getResponses; } return if $self->allResponsesReceived; } $self->_dump->timeoutReset; }}sub start { my $self = shift; if ($self->passive) { $self->doP1(0); $self->doP2(1); $self->doP3(0); $self->offline ? $self->_startOfflinePassive : $self->_startOnlinePassive; } else { $self->offline ? $self->_startOfflineActive : $self->_startOnlineActive; }}sub buildProbes { my $self = shift; $self->pktP1($self->getP1) if $self->doP1; $self->pktP2($self->getP2) if $self->doP2; $self->pktP3($self->getP3) if $self->doP3;}sub sendProbes { my $self = shift; $self->pktP1->send if ($self->pktP1 && ! $self->pktP1->reply); $self->pktP2->send if ($self->pktP2 && ! $self->pktP2->reply); $self->pktP3->send if ($self->pktP3 && ! $self->pktP3->reply);}sub getResponses { my $self = shift; $self->pktP1->recv if ($self->pktP1 && ! $self->pktP1->reply); $self->pktP2->recv if ($self->pktP2 && ! $self->pktP2->reply); $self->pktP3->recv if ($self->pktP3 && ! $self->pktP3->reply);}# This is to verify that RST packets are generated from the target with # the same TTL as a SYN|ACK packet. We accept a difference of 3 hops, but # if this is greater, we consider to not be the same generated TTL# Example: SunOS 5.9 generates a TTL of 60 in a SYN|ACK from our probe,# but a TTL of 64 for a RST from our probe. So, $ttl = 0.sub __analyzeIpTtl { my $self = shift; my ($p, $p2) = @_; return 1 if ! $p2 || ! $p2->reply; my $ttlSrc = $self->getResponseIpTtl($p2); my $ttlDst = $self->getResponseIpTtl($p); my $ttl = 1; $ttl = 0 if (($ttlSrc > $ttlDst) && ($ttlSrc - $ttlDst > 3)); $ttl = 0 if (($ttlDst > $ttlSrc) && ($ttlDst - $ttlSrc > 3)); $ttl;}sub __analyzeIpDfBit { shift->getResponseIpDfBit(shift()) ? '1' : '0' }sub __analyzeIpIdPassive { shift->getResponseIpId(shift()) ? '1' : '0' }sub __analyzeIpId { my $self = shift; my ($p) = @_; return $self->__analyzeIpIdPassive($p) if $self->passive; my $reqId = $self->getProbeIpId($p); my $repId = $self->getResponseIpId($p); my $flag = 1; if ($repId == 0) { $flag = 0 } elsif ($repId == $reqId) { $flag = 2 } elsif ($repId == ++$reqId) { $flag = 3 } # There is no reason for that, but # anyway, we have nothing to loose $flag;}sub __analyzeTcpSeqPassive { shift; shift->reply->l4->seq ? '1' : '0' }sub __analyzeTcpSeq { my $self = shift; my ($p) = @_; return $self->__analyzeTcpSeqPassive($p) if $self->passive; my $reqAck = $p->l4->ack; my $repSeq = $p->reply->l4->seq; my $flag = 1; if ($repSeq == 0 ) { $flag = 0 } elsif ($repSeq == $reqAck ) { $flag = 2 } elsif ($repSeq == ++$reqAck) { $flag = 3 } $flag;}sub __analyzeTcpAckPassive { shift; shift->reply->l4->ack ? '1' : '0' }sub __analyzeTcpAck { my $self = shift; my ($p) = @_; return $self->__analyzeTcpAckPassive($p) if $self->passive; my $reqSeq = $p->l4->seq; my $repAck = $p->reply->l4->ack; my $flag = 1; if ($repAck == 0 ) { $flag = 0 } elsif ($repAck == $reqSeq ) { $flag = 2 } elsif ($repAck == ++$reqSeq) { $flag = 3 } $flag;}sub _analyzeBinary { my $self = shift; my ($p, $p2) = @_; my $flagTtl = $self->__analyzeIpTtl($p, $p2); my $flagId = $self->__analyzeIpId($p); my $flagDf = $self->__analyzeIpDfBit($p); my $flagSeq = $self->__analyzeTcpSeq($p); my $flagAck = $self->__analyzeTcpAck($p); 'B'.$flagTtl.$flagId.$flagDf.$flagSeq.$flagAck;}sub _analyzeTcpFlags { my $self = shift; my ($p) = @_; sprintf("F0x%02x", $p->reply->l4->flags);}sub _analyzeTcpWindow { my $self = shift; my ($p) = @_; 'W'.$p->reply->l4->win;}sub _analyzeTcpOptionsAndMss { my $self = shift; my ($p) = @_; #燫ewrite timestamp values, if > 0 overwrite with ffff, for each timestamp my $mss; my $opts; if ($opts = unpack('H*', $p->reply->l4->options)) { if ($opts =~ /080a(........)(........)/) { if ($1 && $1 !~ /44454144|00000000/) { $opts =~ s/(080a)........(........)/$1ffffffff$2/; } if ($2 && $2 !~ /44454144|00000000/) { $opts =~ s/(080a........)......../$1ffffffff/; } } #燤ove MSS value in its own field if ($opts =~ /0204(....)/) { if ($1) { $mss = sprintf("%d", hex($1)); $opts =~ s/0204..../0204ffff/; } } } $opts .= unpack('H*', $p->reply->l7->data) if $p->reply->l7; $opts = '0' unless $opts; $mss = '0' unless $mss; [ 'O'.$opts, 'M'.$mss ];}sub getResponseSignature { my $self = shift; my ($p, $p2) = @_; return { B => 'B00000', F => 'F0', W => 'W0', O => 'O0', M => 'M0' } if (! $p || ! $p->reply); my $b = $self->_analyzeBinary($p, $p2); my $f = $self->_analyzeTcpFlags($p); my $w = $self->_analyzeTcpWindow($p); my $om = $self->_analyzeTcpOptionsAndMss($p); my $o = $om->[0]; my $m = $om->[1]; { B => $b, F => $f, W => $w, O => $o, M => $m };}sub _passiveMatchUpdate { my $self = shift; $self->pktP2->reply->l4->flags(NP_TCP_FLAG_SYN|NP_TCP_FLAG_ACK); $self->pktP2->reply->l4->pack;}sub analyzeResponses { my $self = shift; # Rewrite TCP flags to be SinFP DB compliant $self->_passiveMatchUpdate if $self->passive; $self->sigP1($self->getResponseSignature($self->pktP1)) if $self->doP1; $self->sigP2($self->getResponseSignature($self->pktP2)) if $self->doP2; $self->sigP3($self->getResponseSignature($self->pktP3, $self->pktP2)) if $self->doP3; # Some systems do not respond to P1, but do for P2 # We write a fake P1 response to be able to match if ($self->pktP2 && $self->pktP2->reply && $self->pktP1 && ! $self->pktP1->reply) { $self->pktP1->reply($self->pktP1->cgClone); $self->sigP1({B => 'B00000', F => 'F0', W => 'W0', O => 'O0', M => 'M0'}); }}sub allResponsesReceived { my $self = shift; if ((! $self->pktP1 || $self->pktP1->reply) && (! $self->pktP2 || $self->pktP2->reply) && (! $self->pktP3 || $self->pktP3->reply)) { return 1; } return undef;}sub matchOsfps { my $self = shift; my ($userMaskList) = @_; # Deactivate match only with P2 unless explicitely asked for my $doP2 = $self->doP1 ? 0 : 1; my $se = Net::SinFP::Search->new( db => $self->db, useAdvancedMasks => $self->h2Match ? 1 : 0, maskUserList => $userMaskList ? $userMaskList : [], ipv6 => $self->ipv6 ? 1 : 0, enableP2Match => $doP2 ? 1 : 0, ); $se->sigP1($self->sigP1) if $self->pktP1 && $self->pktP1->reply; $se->sigP2($self->sigP2) if $self->pktP2 && $self->pktP2->reply; $se->sigP3($self->sigP3) if $self->pktP3 && $self->pktP3->reply; if (my $result = $se->search) { $self->resultList($result); } if ($self->ipv6 && $self->ipv6UseIpv4 && ! $self->found) { my $se2 = Net::SinFP::Search->new( db => $self->db, useAdvancedMasks => $self->h2Match ? 1 : 0, maskUserList => $userMaskList ? $userMaskList : [], ipv6 => 0, enableP2Match => $doP2 ? 1 : 0, ); $se2->sigP1($self->sigP1) if $self->pktP1 && $self->pktP1->reply; $se2->sigP2($self->sigP2) if $self->pktP2 && $self->pktP2->reply; $se2->sigP3($self->sigP3) if $self->pktP3 && $self->pktP3->reply; # We reload with IPv4 signatures $se->db->ipv6(0); $se->db->loadSignatures; if (my $result = $se2->search) { $self->resultList($result); } } $self->found;}sub found { scalar shift->resultList }sub _sigPAsString { my $self = shift; my ($p) = @_; my $sig = $self->$p; return 'B00000 F0 W0 O0 M0' unless $sig; join(' ', $sig->{B}, $sig->{F}, $sig->{W}, $sig->{O}, $sig->{M});}sub sigP1AsString { shift->_sigPAsString('sigP1') }sub sigP2AsString { shift->_sigPAsString('sigP2') }sub sigP3AsString { shift->_sigPAsString('sigP3') }sub clean { my $self = shift; if ($self->_dump) { $self->_dump->stop; $self->_dump->clean; $self->_dump(undef); $Env->dump(undef); } return(0);}sub _signalClean { my $self = shift; $self->clean; exit(0);}1;=head1 NAMENet::SinFP - a full operating system stack fingerprinting suite=head1 DESCRIPTIONGo to http://www.gomor.org/sinfp to know more.=cut=head1 AUTHORPatrice E<lt>GomoRE<gt> Auffret=head1 COPYRIGHT AND LICENSECopyright (c) 2005-2006, Patrice E<lt>GomoRE<gt> AuffretYou may distribute this module under the terms of the Artistic license.See LICENSE.Artistic file in the source distribution archive.=cut
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -