no re 'eval' not secure enough

M

Matthew Braid

Hi all,

Not really a question, more of a pointer....

Now that RE's can contain executable code (via the experimental (?{...}) and a
few others) it is no longer safe to accept an RE as an argument (either to a
program or to a subroutine), since it could, for instance, have:

(?{system("rm -rf /")})

buried in it.

"no re 'eval'" is supposed to stop this, as it disallows use of these constructs
in an RE using variable interpolation at run-time, ie this:

no re 'eval';
chomp(my $chk = <STDIN>);
chomp(my $re = <STDIN>);
print "OK!\n" if eval { $chk =~ $re };

will result in (if the STDIN is "123\n(?{print "UHOH!\n"})123\n"):

Eval-group not allowed at runtime, use re 'eval' in regex m/(?{print
"UHOH!\n"})123/ at test.pl line 4, <STDIN> line 2.

This is good, as long as the data stays raw like that.

Unfortunately, no re 'eval' allows a backdoor - if the re variable was created
with qr//, all is deemed good.

In a contrived example:

no re 'eval';
chomp(my $chk = <STDIN>);
chomp(my $re = <STDIN>);
{
use re 'eval';
$re = qr/$re/;
}
test($chk, $re);
sub test {
my ($chk, $re) = @_;
no re 'eval';
print "OK!\n" if $chk =~ $re;
}

results in (with the same input as before):

UHOH!
OK!

Now while this can't happen in raw data from outside the program (because it
can't be qr'd by the external person), it does mean any subroutine that accepts
an RE as an argument can't rely on no re 'eval' as the supplied RE might have
been created using qr// in a block that had had use re 'eval' turned on.

The only way around this I have found is to use stringy eval and a dummy
variable. To use the test program again:

no re 'eval';
chomp(my $chk = <STDIN>);
chomp(my $re = <STDIN>);
{
use re 'eval';
$re = qr/$re/;
}
test($chk, $re);
sub test {
my ($chk, $re) = @_;
no re 'eval';
my $null = ''; # DUMMY VARIABLE
print "OK!\n" if eval "\$chk =~ /\${null}$re/";
print "ERROR IS $@\n";
}

With the same input as before this results in:

ERROR IS Eval-group not allowed at runtime, use re 'eval' in regex
m/(?-xism:(?{print "UHOH!\n"})123)/ at (eval 2) line 1, <STDIN> line 2.

This is because of the $null variable - it hasn't been made by qr//, so it
triggers the no re 'eval' for you (its in the ${null} form because $re, which is
interpolated, may start with an alphanumeric and cause an unrelated error). no
re 'eval' was also included in the test sub so someone can't just use re 'eval'
before the sub is called.

It has to be a stringy eval because the no re 'eval' check is done at compile
time rather than run time (despite what the error message says....).

Its a very ugly kludge, but as I said its the only thing I've ever got to work
in a secure fashion. It would be fantastic if there was a pragma like no re
'eval_strict' or something that would stop embedded code entirely - I have
never, ever seen a need for it in everyday use.

MB
 
G

gnari

Unfortunately, no re 'eval' allows a backdoor - if the re variable was created
with qr//, all is deemed good.

In a contrived example:

no re 'eval';
chomp(my $chk = <STDIN>);
chomp(my $re = <STDIN>);
{
use re 'eval';
$re = qr/$re/;
}

I fail to imagine a scenario where this is relevant.
you might as well be complaining that 'use strict;'
does not work if you do 'no strict;'

why would you ever use re 'eval' for such a case?

gnari
 
B

Brian McCauley

Matthew said:
Not really a question, more of a pointer....

Now that RE's can contain executable code (via the experimental (?{...})
and a few others) it is no longer safe to accept an RE as an argument
(either to a program or to a subroutine), since it could, for instance,
have:

(?{system("rm -rf /")})

buried in it.

And since the tie and overload mechanisms allow arbitrary code to to
burried in seemingly plain scalar values you must not accept any
argument of any kind.
Now while this can't happen in raw data from outside the program
(because it can't be qr'd by the external person), it does mean any
subroutine that accepts an RE as an argument can't rely on no re 'eval'
as the supplied RE might have been created using qr// in a block that
had had use re 'eval' turned on.

I think you need to take a step back and look at your threat model. You
appear to be concerned that the person who wrote the code with 'use re
qw(eval)' in it is colluding with the supposed attacker. If this is the
case then there's no need for them to call your function at all they can
just execute the attackers code directly.
The only way around this I have found is to use stringy eval and a dummy
variable.

There is no need to get arround anything.
Its a very ugly kludge, but as I said its the only thing I've ever got
to work in a secure fashion.

You are imagining a security issue where there is none. If there are
security holes in the code that's calling your code there's nothing much
you can do about it. In one VERY special case where you know precisely
that there's a missplaced 'use re qe(eval)' in the calling code AND you
know that this only effect REs that get evaluated in your code AND you
can't get to fix the problem in the calling code THEN (and only then)
would it make sense to work-round this in your code.
It would be fantastic if there was a pragma
like no re 'eval_strict' or something that would stop embedded code
entirely - I have never, ever seen a need for it in everyday use.

Well I cant seen a need for such a pragma even in once-in-a-blue-moon use.

--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was sent to
Brian McCauley
And since the tie and overload mechanisms allow arbitrary code to to
burried in seemingly plain scalar values you must not accept any
argument of any kind.

Not relevant, since this code is read from scripts, not the user input.
I think you need to take a step back and look at your threat model. You
appear to be concerned that the person who wrote the code with 'use re
qw(eval)' in it is colluding with the supposed attacker.

Again, I do not think your argument is fully applicable; for it to be,
it should be a readily available knowledge *which* subroutines
interpolate their arguments in scope of 'use re "eval"'. It is not.

I think the threat is real, though not often applicable.

Hope this helps,
Ilya
 
B

Brian McCauley

In my prevous follow-up I made somewhat woolly arguments about threat
analysis. But here's a much simpler argument...

Batthew said:
no re 'eval';
chomp(my $re = <STDIN>);
{
use re 'eval';
$re = qr/$re/;
}

OK stop there, that code in itself allows the user to execute arbitrary
code using (?{BEGIN{...}}). Any further discussion of what happens to
$re subsequently is therefore moot.
 
M

Matthew Braid

Matthew said:
Not really a question, more of a pointer....

Now that RE's can contain executable code (via the experimental (?{...}) and a few others) it is no longer safe to accept an RE as an argument (either to a program or to a subroutine), since it could, for instance, have:

(?{system("rm -rf /")})

buried in it.
<snip>

Oh crap, sorry about that - when I'm thinking about a problem I'll often write
up a message as if I was trying to explain it to someone else (I've found this
often leads me to the answer) and then, if I've figured it out myself (or I've
figured out I'm wrong - as was the case here) I hit cancel, and if I'm still
lost I hit send. I must have accidentally hit send for this one.... whoops.

In this case I was looking for holes in the idea of 'securing' an object's data
through a my'd hash inside the package keyed on an instance's stringification
(eg Object::Child=SCALAR(0x813221c)) and I thought that maybe an eval inside an
RE might be able to access that. After testing I found they can't. That's when I
was supposed to hit cancel instead of send :)

Sorry again,
MB
 
N

nobull

Matthew Braid said:
Matthew Braid wrote:

In this case I was looking for holes in the idea of 'securing' an object's data
through a my'd hash inside the package keyed on an instance's stringification
(eg Object::Child=SCALAR(0x813221c)) and I thought that maybe an eval inside an
RE might be able to access that. After testing I found they can't. That's when I
was supposed to hit cancel instead of send :)

But your threat model is still bogus. Your module cannot 'secure' its
data against the determined programmer[1]. The programmer can simply
edit the source of your module and change the hash from lexically
scoped to package scoped or they could use B and PadWalker.

[1] Or anyone able to execute arbitrary Perl code on the system due to
a security hole elsewhere.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,123
Latest member
Layne6498
Top