#!/usr/bin/perl -T #-*-Perl-*- # # $Id: cmdexe.pl,v 1.1 2004/12/31 18:54:23 provos Exp $ # # cmdexe.pl -- experimental Perl script, that works with honeyd, to # emulate a cmd.exe prompt. It logs the command line # entered. Non-printable characters are logged in # hexdump format. # # Copyright (c) 2004 Luiz Eduardo Roncato Cordeiro # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. ###################################################################### (my $PROGNAME = $0) =~ s@.*/@@; use strict; use warnings; use Getopt::Std; use Fcntl qw(:flock); use POSIX qw(strftime); use File::Path; ###################################################################### my %option = (); getopts('Vhdl:t:p:n:', \%option); #For CVS , use following line my $VERSION=sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/); # unbuffered output. $| = 1; # set PATH. $ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin'; ###################################################################### # configuration defaults -- some of them can be changed via # command line options. # logfile. my $logdir = '/var/cmdexe'; my $logfile = $logdir . '/' . 'logfile'; # timeout limit, in seconds. my $timeout = 60; # echo on or off my $echo = 1; # hexdump length my $hexdumplen = 8; # ip number regex my $ipregex = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\." . "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\." . "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\." . "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"; # default personality my $personality = "winxp"; my $personalities = "win95|win98|winme|winnt|win2000|winxp"; my %info = ( win95 => { systemname => "Microsoft(R) Windows 95", copyright => " (C) Copyright Microsoft Corp 1981-1996", error => "bad command or file name" }, win98 => { systemname => "Microsoft(R) Windows 98", copyright => " (C) Copyright Microsoft Corp 1981-1998", error => "Bad command or file name" }, winme => { systemname => "Microsoft(R) Windows Millenium", copyright => " (C) Copyright Microsoft Corp 1981-1999", error => "bad command or file name" }, winnt => { systemname => "Microsoft(R) Windows NT(TM)", copyright => "(C) Copyright 1985-1996 Microsoft Corp.", error => "The name specified is not recognized as an\n". "internal or external command, operable program or batch file." }, win2000 => { systemname => "Microsoft Windows 2000 [Version 5.00.2195]", copyright => "(C) Copyright 1985-2000 Microsoft Corp.", error => "The name specified is not recognized as an\n". "internal or external command, operable program or batch file." }, winxp => { systemname => "Microsoft Windows XP [Version 5.1.2600]", copyright => "(C) Copyright 1985-2001 Microsoft Corp.", error => "The name specified is not recognized as an\n". "internal or external command, operable program or batch file." } ); ###################################################################### # display usage if requested. show_usage() if ($option{'h'}); # display version if requested. show_version() if ($option{'V'}); # get logdir, if provided. if (defined($option{'l'})) { if ($option{'l'} =~ /^([\w\.\/-]+)$/) { $logdir = $1; $logfile = $logdir . '/' . 'logfile'; } else { show_usage(); } } # get hexdump length. If not provied use default value. if (defined($option{'n'})) { if ($option{'n'} =~ /^(\d+)$/) { $hexdumplen = $1; } else { show_usage(); } } # get timeout. If not provied use default value. if (defined($option{'t'})) { if ($option{'t'} =~ /^(\d+)$/) { $timeout = $1; } else { show_usage(); } } # get personality. If not provied use default value. if (defined($option{'p'})) { if ($option{'p'} =~ /^($personalities)$/) { $personality = $1; } else { show_usage(); } } # get srchost, dsthost, srcport and dstport from the environment # variables set by honeyd. my $srchost = '127.0.0.1'; if (defined($ENV{'HONEYD_IP_SRC'}) && $ENV{'HONEYD_IP_SRC'} =~ /^($ipregex)$/) { $srchost = $1; } my $dsthost = '127.0.0.1'; if (defined($ENV{'HONEYD_IP_DST'}) && $ENV{'HONEYD_IP_DST'} =~ /^($ipregex)$/) { $dsthost = $1; } my $srcport = 0; if (defined($ENV{'HONEYD_SRC_PORT'}) && $ENV{'HONEYD_SRC_PORT'} =~ /^([0-9]{1,5})$/) { $srcport = $1; } my $dstport = 0; if (defined($ENV{'HONEYD_DST_PORT'}) && $ENV{'HONEYD_DST_PORT'} =~ /^([0-9]{1,5})$/) { $dstport = $1; } ###################################################################### # main # install SIGALRM handler. $SIG{ALRM} = sub { my $msg = sprintf("timed out after %d seconds", $timeout); logentry($logfile, $msg); die("\n$PROGNAME: $msg\n"); exit 0; }; alarm $timeout; logentry($logfile, sprintf("connection from %s:%d to %s:%d", $srchost, $srcport, $dsthost, $dstport)); printf("%s\n%s\n\n", $info{$personality}{systemname}, $info{$personality}{copyright}); print "C:\\>"; # read stdin from honeyd. while () { chomp; $_ =~ s/\r$//; # extract the last 0x0A if it exists if ($_ =~ /[[:^print:]]/) { my @lines = hexdump($_); foreach my $line (@lines) { logentry($logfile, sprintf("hex: %s", $line)); } } else { logentry($logfile, sprintf("cmd: %s", $_)); } $echo = 0 if ($_ =~ /echo\s+off/i); $echo = 1 if ($_ =~ /echo\s+on/i); if ($_ =~ /^exit/i) { logentry($logfile, sprintf("exiting %s", $PROGNAME)); exit 0; } print $info{$personality}{error} . "\n" if ($echo); print "\nC:\\>" if ($echo); alarm $timeout; } alarm 0; logentry($logfile, "forced exit of cmdexe.pl (eg, ^C in a connection)"); exit 0; ###################################################################### # create a log entry. sub logentry { my ($logfile, $msg) = @_; my $datum = strftime "%F %T %z", localtime; my $pid = $$; open(LOG, ">>$logfile") || die "$PROGNAME: $logfile: $!\n"; flock(LOG, LOCK_EX); seek(LOG, 0, 2); printf LOG ("%s: %s[%d]: %s\n", $datum, $PROGNAME, $pid, $msg); flock(LOG, LOCK_UN); close(LOG); } ###################################################################### # return a hexa representation of data. sub hexdump { my ($buffer) = @_; my @bytes = unpack('C*', $buffer); my $bufferlen = length $buffer; my @lines = (); my $line = ""; my $ascii = ""; for (my $i=0; $i<$bufferlen; $i++) { $line .= sprintf('%0.2X ', $bytes[$i]); $ascii .= $bytes[$i] >= 32 && $bytes[$i] < 127 ? chr $bytes[$i] : '.'; if ($i % $hexdumplen == $hexdumplen - 1 || $i == $bufferlen - 1) { $line .= ' ' x (($hexdumplen - 1) - ($i % $hexdumplen)) . '|' .$ascii; $line .= ' ' x (($hexdumplen - 1) - ($i % $hexdumplen)) . '|'; push @lines, $line; $line = $ascii = ""; } } return @lines; } ###################################################################### # print program usage and exit. sub show_usage { print <