perl security question

T

Tulan W. Hu

I have a C wraper program that has a setgid turned on.
The C wraper will call proper perl program with correct gid.
The perl program will exec another program based upon the $ARGV[0].
This works fine under perl 5.6. However, it stop working
when I recently upgraded to perl 5.8.2.
It gives me the following error
Insecure dependency in exec while running with -T switch at myprog line 26
If a user in the same group of the program, it works fine, but if a user is
not
in the the group, then it prints out the Insecure message.

Any suggestions? How can I make the exec work for a user not in the group?

c program
main() {
system("/path/prog1 /path/prog2");
}

/path/prog1:
#!/usr/bin/perl
use lib '/path/mylib';
use English;
delete @ENV{qw(IFS ENV)};
$ENV{PATH} = '/bin';
my $cmd = shift;
$EGID= $GID;
exec $cmd, 'arg1', 'arg2';
die "can't exec $cmd: $!";

/path/prog2:
#!/usr/bin/perl
print "hello $ARG[0] $ARG[1]\n";
 
B

Ben Morrow

Tulan W. Hu said:
I have a C wraper program that has a setgid turned on.
The C wraper will call proper perl program with correct gid.
The perl program will exec another program based upon the $ARGV[0].

Why? Why not simply exec the right program in the first place?
This works fine under perl 5.6. However, it stop working
when I recently upgraded to perl 5.8.2.
It gives me the following error
Insecure dependency in exec while running with -T switch at myprog line 26
If a user in the same group of the program, it works fine, but if a user is
not
in the the group, then it prints out the Insecure message.

As this program is called from a setid C program, you should have -t
on the #! line. Then you would always get the message.
Any suggestions? How can I make the exec work for a user not in the
group?

Untaint $ARGV[0] with a *carefully* *crafted* regex that only lets
through programs you want to be able to exec. See perldoc perlsec, and
read *all* of it before you go any further.
c program
main() {
system("/path/prog1 /path/prog2");
}

/path/prog1:
#!/usr/bin/perl
use lib '/path/mylib';
use English;
Why?

delete @ENV{qw(IFS ENV)};
$ENV{PATH} = '/bin';
my $cmd = shift;
$EGID= $GID;

Another way to get rid of the taint error would be to fork and exec
manually in the C program, and do this setregid step between the
two. Then the perl program wouldn't be setid, and so wouldn't be
tainting.
exec $cmd, 'arg1', 'arg2';
die "can't exec $cmd: $!";

/path/prog2:
#!/usr/bin/perl
print "hello $ARG[0] $ARG[1]\n";

Ben
 
B

Ben Morrow

Ben Morrow said:
As this program is called from a setid C program, you should have -t
on the #! line. Then you would always get the message.

$*%&# fingers!

I meant -T, of course.

Ben
 
D

David Dyer-Bennet

Ben Morrow said:
Tulan W. Hu said:
I have a C wraper program that has a setgid turned on.
The C wraper will call proper perl program with correct gid.
The perl program will exec another program based upon the $ARGV[0].

Why? Why not simply exec the right program in the first place?

Because you *can't*. Apache will refuse to exec a setuid/setgid
program. (So the sequence I find myself using is different from that
described by the OP; I use a non-setuid intermediate to invoke the
setuid perl script.)
 
T

Tulan W. Hu

David Dyer-Bennet said:
Ben Morrow said:
Tulan W. Hu said:
I have a C wraper program that has a setgid turned on.
The C wraper will call proper perl program with correct gid.
The perl program will exec another program based upon the $ARGV[0].

Why? Why not simply exec the right program in the first place?

Because you *can't*. Apache will refuse to exec a setuid/setgid
program. (So the sequence I find myself using is different from that
described by the OP; I use a non-setuid intermediate to invoke the
setuid perl script.)

In fact, my problem does not involve Apache at all.
My c and perl programs are owned by an user, but the person, who runs
the program at the unix prompt, is not in the same group as the program
owner.
I believe perl 5.8.2 made the security tighter than perl 5.6.

I put '-t' in my program1 as Ben suggested and it still gives me the warning
but executes the program2.
 
B

Ben Morrow

Tulan W. Hu said:
In fact, my problem does not involve Apache at all.
My c and perl programs are owned by an user, but the person, who runs
the program at the unix prompt, is not in the same group as the program
owner.
I believe perl 5.8.2 made the security tighter than perl 5.6.

I put '-t' in my program1 as Ben suggested and it still gives me the warning
but executes the program2.

Damn damn damn!!!

DO NOT USE -t IN PRODUCTION CODE.

-t was a typo. I meant to type -T. The correct answer is, as I said,
to use a regex to check that $ARGV[0] contains what you expect. Read
perldoc perlsec.

Ben
 
T

Tulan W. Hu

Ben Morrow said:
Tulan W. Hu said:
In fact, my problem does not involve Apache at all.
My c and perl programs are owned by an user, but the person, who runs
the program at the unix prompt, is not in the same group as the program
owner.
I believe perl 5.8.2 made the security tighter than perl 5.6.

I put '-t' in my program1 as Ben suggested and it still gives me the warning
but executes the program2.

Damn damn damn!!!

DO NOT USE -t IN PRODUCTION CODE.

-t was a typo. I meant to type -T. The correct answer is, as I said,
to use a regex to check that $ARGV[0] contains what you expect. Read
perldoc perlsec.

Ben

I knew you had a correction. I have done the checking for $ARGV[0] with -T
but it gives the same error message and not executes the program 2.
In our case, the parameter is hard coded in another program by a programmer
instead of entering by an user. I would assume it is safe.
 
B

Ben Morrow

Tulan W. Hu said:
Ben Morrow said:
Damn damn damn!!!

DO NOT USE -t IN PRODUCTION CODE.

-t was a typo. I meant to type -T. The correct answer is, as I said,
to use a regex to check that $ARGV[0] contains what you expect. Read
perldoc perlsec.

Ben

I knew you had a correction. I have done the checking for $ARGV[0] with -T
but it gives the same error message and not executes the program 2.
In our case, the parameter is hard coded in another program by a programmer
instead of entering by an user. I would assume it is safe.

NO. There are very good reasons for the taint check errors; DO NOT get
rid of them.

What if someone else invokes your program? What if the first program
is compromised?

The whole point of taint checking is that you do not *assume* things
are safe, you *check* them. Have you read perldoc perlsec yet? If you
had, you would know how to untaint data, and you would have your
solution.

You still haven't answered: why do you need to do this? I strongly
suspect there is a better solution.

Ben
 
T

Tulan W. Hu

Ben Morrow said:
You still haven't answered: why do you need to do this? I strongly
suspect there is a better solution.
Sorry I cannot give you a good answer on this question.
This just very old code we got. It works when we were using perl 5.6
but not perl 5.8.2 and no one really understands the reason behind it.

I'll re-read the security section and hopefully I'll find out the answer.
Thanks!
 
M

Matthew Braid

Tulan said:
I have a C wraper program that has a setgid turned on.
The C wraper will call proper perl program with correct gid.
The perl program will exec another program based upon the $ARGV[0].
This works fine under perl 5.6. However, it stop working
when I recently upgraded to perl 5.8.2.
It gives me the following error
Insecure dependency in exec while running with -T switch at myprog line 26
If a user in the same group of the program, it works fine, but if a user is
not
in the the group, then it prints out the Insecure message.

Any suggestions? How can I make the exec work for a user not in the group?

This is a trap I fell into as well. Between 5.6 and 5.8, taint mode
became more vigorous and now no longer allows exec'ing with tainted
data. I had this problem with MIME::Lite when it exec'd for its
best-guess of sendmail.

Taint mode is on because you're running setuid/gid and perl (quite
rightly) gets antsy about what you may be about to do as someone else.
To get around it (assuming you're 100% absolutely sure what you're about
to call), you need to run the tainted data through a capturing regular
expression and use the captured data instead, eg:

my $act = $ARGV[0];
$act =~ /^(.*)\z/s; # Dangerous RE!!!
$act = $1; # $act is now untainted
exec($act); # Who knows what this is about to do

This one is dangerous though - you still have no idea what $act is, and
using it externally could lead to people running whatever they want as
the user you're setuid to.

A better way to do it is to make the RE more exact, eg:

my $act = $ARGV[0];
if ($act !~ m{^((?:/bin/ps)|(?:/bin/ls))}) {
die "Oi! That's not a valid action!";
}
$act = $1; # $act is now untainted, and you know its
# either /bin/ps or /bin/ls
exec($act);

or make a level of indirection through a dispatch table:

my $dispatch = {ls => '/bin/ls',
ps => '/bin/ps'};
my $act = $ARGV[0]; # $act is tainted, but we don't care anymore
if (not exists $dispatch->{$act}) {
die "Oi! That's not a valid action!";
}
$act = $dispatch->{$act}; # $act is now untainted
exec($act);

The last one is arguably better, since you control what the actual
external program is, and the user just gives you a flag that never hits
the underlying operating system.

Remember that you not only have to un-taint the first argument to exec,
but all the others too!

perldoc perlsec - its a wonderful thing.
MB
 
T

Tulan W. Hu

Thanks all who helped here!!!
I added an untaint function in my prog1 and
I felt much better now.

[snip]
c program
main() {
system("/path/prog1 /path/prog2");
}

/path/prog1:
#!/usr/bin/perl
use lib '/path/mylib';
use English;
delete @ENV{qw(IFS ENV)};
$ENV{PATH} = '/bin';
#> my $cmd = shift;
my $cmd = untaint($ARGV[0]);
$EGID= $GID;
exec $cmd, 'arg1', 'arg2';
die "can't exec $cmd: $!";
sub untaint {
my ($param) = @_;
# do my checking and make sure it is expected.
$param =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C", hex ($1))/eg;
return $param;
}
/path/prog2:
#!/usr/bin/perl
print "hello $ARG[0] $ARG[1]\n";
 
J

Joe Smith

Tulan said:
Thanks all who helped here!!!
I added an untaint function in my prog1 and
I felt much better now.

Um, your untaint() function does not actually
untaint its argument.
sub untaint {
my ($param) = @_;
# do my checking and make sure it is expected.
$param =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C", hex ($1))/eg;
return $param;
}

Try running that with untaint("rm%20-rf%20.");
It's time for you to go back to the drawing board.
-Joe
 
J

James Willmore

Thanks all who helped here!!!
I added an untaint function in my prog1 and I felt much better now.

[snip]
c program
main() {
system("/path/prog1 /path/prog2");
}

/path/prog1:
#!/usr/bin/perl
use lib '/path/mylib';
use English;
delete @ENV{qw(IFS ENV)};
$ENV{PATH} = '/bin';
#> my $cmd = shift;
my $cmd = untaint($ARGV[0]);
$EGID= $GID;
exec $cmd, 'arg1', 'arg2';
die "can't exec $cmd: $!";
sub untaint {
my ($param) = @_;
# do my checking and make sure it is expected. $param =~
s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C", hex ($1))/eg; return $param;
}
/path/prog2:
#!/usr/bin/perl
print "hello $ARG[0] $ARG[1]\n";

To learn how to properly untaint values for CGI scripts to use, visit:
http://www.w3.org/Security/Faq/www-security-faq.html

And, you can also see CERT's blurb on removing metacharacters from user
supplied data at: http://www.cert.org/tech_tips/cgi_metacharacters.html

--
Jim

Copyright notice: all code written by the author in this post is
released under the GPL. http://www.gnu.org/licenses/gpl.txt
for more information.

a fortune quote ...
Even if you do learn to speak correct English, whom are you going
to speak it to? -- Clarence Darrow
 
T

Tulan W. Hu

Joe Smith said:
Try running that with untaint("rm%20-rf%20.");
It's time for you to go back to the drawing board.
Thanks for the warning.
I picked Mathew Braid's suggestion and put in the untaint function.
I'll read more about the security issues from James Willmore suggested URLs.

You guys are great and picked all the details.
 
M

Matthew Braid

Tulan said:
Thanks for the warning.
I picked Mathew Braid's suggestion and put in the untaint function.
I'll read more about the security issues from James Willmore suggested URLs.

You guys are great and picked all the details.

BTW - just noticed there is a security bug in one of my examples:

# my $act = $ARGV[0];
# if ($act !~ m{^((?:/bin/ps)|(?:/bin/ls))}) {
^^
# die "Oi! That's not a valid action!";
# }
# $act = $1; # $act is now untainted, and you know its
# # either /bin/ps or /bin/ls
# exec($act);

I forgot to anchor the end of the RE with a \z, so anything that
_starts_ with /bin/ps or /bin/ls will get though. Whoops :)

MB
 

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,968
Messages
2,570,154
Members
46,701
Latest member
XavierQ83

Latest Threads

Top