#!/usr/bin/perl

##########################################################################
# This is a cut-down version of the program TarPop (nopop) and is
# written by Paul Grosse.
#
# This version (0.0.1 SE) Copyright 2004 by Future Publishing.
# All rights reserved.
#
# Redistribution and use in source is permitted provided that this entire
# copyright notice is duplicated in all such copies.
#
# This software is provided "as is" and without any expressed or implied
# warranties, including, without limitation, the implied warranties of
# merchantibility and fitness for any particular purpose.
##########################################################################

# TarPop is a simple POP3 server that will run on a system that does not
# use pop3 (port 110) but is configured to receive pop3 request traffic.
#
# Also, if it appears that we have someone logged into port 110 with telnet
# (ie, the time it takes for the responses is greater than 1 second), the
# honeypot stays in honeypot mode (ie, lots of errors caused by typing
# errors?) otherwise, it toggles into tarpit mode (ie, long delays between
# requests and server responses - could it be someone trying it out with a
# mail-client (typing in a new guess at a password each time) ?)
#
# In other words:
# * You set up a honeypot on your webserver, full of false email addresses
# with your domain name so that it can get spidered.
#
# * Somebody spidered it and is possibly trying to gain access to those
# non-existent mailboxsx by connecting to port 110, guessing the userid
# (same as the email address which they already have) and then guessing
# a password.
#
# * They then use up a lot of their time achieving nothing on this honeypot
# instead of breaking in somewhere else.
#
# To get it to work:
#
# * copy this file to /usr/local/etc/ and make sure that it is called 'nopop'
#
# * make sure that it is executable
#
# * you need to have a file in /etc/xinetd.d called 'pop3' containing
# the following (just copy it, paste it into a new file, remove the
# left set of hash marks (#) and save it there):
## begin #################### /etc/xinetd.d/pop3 ##################
## default: on
##
## TarPop: a combined POP3 honeypot and tarpit
#service pop3
#{
#	socket_type     = stream
#	protocol        = tcp
#	wait            = no
#	user            = root
#	server          = /usr/local/etc/nopop
#       log_type        = FILE /var/log/pophp/nopopx 2048 2100
#	log_on_success  = PID HOST
#	log_on_failure  = HOST USERID
#	instances       = 10
#	bind            = 192.168.168.200
#}
## end #################### /etc/xinetd.d/pop3 ##################
#
# * in /etc/hosts.allow you need an entry like this:
# pop3 : ALL : twist exec /usr/local/etc/nopop %h %a
#
# * kick it all into action.
#
# * Open up a console and type:
# netstat -atn | grep LISTEN
# if all is well, you should see the ip address you are using along
# with port 110 somewhere in the list ie:
# tcp     0    0 10.1.6.20:110    0.0.0.0:*    LISTEN

# declare subroutines that need it
#
# print out a line of text to STDOUT and also
# a PID and time stamp version to the log file
sub printnlog ($);
#
# Sleepy time - send the server into schlafie-schlafie land
sub schlafzeit ($);

##### get things going with a response ###
## make STDOUT respond immedeately
select(STDOUT); $| = 1;
## note the time now for log and salt purposes in two formats
$c_time = substr(localtime, 4, -5);
$s_time = time;
## select the salt for any hash-based authentication
$salt = "<$$.$s_time".chr(64)."pop.domain.com>";
## respond
print "+OK POP3 server ready $salt\n";
## mode honeypot or tarpit? start off as a honeypot
$teergrube = 0;
## any messages deleted ?
$delmessages = 0;

##### open the ongoing log file ###
if (-e '/var/log/pophp/poplog') {
  open(POPLOG, ">>/var/log/pophp/poplog");
  print POPLOG "NEW $$ $c_time $remhost\n$$ $c_time +OK POP3 server ready $salt\n";
} else {
  open(POPLOG, ">/dev/null");
}

##### set up some variables ###
# $process_state is the state that the POP3 server is in...
#    = 1 AUTHORISATION STATE -- pre-user-identified
#    = 2 AUTHORISATION STATE -- pre-password
#    = 3 TRANSACTION STATE
#    = 4 UPDATE STATE
$process_state = 1;
# first time in the loop: set
$f_loop = 1;
# only allowed 3 goes before the system kicks you off
$faileduserids = 0;

# store some variables that are passed to the server from the client
# (space for some more later, denoting such things as:
# how easy we should make the passwords, the mail present, errors and so on.
my ($user_name);

##### start program loop ##
while (<STDIN>) {
  # remove line-ending characters
  chomp;
  # remove returns
  s/\r//g;
  # log it - note, we are dong this longhand here because we
  # don't want it echoed back to the user
  $c_time = substr(localtime, 4, -5);
  print POPLOG "$$ $c_time $_\n";
  # is this the first time???
  if ($f_loop) {
    # first time around the loop so assess the cracker
    $f_loop = 0;
    # get the response time
    $f_time = time;
    $delta_t = $f_time - $s_time;
    # print the string in the POPLOG file
    print POPLOG "$$            --            delta_t = $delta_t seconds\n";
    if ($delta_t < 3) {
      # quick response so probably a mail client
      $teergrube = 1;
      print POPLOG "$$            --            Fast response: selecting tarpit mode\n";
    } else {
      # slow response so probably someone on
      # telnet or similar therefore $teergrube = 0
      print POPLOG "$$            --            Slow response: selecting honeypot mode\n";
    };
  };
  # now look at the input and process it
  /^QUIT/ and do {
    # go to sleep if a tarpit
    schlafzeit(1);
    # quit exists in all states
    if ($process_state == 1) {
      printnlog "+OK POP3 server pop.domain.com signing off";
    } elsif ($process_state == 2) {
      printnlog "+OK $user_name POP3 server signing off";
    } elsif ($process_state == 3) {
      if ($delmessages == 0) {
        printnlog "+OK $user_name POP3 server signing off";
      } else {
        printnlog "+ERR $user_name some deleted messages not removed";
      };
    } else {
      # $process_state = 4
      printnlog "+OK $user_name POP3 server signing off";
    };
    done(0);
    next;
  };
  /^USER\s+(.*)/ and do {
    if ($process_state == 1) {
      schlafzeit(1);
      $user_name = $1;
      if (length($user_name)) {
        printnlog "+OK";
        $process_state = 2;
      } else {
        printnlog "-ERR User ID must be entered";
      };
    } else {
      schlafzeit(2);
      printnlog "-ERR User already defined";
    };
    next;
  };
  /^PASS\s+(.*)/ and do {
    schlafzeit (1);
    if ($process_state == 2) {
      printnlog "-ERR User ID/password combination not recognised";
      $user_name = "";
      $faileduserids++;
      $process_state = 1;
      if ($faileduserids > 2) {
        schlafzeit (1);
        printnlog "+OK POP3 server pop.domain.com signing off";
        print POPLOG "$$            --            Reached max UserID fails (max = 3)\n";
        done(0);
      };
    } else {
      if ($user_name == "") {
        printnlog "-ERR Need User ID first";
      };
    };
    next;
  };
  /^PASS/ and do {
    schlafzeit (1);
    if ($process_state == 2) {
      printnlog "-ERR Failed Log - USERid and PASSword needed";
      $user_name = "";
      $faileduserids++;
      $process_state = 1;
    } else {
      if ($user_name == "") {
        printnlog "-ERR Need User ID first";
      };
    };
    next;
  };
  /^STAT/ and do {
    schlafzeit(1);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
    } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^LIST\s+(.*)/ and do {
    schlafzeit (1);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
    } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^LIST/ and do {
    schlafzeit (1);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
    } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^RETR\s+(.*)/ and do {
    schlafzeit (1);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
    } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^DELE\s+(.*)/ and do {
    schlafzeit (1);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
    } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^NOOP/ and do {
    schlafzeit (2);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
    } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^RSET/ and do {
    schlafzeit (1);
    if ($process_state == 3) {
      # as you add functionality, it can go in here.
      } else {
      printnlog "-ERR Not logged in";
      $process_state = 1;
    };
    next;
  };
  /^APOP\s+(.*)/ and do {
    schlafzeit (2);
    printnlog "-ERR Command not supported";
    # you can add this by using the salt that
    # we created and sent at the beginning.
    # This is used as an MD5 has along with
    # the password to create a hash. That is
    # sent back to the server in the form of:
    # APOP [userid] [hash]
    # so the has must be the right length and
    # it must have a 'valid' userid in front.
    if ($process_state == 2) {
      $process_state = 1;
    };
    next;
  };
  /^TOP\s+(.*)/ and do {
    schlafzeit(2);
    printnlog "-ERR Command not supported";
    if ($process_state == 2) {
      $process_state = 1;
    };
    next;
  };
  /^UIDL\s+(.*)/ and do {
    schlafzeit (2);
    printnlog "-ERR Command not supported";
    if ($process_state == 2) {
      $process_state = 1;
    };
    next;
  };
  # all known states accounted for. Now the rest
  schlafzeit (3);
  printnlog "-ERR command not recognised";
  if ($process_state == 2) {
    $process_state = 1;
  };
};

done(0);
exit(0);

##### subroutines ###
## push the client mailer into schlafie-schlafie land
sub schlafzeit ($) {
  #sleepy time
  my $sst = shift;
  if ($teergrube == 1) {
    if ($sst == 1) {
      sleep int(rand 5) + 50;
    } elsif ($sst == 2) {
      sleep int(rand 8) + 45;
    } else {
      sleep int(rand 7) + 2;
    };
  } else {
    sleep int(rand 5) +3;
  };
}
## print output to the remote client and also to the poplog
# note that no new-line is in the string that is sent to this routine
sub printnlog ($) {
  my $pt_a_lg = shift;
  $c_time = substr(localtime, 4, -5);
  print "$pt_a_lg\n";
  print POPLOG "$$ $c_time $pt_a_lg\n";
}
## tidy up and log
# if youhave set up the pop3 xinetd file to save output to
# the log (along with the PID which here is $$), that is
# where the IP address is stored.
sub done {
  $f_time = time;
  $delta_t = $f_time - $s_time;
  open(CRACKLOG, ">>/var/log/pophp/pophacklog");
    $maintime = substr(localtime, 4, -5);
    print CRACKLOG "$maintime - duration = $delta_t\n";
  close(CRACKLOG);
  print POPLOG "$$            --            duration: $delta_t seconds\n";
  close(POPLOG);
  exit(0);
}
##### finish ###
