develooper Front page | perl.perl5.porters | Postings from October 2002

[perl #17744] Security-Hole in module Safe.pm

Thread Next
From:
Andreas Jurenda
Date:
October 4, 2002 07:58
Subject:
[perl #17744] Security-Hole in module Safe.pm
Message ID:
rt-17744-39131.3.96370682846239@bugs6.perl.org
# New Ticket Created by  Andreas Jurenda 
# Please include the string:  [perl #17744]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt2/Ticket/Display.html?id=17744 >


Well, I have found a security problem in module Safe.pm

Sorry at may english, but my tongues are Pascal, Basic, C, C++,... maybe Perl but neither german nor english, but I will do my best ;-)

The problem belongs to these two versions of Safe.pm:
Safe.pm Version 2.06 at Perl 5.6.1 and
Safe.pm Version 2.07 at Perl 5.8.0

In both versions there is the same code for Safe::reval()

Safe::reval() execute a given code in a safe compartment.

But this routine has a one-time safeness.
If you call reval() a second (or more) time with the same compartment, you are potential unsafe.

These depends on the values of @_ at the entrypoint of the safe compartment.

Have a look at the source code of Safe::reval()

Source:
=======

sub reval {
    my ($obj, $expr, $strict) = @_;
    my $root = $obj->{Root};

    # Create anon sub ref in root of compartment.
    # Uses a closure (on $expr) to pass in the code to be executed.
    # (eval on one line to keep line numbers as expected by caller)
        my $evalcode = sprintf('package %s; sub { eval $expr; }', $root);
    my $evalsub;

        if ($strict) { use strict; $evalsub = eval $evalcode; }
        else         {  no strict; $evalsub = eval $evalcode; }

    return Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub);
}


In the last line there is the call for the execution of our $expr.
Inside $expr at runtime there are @_ set with ($root, $obj->{Mask}, $evalsub).

And thats the hole, because $_[1] is directly linked to $obj->{Mask}.

Modifying of $_[1] manipulate directly the operationmask of the safe compartment!

At the first time calling reval() and manipulation $_[1] has no effect.
But after that the second (and more) call you get the (un-)"safe" compartment with the manipulatet operation mask!

Example:
========

$codefullopmask = '$_[1] = chr(0x00) x 44;';   # at Perl 5.6.1 and 5.8.0 there are 352 built in opcodes (352/8=44)

$codewithtrape = <<'EOC';
opendir(DIR,"."); @d=readdir(DIR); closedir(DIR);
foreach my $dt (@d) { print "$dt\n"; }
EOC

use Safe;
$safe=new Safe;
$safe->deny(qw(opendir));       # deny opendir: You can't use opendir() inside the safe compartment

$safe->reval($codefullopmask);  # this manipulate the operation mask to full capability of all opcodes
$safe->reval($codewithtrap);    # now there is NO trap for opendir, and you get the directory!


The solution of this problem is very simple.

You have only put the operation-mask into a temporary variable for execution of $expr.
Here the source code of the solution. You have only modify the two commented lines.

Solution:
=========

sub reval {
    my ($obj, $expr, $strict) = @_;
    my $root = $obj->{Root};

    # Create anon sub ref in root of compartment.
    # Uses a closure (on $expr) to pass in the code to be executed.
    # (eval on one line to keep line numbers as expected by caller)
        my $evalcode = sprintf('package %s; sub { eval $expr; }', $root);
    my $evalsub;

        if ($strict) { use strict; $evalsub = eval $evalcode; }
        else         {  no strict; $evalsub = eval $evalcode; }

    my $temp_mask = $obj->{Mask};                                  # JURENDA: put opmask in temporary scalar
    return Opcode::_safe_call_sv($root, $temp_mask, $evalsub);     # JURENDA: call with this temp var
}


Now you can't modify the operationmask within the safe compartment.

Herzliche Grüße von Andreas Jurenda :-})


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About