Breaking

DDoS Block PERL NEW HOT

CODE:

#!/usr/bin/perl

###############################################################################
## DDOSBlock version 0.1 Author: Jo3-GHT                                      #
##                                                                            #
## Download from https://sourceforge.net/projects/ddosblock                   #
##                                                                            #
## To execute, run:                                                           #
##                                                                            #
## ./ddosblock-0.2.pl                                                         #
###############################################################################

use strict;
use warnings;

use vars qw(%CONFIG %blocked);

$| = 1;

my %CONFIG = (

  ## 0 = No output
  ## 1 = Actions reported
  ## 2 = Actions + unix commands reported
  ## 3 = Verbose in the extreme

  DEBUG => 2,

  ## Do we perform checks only - no IPTABLE changes - Good for debugging

  TESTMODE => 0,

  ## Ban IP addresses above this number of accesses

  THRESHOLD => 150,

  ## How long, in seconds, between checks

  INTERVAL => 30,

  ## If we have APF use that, otherwise fallback on iptables

  USEAPF => 0,

  ## How long (in seconds) do we ban people for
  ## escalating each time they're bad
  ## This resets to the first item, when they have gone
  ## a full interval without being flagged
  ##
  ## 60, 300, 3600 would mean that they are banned for 60 seconds, then 5 minutes, then an hour
  ##
  ## This list can have as many increment levels as you like

  BANINCREMENTS => [60, 120, 240, 800, 1200],

  ## IP ADDRESSES THAT WE WILL NEVER BAN

  EXCLUDEIPS => [ "127.0.0.1", "10.0.0.1" ],

  ## UNIX COMMANDS

  NETSTAT => "/bin/netstat",

  IPT => "/usr/sbin/iptables",

  APF => "/usr/local/sbin/apf",

  SENDMAIL => "/usr/sbin/sendmail",

  ## WHERE TO STORE/SAVE OUTPUT

  LOGDIR => "/var/log/ddosblock",

  MAILTO => "nobody\@example.com", # NOTE: Under Perl you have to escape the @ symbol with a slash

  STDOUT => 1, # In addition to logging, do we want to report it to STDOUT

);

$CONFIG{TOTINCREMENTS} = $#{$CONFIG{BANINCREMENTS}};
$CONFIG{LISTFILE}      = "$CONFIG{LOGDIR}/bannedips.txt";
$CONFIG{LASTDAY}       = 0;

if ( ! -d $CONFIG{LOGDIR})
{
  print "Creating log directory $CONFIG{LOGDIR}\n\n";
  mkdir $CONFIG{LOGDIR},700;
}

my $command = qq($CONFIG{NETSTAT} -ntu | awk '{ sub(/(.*)\:/,"",\$4); sub(/\:(.*)/,"",\$5); print \$5,\$4}' | grep ^[0-9] | sort | uniq -c | sort -nr | head -30 );

&rotatelog();

if ($CONFIG{TESTMODE} == 1)
{
  &debug(qq(** NOTE **\n\nTest mode - No IPTABLE changes will be made\n));
}

&loadbanned();

while (1)
{
  &rotatelog();

  &check();

  &release();

  &debug(qq(- Sleeping for $CONFIG{INTERVAL} seconds\n)) if ($CONFIG{DEBUG} > 1);

  sleep $CONFIG{INTERVAL};
}

sub rotatelog
{
  my @date = localtime();
  my $weekday = $date[6];

  if ($weekday != $CONFIG{LASTDAY})
  {
    if ($CONFIG{LASTDAY})
    {
      close(DEBUG);
    }
    $CONFIG{LASTDAY} = $weekday;

    open(DEBUG, "> $CONFIG{LOGDIR}/doslog.$weekday.txt");
  }
}

sub check
{
  my @input = `$command`;

  my $now = time;

  my $savelist;

  INPUTLOOP:
  foreach my $item (@input)
  {
    chomp($item);

    $item =~ s/^ +//io;
    $item =~ s/ +/ /io;

    &debug("-- $item\n") if ($CONFIG{DEBUG} >= 3);

    my ($hits, $ipaddress, $port) = split(/ /, $item);

    next INPUTLOOP if (grep(/^$ipaddress/, @{$CONFIG{EXCLUDEIPS}}));

    next INPUTLOOP if (defined $blocked{$ipaddress} && $now < $blocked{$ipaddress}{sleepuntil});

    if ($hits > $CONFIG{THRESHOLD})
    {
      $blocked{$ipaddress}{blocklevel} = $blocked{$ipaddress}{lastblock} + 1 || 1;
      $blocked{$ipaddress}{lastblock}  = $blocked{$ipaddress}{blocklevel};

      if ($blocked{$ipaddress}{blocklevel} == 1)
      {
        my $iptcmd     = ($CONFIG{USEAPF}) ?
          "$CONFIG{APF} -d $ipaddress"
        :
          "$CONFIG{IPT} -I INPUT -s $ipaddress -j DROP";

        my $ok = `$iptcmd` unless ($CONFIG{TESTMODE});

        &debug("Adding block for $ipaddress ($hits hits/minute, port $port)") if ($CONFIG{DEBUG} > 0);
        &debug("- Command $iptcmd") if ($CONFIG{DEBUG} > 1);

        if ($CONFIG{MAILTO})
        {
          my $localtime = localtime();

          open (MAIL, "| $CONFIG{SENDMAIL} -t");
          print MAIL qq(To: $CONFIG{MAILTO}\nSubject: IP address $ipaddress banned\n\nBanned ip addresses $ipaddress on $localtime with $hits hits\n);
          close (MAIL);
        }
      }
      else
      {
        &debug("Setting $ipaddress to block level $blocked{$ipaddress}{blocklevel} ($hits hits/minute)") if ($CONFIG{DEBUG} > 0);
      }

      &updatesleep($ipaddress);

      $savelist = 1;
    }
  }

  # Save a list of banned IPs to a file incase this process dies
  if ($savelist)
  {
    &savebanned();
  }
}

sub release
{
  my $now = time;
  my $savelist;

  # Loop through all the IP addresses flagged

  foreach my $ipaddress (keys %blocked)
  {
    # No point looking at it until we've gone past the sleep date

    if ($now > $blocked{$ipaddress}{sleepuntil})
    {
      $blocked{$ipaddress}{passcount}++;

      # Remove the block on the first pass
      # then remove the

      if ($blocked{$ipaddress}{passcount} == 1)
      {
        # Release the block

        my $iptcmd = ($CONFIG{USEAPF}) ?
          "$CONFIG{IPT} -u "
          :
          "$CONFIG{IPT} -D INPUT -s $ipaddress -j DROP";

        &debug("Removing block from $ipaddress") if ($CONFIG{DEBUG}> 0);
        &debug("- Command $iptcmd") if ($CONFIG{DEBUG} > 1);

        my $ok = `$iptcmd` unless ($CONFIG{TESTMODE});

        $blocked{$ipaddress}{blocklevel} = 0;
      }
      else
      {
        &debug("- Two passes without issue $ipaddress, forgetting block level") if ($CONFIG{DEBUG} > 1);
        delete $blocked{$ipaddress};
      }

      $savelist = 1;
    }
  }

  # Save a list of banned IPs to a file incase this process dies
  if ($savelist)
  {
    &savebanned(\%blocked);
  }
}

sub updatesleep
{
  my ($ipaddress) = @_;

  my $blocklevel = $blocked{$ipaddress}{blocklevel};

  $blocklevel = ($blocklevel >= $CONFIG{TOTINCREMENTS}) ? $#{$CONFIG{BANINCREMENTS}} : $blocklevel;

  my $increment = $CONFIG{BANINCREMENTS}[$blocklevel - 1];

  $blocked{$ipaddress}{sleepuntil} = time + $increment;

  my $stamp = localtime(time + $increment);

  &debug("- Will check $ipaddress after $increment seconds ($stamp) - Block level $blocklevel") if ($CONFIG{DEBUG} > 1);
}

sub loadbanned
{
  undef %blocked;

  if (-f $CONFIG{LISTFILE})
  {
    open(LIST, "< $CONFIG{LISTFILE}");
    my @list = <LIST>;
    close (LIST);

    # Rebuild a list of those things we've blocked
    foreach my $ipaddress (@list)
    {
      chomp($ipaddress);

      # Let them be released and evaluated again
      $blocked{$ipaddress}{sleepuntil} = 1;
      $blocked{$ipaddress}{blocklevel} = 1;
      $blocked{$ipaddress}{lastblock}  = 1;
      $blocked{$ipaddress}{passcount}  = 0;

      &debug("- Loading blocked IP $ipaddress") if ($CONFIG{DEBUG} > 0);

    }
  }
}

sub savebanned
{
  my ($list) = @_;

  open(LIST, "> $CONFIG{LISTFILE}");
  print LIST join("\n", keys %blocked);
  close (LIST);
}

sub debug
{
  my ($line) = @_;

  print DEBUG localtime() . " $line \n";

  if ($CONFIG{STDOUT})
  {
    print localtime() . " $line \n";
  }
}

No comments:

Powered by Blogger.