Revision [2853]
This is an old revision of ddosDotPl made by JeffTaylor on 2012-04-04 08:53:27.
#!/usr/bin/perl # ddos.pl 2012.04.03 $| = 1; # Net::Pcap (debian: libnet-pcap-perl) # IPTables::ChainMgr (debian: libiptables-chainmgr-perl) #--- EDIT THESE VALUES AS NEEDED ---# use constant THRESH => 6; use constant MAXPERSEC => 40; use constant MAXCOUNT => 64; use constant MAXMULTI => 16; use constant MAXBLOCKS => 4; use constant BASETIME => 450/256; use constant BLOCKFILE => "/root/ddos.test"; use constant NETDEV => "eth0"; use constant NETMASK => "10.0.0.0/16"; #--- 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 %opts = ( 'iptables' => '/sbin/iptables', 'iptout' => '/tmp/iptables.out', 'ipterr' => '/tmp/iptables.err', 'debug' => 0, 'verbose' => 0 ); 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 { print "\nEnding script...\n"; foreach $IP (keys %pkt) { foreach $port (keys %{$pkt{$IP}}) { if ($pkt{$IP}{$port}{block} == 1) { print "* Unloading $IP:$port\n"; iptUpdate($IP, $port, "D"); } } } exit(0); } #------------ Initialize array with stored data ------------# 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]; $pkt{$IP}{$port}{timer} = $pkt{$IP}{$port}{count}; $pkt{$IP}{$port}{start} = $data[3]; $pkt{$IP}{$port}{block} = 1; if (substr($data[0], 0, 1) eq "#") { $pkt{$IP}{$port}{block} = 2; } else { iptUpdate($IP, $port, "I"); } print "Loading ($pkt{$IP}{$port}{block}) $IP:$port\n"; } } } else { # Create new blockfile open FH, ">", BLOCKFILE; print FH "#IP:Port #Multi #Count #Start\n"; close(FH); } #--------------------- Begin main loop ---------------------# open DUMP, "tcpdump -pnt -i ".NETDEV." not src net ".NETMASK." and not udp src port 53 and udp dst port 53 and '(ip[2:2] != 0)' 2>/dev/null |" or die; while (<DUMP>) { chomp ($line = <DUMP>); # 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}{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) { print "WARNING! $IP:$port adding existing block\n"; } $pkt{$IP}{$port}{block} = 0; $pkt{$IP}{$port}{count} = 0; $pkt{$IP}{$port}{multi} = 0; $pkt{$IP}{$port}{start} = $tmr; } # Do unless port is currently blocked if ($pkt{$IP}{$port}{block} != 1) { $pkt{$IP}{$port}{count}++; #$pkt{$IP}{$port}{"$bits[1].$bits[0]"}++; 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"; 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}; 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} * 2) ** $pkt{$IP}{$port}{multi}) * $pkt{$IP}{$port}{timer} * BASETIME); if ($tmr > $expire) { $blocked = $tmr - $pkt{$IP}{$port}{start}; if ($pkt{$IP}{$port}{block} == 2) { print "$hms Deleting $IP:$port after $blocked seconds\n"; $pkt{$IP}{$port}{block} = 0; updateBlockfile($IP, $port, %pkt); delete ($pkt{$IP}{$port}); } else { print "$hms Unblocking $IP:$port after $blocked seconds\n"; iptUpdate($IP, $port, "D"); $pkt{$IP}{$port}{block} = 2; $pkt{$IP}{$port}{count} = 0; $pkt{$IP}{$port}{start} = $tmr; $pkt{$IP}{$port}{multi}++; if ($pkt{$IP}{$port}{multi}>MAXMULTI) {$pkt{$IP}{$port}{multi}=MAXMULTI;} updateBlockfile($IP, $port, %pkt); } } } # 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} = 1; } } # 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) { 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) { if ($pkt{$IP}{0}{portblock} > 0) { print "$hms [$pkt{$IP}{0}{portblock}] Cleanup $IP:port\n"; } delete ($pkt{$IP}); } else { $pkt{$IP}{0}{count} = 0; } } #foreach IP } } #----------------------- Subroutines ------------------------# sub updateBlockfile { # IP, port, %pkt my $IP = shift; my $port = shift; my $pkt = shift; my $key = "$IP:$port"; my $found = 0; 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}\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}\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}\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}); } } } }