I've been mucking with the beginning of an insecure-ish perl module. Basically, if you run taint, you have to regexp and untaint your input. This module presents a shell() function that works like the backtick operator, but will shell-escape anything passed to it.
While working on it, I was befuddled by the following:
- Quoted regexps for character classes can't be interpolated
- Quoted regexps for character classes can't use the qr//x for extended regexps.
- You have to map { s/$evil/\\$1/g; $_ } (my @a = @_);
instead of
map { s/$evil/\\$1/g; $_ } @_;
to get around the Modification of a read-only value attempted. I don't understand why map is trying to alter @_
with the s///.
package InSecurity;
use base 'Exporter';
our @EXPORT = ('shell', 'shell_escape');
# kermit may like SUVs, but I keep my environment clean.
delete @ENV{ grep { !/^(HOME|LOGNAME|USER)$/ } keys %ENV };
$ENV{'PATH'} = '/bin:/usr/bin';
my $evil_characters = qr![\;\|\!\$\`\&\*\(\)\>\<\'\"\\]!;
# no q//x or interpolation with character classes = the suck.
sub shell {
local($_);
my $cmd = join ' ', map { shell_escape($_) } @_;
return `$cmd `;
}
sub shell_escape {
local($_); $_ = $_[0];
s/($evil_characters)/\\$1/g;
/(.*)/ and return $1;
}
! caller() and do {
print "Running insecurity tests\n";
for (keys %ENV) { print "$_ => $ENV{$_}\n" }
printf("\`echo hi | grep hi\` => %s", shell("echo hi | grep hi"));
};
caller() and return 1;
$_ (the bit s/// operates on) is an alias to each item on @_ inside of
map{}. It isn't a copy of the item on the list.
Thus, when you modify $_ through s///, you are actually modifying the
elements of @_.
I would guess that you are calling shell() here as
shell("testing");
rather than
shell($test);
http://lists.debian.org/debian-user/2003/01/msg00940.html
http://dev.perl.org/perl6/rfc/344.html
Thanks! You're spot on; one has to take care regarding where the elements of @_ came from, making the @_ copy to @a almost obligatory. -- Patrick.