#!/usr/bin/perl # # Copyright (c) 2006-2007, Gregory Fleischer (gfleischer@gmail.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: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. 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. # 3. The names of the authors may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # use strict; use IO::File; my @cmds = qw(ls id nc); my @lines = ; my $mail = '/bin/mail'; my $tmpdir = '/challenge'; # decide how we want to restrict the environment # use a restricted shell? # this makes the most sense and has the least risk my $use_restricted = 1; # validate the list of commands sent # useful, but uh limiting :> my $check_commands = 1; # validate the commands for shell characters # this is so restrictive as to be useless my $check_command_chars = 0; # the path my $use_path = ""; if ($use_restricted) { $use_path = ".:/${tmpdir}-bin"; # good for restricted shell } else { $use_path = "/${tmpdir}-bin"; # good for non-restricted } my ($from, $cmd) = (undef, undef); foreach (@lines) { if (/^From:\s(.+?)(?:\s\<(.+?)\>)?\s*$/) { if ($2) { $from = $2; } else { $from = $1; } } if (/^Subject:\s*(.+?)\s*$/) { $cmd = $1; if ($cmd=~/^(Failure|Success)$/i) { # guard against mail loops $cmd = undef; } } } if ($cmd && $from) { my $valid = 1; if ($check_commands) { my @parts = split(/\s+/, $cmd); $valid = 0; foreach my $c (@cmds) { if ($parts[0] =~ /^(?:\.\/)?$c$/) { $valid = 1; last; } } if ($valid && $check_command_chars) { foreach my $p (@parts) { if ($p=~/`|\<|\>|;|\$|\(|\)/) { $valid = 0; last; } } } } %ENV = (); my @rc = (); my $subject = "Failure"; my $orig_cmd = $cmd; if ($valid) { if ($cmd=~s/^\s*\"+//) { $cmd=~s/\"+\s*$//; } $cmd=~s/\"/\\\"/g; my $is_restricted = ""; if ($use_restricted) { $is_restricted = "--restricted"; } my $exec = qq[env -i PS4="\\\$ " PATH="$use_path" /bin/bash --noprofile --norc $is_restricted -c "$cmd" 2>\&1]; print STDERR $exec, "\n"; chdir($tmpdir); @rc = `$exec`; $subject = $? ? "Failure" : "Success"; } else { @rc = ("invalid command [ $cmd ]\n"); } if ($from=~/^(.+?)@((?:[0-9]{1,3}\.){3}[0-9]{1,3})$/) { my $to = $1; my $server = $2; use IO::Socket; my $sock = IO::Socket::INET->new( PeerAddr => $server, PeerPort => 25, Proto => 'tcp' ); if ($sock) { local $| = 1; my $junk = <$sock>; my $buf =<; } $buf=<; print $sock "QUIT\n"; $junk=<$sock>; $sock->close(); } } else { open(PIPE, "| $mail -s $subject $from"); print PIPE "\$ ", $orig_cmd, "\n\n"; print PIPE @rc; close(PIPE); } open(LOG, ">>", "/tmp/runsh.log"); if (LOG) { print LOG "=" x 32, "\n"; print LOG scalar(localtime), ": $subject\n"; print LOG "[$orig_cmd]\n"; print LOG "[$cmd]\n"; print LOG @rc, "\n"; } close(LOG); }