#!/usr/bin/perl ###################################################################### # # http://www.unspecific.com/spa/ # # # Copyright (c) 2005, MadHat (madhat@unspecific.com) # 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. # * Neither the name of MadHat Productions nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # 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 # OWNER 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. # ###################################################################### $sparc = "$ENV{HOME}/.spa/sparc"; $gpgdir = "$ENV{HOME}/.gnupg"; use Getopt::Std; use GnuPG::Interface; use Net::RawIP; use Socket; $VERSION = 0.3; getopts("p:c:D:s:S:i:"); # -D is debug, with values form 1-4 for level of debugging # we need what to do and server to send it to if (!$opt_c or !$opt_s) { print STDERR "-c for command and -s for server are both required\n"; &usage; } # just look at the first if ($opt_p =~ /^t/i) { $opt_p = 'tcp'; } elsif ($opt_p =~ /^i/i) { $opt_p = 'icmp'; print STDERR "ICMP is not supported at this time\n"; &usage; } elsif ($opt_p =~ /^u/i) { $opt_p = 'udp'; } else { $opt_p = 'tcp'; } # specify the action to take if ($opt_c eq 'open') { $opt_c = 1; } elsif ($opt_c eq 'exec') { $opt_c = 3; } elsif ($opt_c eq 'rvsh') { print STDERR "Reverse Shell is not supported at this time\n"; &usage; } else { print STDERR "Your option for -c is not supported at this time\n"; &usage; } # if you don't specify the IP, we use the src ip (send 0) if (!$opt_i) { $opt_i = '0'; } # load the seq numbers and user to use &load_conf($sparc); my @data = @ARGV; my $data = join(' ', @data); if (!$conf{$opt_s}) { print "The server $opt_s does not appear to be setup.\n"; print "Please check with the server admin to setup this new server.\n\n"; exit 1; } my $sport = int(rand(64512)) + 1024; my $dport = int(rand(64512)) + 1024; my $nseq = int(rand(64512)); my @time = gmtime(); $time[5] = $time[5] + 1900; for ($i = 0; $i < 5; $i++) { if (length($time[$i]) < 2) { $time[$i] = "0$time[$i]"; } } my @seq = split(',', $conf{$opt_s}{'seq'}); if ($opt_S =~ /^\d{3,5}$/) { unshift(@seq, $opt_S); } my $time = "$time[5]$time[4]$time[3]$time[2]$time[1]$time[0]"; my @plaintext = ("$time:$seq[0],$seq[1],$nseq:$opt_i:$opt_c:$data"); print STDERR "DATASENT: $plaintext[0]\n" if ($opt_D); # reset for saving after run if ($opt_S) { $conf{$opt_s}{'seq'} = "$opt_S,$nseq"; } elsif ($seq[1]) { $conf{$opt_s}{'seq'} = "$seq[1],$nseq"; } else { $conf{$opt_s}{'seq'} = "$seq[0],$nseq"; } my $gnupg = GnuPG::Interface->new(); $gnupg->options->default_key( $conf{$opt_s}{user} ); $gnupg->options->push_recipients( "spa\@$opt_s" ); print STDERR "Seting homedir for gnupg\n" if ($opt_D > 1); $gnupg->options->hash_init( homedir => $gpgdir ); my ( $input, $output, $error, $status_fh ) = ( IO::Handle->new(), IO::Handle->new(), IO::Handle->new(), IO::Handle->new(), ); print STDERR "seting handles for gnupg\n" if ($opt_D > 1); my $handles = GnuPG::Handles->new( stdin => $input, stdout => $output, stderr => $error, status => $status_fh, ); print STDERR "launching gnupg\n" if ($opt_D > 1); my $pid = $gnupg->sign_and_encrypt( handles => $handles ); print STDERR "launched gnupg $pid\n" if ($opt_D > 1); print $input @plaintext; close $input; my @ciphertext = <$output>; close $output; my @error = <$error>; print "@error\n" if ($opt_D); close $error; close $status_fh; waitpid $pid, 0; $ciphertext = join('', @ciphertext); if ($opt_D > 3) { open(TMP, ">spa_packet") or die("spa_packet $!"); print TMP $ciphertext; close(TMP); open(TMP, ">spa_raw") or die("spa_raw $!"); print TMP @plaintext; close(TMP); } print STDERR "Ciphertext created. Length: " . length($ciphertext) . "\n" if ($opt_D > 2); if ($opt_p eq 'tcp') { $send = new Net::RawIP(); $send = new Net::RawIP({tcp => {}}); $send->set( { ip => { daddr => $opt_s, ttl => 255, tos => 0x08 , frag_off => 0, id => int(rand(65536)) }, tcp => { source => $sport, dest => $dport, syn => 1, window => 16384, ack => 0, doff => 5, urg => 0, } }); } elsif ($opt_p eq 'udp') { $send = new Net::RawIP({udp => {}}); $send->set( { ip => { daddr => $opt_s, ttl => 255, tos => 0x08 , frag_off => 0, id => int(rand(65536)) }, udp => { source => $sport, dest => $dport} } ); } elsif ($opt_p eq 'icmp') { $send = new Net::RawIP({icmp => {}}); $send->set( { ip => { daddr => $opt_s, ttl => 255, tos => 0x08 , frag_off => 0, id => int(rand(65536)) }, icmp => {type => 8} } ); } $send->set( { $opt_p => { data => $ciphertext } } ); print STDERR "Sending message to $opt_s of length " . length($ciphertext) . "\n" if ($opt_D > 2); $send->send(); # or die ("Can't send packet: $!" . $send->strerror); if (&save_conf($sparc) == -1) { print STDERR "ERROR SAVING CONFIG\n"; } sub load_conf { my ($file) = @_; my $group = ''; undef %conf; open(CONF, $file) or die ("Unable to open config($file): $!"); while() { print STDERR "$_" if ($opt_D > 3); chomp; push @raw_conf, $_; next if (/^#/ or /^\s*$/); if (/^\[([\w\-\.]+)\]/) { $group = $1; print STDERR "GROUP: $group\n" if ($opt_D > 2); } elsif ($group) { my ($key, $value) = split(/ ?= ?/); $conf{$group}{$key} = $value; print STDERR "VALUE: $group - $key => $value\n" if ($opt_D > 2); } } close(CONF); } sub save_conf { my ($file) = @_; my $group = ''; open(CONF, ">$file") or return -1; for my $group (sort keys %conf) { print CONF "[$group]\n"; print STDERR "save_conf: [$group]\n" if ($opt_D > 2); for my $key (sort keys %{$conf{$group}}) { print CONF "$key=$conf{$group}{$key}\n"; print STDERR "save_conf $key=$conf{$group}{$key}\n" if ($opt_D > 2); } } close(CONF); } sub usage { print "$0 [-p ] [-i ] [-S ] -c -s
\n"; print " -c code to send to \n"; print " 'open' to open a port\n"; print " 'rvsh' to open a reverse shell\n"; print " 'exec' to execute a command\n"; print " -s server to send encrypted to\n"; print " -S sequence number to use overriding config (reset seq from admin)\n"; print " -i to specify the IP to open a port for\n"; print " leave blank to use the IP the packet comes from (good for NAT)\n"; print " -p protocol to use for the spa packet\n"; print " tcp, udp or icmp - defaults to tcp\n\n"; print "
with 'open' would be the port to open\n"; print " with 'exec' is the command to execute on the remote host\n"; print "\n Examples: \n"; print " $0 -c open -i 10.0.0.5 -s spa_server.foocorp.biz 22\n"; print " will open port 22 for 10.0.0.5 on spa_server.foocorp.biz\n\n"; print " $0 -c exec -s spa_server.foocorp.biz /usr/bin/reboot\n"; print " will reboot spa_server.foocorp.biz\n\n"; print " Expected GPG public key for spa@ in your local keyring\n"; exit 0; }