Revision [2897]

This is an old revision of ddosDotPl made by JeffTaylor on 2012-05-25 05:38:58.

 

#!/usr/bin/perl
$| = 1;

# ddos.pl 2012.05.23
# by Jeff Taylor (Shdwdrgn)

#--- REQUIRED PERL LIBRARIES ---#
# Net::Pcap (debian: libnet-pcap-perl)
# IPTables::ChainMgr (debian: libiptables-chainmgr-perl)

#--- EDIT THESE VALUES AS NEEDED ---#
# BLOCKFILE: where to store IP and port information
use constant BLOCKFILE  => "/root/ddos.dns";

# NETDEV: device your DNS service listens on
use constant NETDEV     => "eth0";

# NETMASK: add a mask for your server's real or local IP
use constant NETMASK    => [ "127.0.0.0/8", "10.0.0.0/8" ];

# DEBUG: 0=off, 1=show debug info
use constant DEBUG      => 0;

# THRESH: maximum queries on a single port
use constant THRESH     => 10;

# MAXPERSEC: maximum number of queries per second
use constant MAXPERSEC  => 40;

# MAXCOUNT: limits the queries/sec that we observe
use constant MAXCOUNT   => 64;

# MAXMULTI: Limits how high the multiplier can go
use constant MAXMULTI   => 8;

# MAXBLOCKS: If blocking this number of ports or more
# for an IP, start blocking all DNS queries from IP
use constant MAXBLOCKS  => 4;

# BASETIME: used to calculate how long an IP is blocked
use constant BASETIME   => 225/128; # Approx. 2 seconds

# Adjust the path to iptables for your system
my %opts = (
  'iptables' => '/sbin/iptables',
  'iptout'   => '/tmp/iptables.out',
  'ipterr'   => '/tmp/iptables.err',
  'debug'    => 0,
  'verbose'  => 0
);
#--- DO NOT EDIT PAST THIS POINT ---#


use POSIX qw(strftime);
use Switch;
use IPTables::ChainMgr;
#use Net::Pcap;
#use NetPacket::Ethernet;
#use NetPacket::IP;
#use NetPacket::TCP;
#use strict;

my $blocked;
my @data;
my $dom;
my @tmp;
my $IP;
my $port;
my $key;
my $line;
my $hms;
my %pkt;
my $tmr = time();
my $h; my $m; my $s;
my $expire;
my $max;
my $portcount;
my $netrules;
my $lastReset;

my $ipt_obj = new IPTables::ChainMgr(%opts)
  or die "[*] Could not acquire IPTables::ChainMgr object";


#---------------------- Error Handlers ----------------------#
$SIG{__DIE__} = 'INT_handler';
$SIG{'INT'} = 'INT_handler';
$SIG{'TERM'} = 'INT_handler';
sub INT_handler {
  if (DEBUG) { print "\nEnding script...\n"; }
  close(DUMP);
  foreach $IP (keys %pkt) {
	foreach $port (keys %{$pkt{$IP}}) {
	  if ($pkt{$IP}{$port}{block} == 1) {
		if (DEBUG) { print "* Unloading $IP:$port\n"; }
		iptUpdate($IP, $port, "D");
	  }
	}
  }
  exit(0);
}


#--------------------- Begin main loop ---------------------#
$netrules="";
foreach $IP (@{+NETMASK}) {
  if ($netrules) { $netrules .= " and "; }
  $netrules .= "not src net $IP";
}
$netrules = "tcpdump -pnt -i ".NETDEV." $netrules and not udp src port 53 and udp dst port 53 and '(ip[2:2] != 0)' 2>/dev/null |";

open DUMP, "$netrules" or die "Invalid tcpdump defined\n";
while (<DUMP>) {
  chomp ($line = <DUMP>);

  # Reload array data every hour
  $h = strftime("%H", localtime);
  if ($lastReset != $h) {
	$lastReset = $h;
	getBlockfile(%pkt);
   }

  # Values for {block}
  # 0 - Port traffic detected, but no action taken
  # 1 - IP:port is currently blocked
  # 2 - IP:port block expired, now monitoring for repeat offense

  # Record each packet in array
  @data = split(/ /, $line);
  @tmp = split(/\./, $data[1]);
  $IP = "$tmp[0].$tmp[1].$tmp[2].$tmp[3]";
  $port = $tmp[4];
  #$url = ($data[5] eq "[1au]") ? $data[7] : $data[6];
  #@bits = reverse (split(/\./, $url));
  if ($IP) {
	# Add new IP to array
	if (! $pkt{$IP}) {
	  $pkt{$IP}{0}{block} = 0;
	  $pkt{$IP}{0}{count} = 0;
	  $pkt{$IP}{0}{multi} = 0;
	}
	$pkt{$IP}{0}{count}++;

	# New port number seen for this IP
	if (! $pkt{$IP}{$port}) {
	  if (($pkt{$IP}{$port}{block} > 0) && (DEBUG)) { print "WARNING! $IP:$port adding existing block\n"; }
	  $pkt{$IP}{$port}{block} = 0;
	  $pkt{$IP}{$port}{count} = 0;
	  $pkt{$IP}{$port}{multi} = 0;
	}

	# Do unless port is currently blocked
	if ($pkt{$IP}{$port}{block} != 1) {
	  $pkt{$IP}{$port}{count}++;
	  if ($pkt{$IP}{$port}{count} > MAXCOUNT) { $pkt{$IP}{$port}{count}=MAXCOUNT; }
	}

	# If IP:port exceeds limits while being monitor, prepare to block again
	if (($pkt{$IP}{$port}{block} == 2) && ($pkt{$IP}{$port}{count} >= THRESH)) { $pkt{$IP}{$port}{block} = 0; }

  }

  # If one second has passed since the last check, examine collected data
  if ($tmr != time()) {
	$tmr = time();
	$h = strftime("%H", localtime);
	$m = strftime("%M", localtime);
	$s = strftime("%S", localtime);
	$hms = "$h:$m:$s";
	if (DEBUG) { print "$hms Count: ", scalar(keys %pkt), "  \r"; }

	foreach $IP (keys %pkt) {
	  $pkt{$IP}{0}{portblock} = 0;
	  foreach $port (reverse sort keys %{$pkt{$IP}}) {
		if ($port == 0) {
		  if (($pkt{$IP}{0}{block} == 2) && ($pkt{$IP}{0}{portblock} >= MAXBLOCKS)) { $pkt{$IP}{0}{block} = 0; }
		} else {
		  # Count the number of ports blocked for this IP
		  if ($pkt{$IP}{$port}{block} == 1) { $pkt{$IP}{0}{portblock}++; }
		}

		# Add new block
		if ($pkt{$IP}{$port}{block} == 0) {
		  $max = "";
		  if (($port > 0) && ($pkt{$IP}{$port}{count} >= THRESH)) { $max = "port"; }
		  if (($port == 0) && ($pkt{$IP}{0}{count} >= MAXPERSEC)) { $max = "maxpersec"; }
		  if (($port == 0) && ($pkt{$IP}{0}{portblock} >= MAXBLOCKS)) { $max = "maxblock"; }

		  if ($max) {
			$pkt{$IP}{$port}{block} = 1;
			$pkt{$IP}{$port}{start} = $tmr;
			$pkt{$IP}{$port}{timer} = $pkt{$IP}{$port}{count};
			if (DEBUG) { print "$hms [$pkt{$IP}{$port}{multi}] ",$max,"Blocking $IP:$port - $pkt{$IP}{$port}{count} hits\n"; }
			iptUpdate($IP, $port, "I");
			updateBlockfile($IP, $port, %pkt);
		  }

		# Remove expired block
		} else {
		  $expire = $pkt{$IP}{$port}{start} + ((($pkt{$IP}{$port}{block} + 1) ** $pkt{$IP}{$port}{multi}) * $pkt{$IP}{$port}{timer} * BASETIME);
		  if ($tmr > $expire) {
			$blocked = $tmr - $pkt{$IP}{$port}{start};
			if ($pkt{$IP}{$port}{block} == 2) {
			  if (DEBUG) { print "$hms Deleting $IP:$port after $blocked seconds\n"; }
			  $pkt{$IP}{$port}{block} = 0;
			  updateBlockfile($IP, $port, %pkt);
			  delete ($pkt{$IP}{$port});

			} else {
			  if (DEBUG) { print "$hms Unblocking $IP:$port after $blocked seconds\n"; }
			  iptUpdate($IP, $port, "D");
			  $pkt{$IP}{$port}{block} = 2;
			  $pkt{$IP}{$port}{start} = $tmr;
			  $pkt{$IP}{$port}{multi}++;
			  if ($pkt{$IP}{$port}{multi}>MAXMULTI) { $pkt{$IP}{$port}{multi}=MAXMULTI; }
			  updateBlockfile($IP, $port, %pkt);
			  $pkt{$IP}{$port}{count} = 0;
			}
		  }
		}

		# If record still exists, make updates
		if ($pkt{$IP}{$port}) {

		  # Unblocked IPs should decrease their count at a normal rate
		  if ($pkt{$IP}{$port}{block} == 2) {
			$pkt{$IP}{$port}{count}--;
			if ($pkt{$IP}{$port}{count} < 1) { $pkt{$IP}{$port}{count} = 0; }
		  }

		  # Remove array element when no longer needed
		  if (($port > 0) && ($pkt{$IP}{$port}{block} == 0)) {
			$pkt{$IP}{$port}{count}--;
			if ($pkt{$IP}{$port}{count} <= 0) {
			  if (($pkt{$IP}{$port}{timer} > 0) && (DEBUG)) { print "WARNING! Removed $IP:$port from array [$pkt{$IP}{$port}{block}]\n"; }
			  delete ($pkt{$IP}{$port});
			}
		  }

		} #if exists

	  } #foreach PORT

	  # Clean up unused IPs
	  $portcount = scalar(keys %{$pkt{$IP}});
	  if (($portcount <= 1) && ($pkt{$IP}{0}{block} == 0)) {
		if (($pkt{$IP}{0}{portblock} > 0) && (DEBUG)) { print "WARNING! [$pkt{$IP}{0}{portblock}] Cleanup $IP:port\n"; }
		delete ($pkt{$IP});
	  } else {
		$pkt{$IP}{0}{count} = 0;
	  }

	} #foreach IP
  }

}
print "Error: tcpdump not found or bad NETMASK defined\n";


#----------------------- Subroutines ------------------------#

sub getBlockfile {
  my $pkt = shift;

  if (-e BLOCKFILE) {
	# Read blockfile into memory
	open FH, BLOCKFILE; my @LINES = <FH>; close(FH);

	foreach $line (@LINES) {
	  @data = split(/[ \t]+/, $line);
	  if (($data[0]) && ($data[0] ne "#IP:Port")) {
		@tmp = split(/:/, $data[0]);
		$IP = $tmp[0]; $IP =~ s/^\#//;
		$port = $tmp[1];
		$pkt{$IP}{$port}{multi} = $data[1];
		  if ($pkt{$IP}{$port}{multi} eq "") { $pkt{$IP}{$port}{multi} = 0; }
		$pkt{$IP}{$port}{count} = $data[2];
		  if ($pkt{$IP}{$port}{count} == 0) { $pkt{$IP}{$port}{count} = THRESH; }
		if (! $pkt{$IP}{$port}{timer}) { $pkt{$IP}{$port}{timer} = $pkt{$IP}{$port}{count}; }
		if (! $pkt{$IP}{$port}{start}) { $pkt{$IP}{$port}{start} = $data[3]; }
		$pkt{$IP}{$port}{block} = 1;
		if (substr($data[0], 0, 1) eq "#") {
		  $pkt{$IP}{$port}{block} = 2;
		  $pkt{$IP}{$port}{count} = 0;
		} else {
		  iptUpdate($IP, $port, "I");
		}
		if (DEBUG) { print "Loading ($pkt{$IP}{$port}{block}) $IP:$port\n"; }
	  }
	}
  } else {
	# Create new blockfile
	open FH, ">", BLOCKFILE;
	print FH "#IP:Port          #Multi  #Count  #Start          #Expires\n";
	close(FH);
  }
}


sub updateBlockfile { # IP, port, %pkt
  my $IP = shift;
  my $port = shift;
  my $pkt = shift;
  my $key = "$IP:$port";
  my $found = 0;

  my $expire = int($pkt{$IP}{$port}{start} + ((($pkt{$IP}{$port}{block} * 2) ** $pkt{$IP}{$port}{multi}) * $pkt{$IP}{$port}{timer} * BASETIME));

  open FH, BLOCKFILE;
  my @LINES = <FH>;
  close(FH);

  open FH, ">", BLOCKFILE;
  foreach $line (@LINES) {
	switch ($pkt{$IP}{$port}{block}) {
	  case 1 {
		if (($line =~ /^$key/) || ($line =~ /^\#$key/)) {
		  print FH setLen("$IP:$port"), "       $pkt{$IP}{$port}{multi} $pkt{$IP}{$port}{count} $pkt{$IP}{$port}{start} $expire\n";
		  $found++;
		} else { print FH $line; }
	  }

	  case 2 {
		if (($line =~ /^$key/) || ($line =~ /^\#$key/)) {
		  print FH setLen("#$IP:$port"), "      $pkt{$IP}{$port}{multi} $pkt{$IP}{$port}{count} $pkt{$IP}{$port}{start} $expire\n";
		  $found++;
		} else { print FH $line; }
	  }

	  else {
		print FH $line unless (($line =~ /^$key/) || ($line =~ /^\#$key/));
	  }
	}
  }

  if (($pkt{$IP}{$port}{block} == 1) && (!$found)) {
	print FH setLen("$IP:$port"), "     $pkt{$IP}{$port}{multi} $pkt{$IP}{$port}{count} $pkt{$IP}{$port}{start} $expire\n";
  }
  close(FH);
}

sub setLen {
  my $str = $_[0];
  if (length $str < 16) { $str .= "     "; }
  return $str;
}

sub iptUpdate { # IP, port, I/D
  my $ip = $_[0];
  if (substr($ip, 0, 1) eq "#") { $ip = substr($ip, 1); }
  my $port = $_[1];
  my $act = $_[2];

  if ($ip && $act) {
	if ($act eq "D") {
	  if ($port > 0) {
		$ipt_obj->delete_ip_rule($ip, '0.0.0.0/0', 'filter', 'INPUT', 'DROP', {'protocol' => 'udp', 's_port' => $port});
	  } else {
		$ipt_obj->delete_ip_rule($ip, '0.0.0.0/0', 'filter', 'INPUT', 'DROP', {'protocol' => 'udp', 'd_port' => 53});
	  }
	}
	if ($act eq "I") {
	  if ($port > 0) {
		$ipt_obj->add_ip_rule($ip, '0.0.0.0/0', 1, 'filter', 'INPUT', 'DROP', {'protocol' => 'udp', 's_port' => $port});
	  } else {
		$ipt_obj->add_ip_rule($ip, '0.0.0.0/0', 1, 'filter', 'INPUT', 'DROP', {'protocol' => 'udp', 'd_port' => 53});
	  }
	}
  }
}
There are no comments on this page.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki