taint en mode perl:préserver suid lors de l'exécution de programme externe via system()
Question
Je suis en train d'ajouter une fonctionnalité à un héritage de script.Le script est suid, et utilise perl -T (souillure mode:l'homme perlsec), pour plus de sécurité.La fonctionnalité que je dois ajouter est implémenté en Python.
Mon problème est que je ne peux pas convaincre perlsec pour préserver les permissions les permissions, peu importe combien j'ai de blanchir de l'environnement et de la ma des lignes de commande.
Ce qui est frustrant, car elle préserve la suid pour les autres fichiers binaires (comme /bin/id).Est-il un sans-papiers cas particulier pour /usr/bin/perl?Cela semble peu probable.
Est-ce quelqu'un connais un moyen de faire ce travail?(Que-est:Nous n'avons pas les ressources nécessaires pour réorganiser tout ça.)
Solution: (comme par @gbacon)
# use the -p option to bash
system('/bin/bash', '-p', '-c', '/usr/bin/id -un');
# or set real user and group ids
$< = $>;
$( = $);
system('/usr/bin/python', '-c', 'import os; os.system("/usr/bin/id -un")');
Donne les résultats souhaités!
Voici un coupe-bas version de mon script, qui montre toujours mon problème.
#!/usr/bin/perl -T
## This is an SUID script: man perlsec
%ENV = ( "PATH" => "" );
##### PERLSEC HELPERS #####
sub tainted (@) {
# Prevent errors, stringifying
local(@_, $@, $^W) = @_;
#let eval catch the DIE signal
$SIG{__DIE__} = '';
my $retval = not eval { join("",@_), kill 0; 1 };
$SIG{__DIE__} = 'myexit';
return $retval
}
sub show_taint {
foreach (@_) {
my $arg = $_; #prevent "read-only variable" nonsense
chomp $arg;
if ( tainted($arg) ) {
print "TAINT:'$arg'";
} else {
print "ok:'$arg'";
}
print ", ";
}
print "\n";
}
### END PERLSEC HELPERS ###
# Are we SUID ? man perlsec
my $uid = `/usr/bin/id --user` ;
chomp $uid;
my $reluser = "dt-pdrel";
my $reluid = `/usr/bin/id --user $reluser 2> /dev/null`;
chomp $reluid;
if ( $uid ne $reluid ) {
# what ? we are not anymore SUID ? somebody must do a chmod u+s $current_script
print STDERR "chmod 4555 $myname\n";
exit(14);
}
# comment this line if you don't want to autoflush after every print
$| = 1;
# now, we're safe, single & SUID
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# BEGIN of main code itself
print "\nENVIRON UNDER BASH:\n";
run('/bin/bash', '-c', '/bin/env');
print "\nTAINT DEMO:\n";
print "\@ARGV: ";
show_taint(@ARGV);
print "\%ENV: ";
show_taint(values %ENV);
print "`cat`: ";
show_taint(`/bin/cat /etc/host.conf`);
print "\nworks:\n";
run('/usr/bin/id', '-un');
run('/usr/bin/id -un');
print "\ndoesn't work:\n";
run('/bin/bash', '-c', '/usr/bin/id -un');
run('/bin/bash', '-c', '/bin/date >> /home/dt-pdrel/date');
run('/bin/date >> /home/dt-pdrel/date');
run('/usr/bin/python', '-c', 'import os; os.system("/usr/bin/id -un")');
run('/usr/bin/python', '-c', 'import os; os.system("/usr/bin/id -un")');
sub run {
my @cmd = @_;
print "\tCMD: '@cmd'\n";
print "\tSEC: ";
show_taint(@cmd);
print "\tOUT: ";
system @cmd ;
print "\n";
}
Et voici le résultat:
$ id -un
bukzor
$ ls -l /proj/test/test.pl
-rwsr-xr-x 1 testrel asic 1976 Jul 22 14:34 /proj/test/test.pl*
$ /proj/test/test.pl foo bar
ENVIRON UNDER BASH:
CMD: '/bin/bash -c /bin/env'
SEC: ok:'/bin/bash', ok:'-c', ok:'/bin/env',
OUT: PATH=
PWD=/proj/test2/bukzor/test_dir/
SHLVL=1
_=/bin/env
TAINT DEMO:
@ARGV: TAINT:'foo', TAINT:'bar',
%ENV: ok:'',
`cat`: TAINT:'order hosts,bind',
works:
CMD: '/usr/bin/id -un'
SEC: ok:'/usr/bin/id', ok:'-un',
OUT: testrel
CMD: '/usr/bin/id -un'
SEC: ok:'/usr/bin/id -un',
OUT: testrel
doesn't work:
CMD: '/bin/bash -c /usr/bin/id -un'
SEC: ok:'/bin/bash', ok:'-c', ok:'/usr/bin/id -un',
OUT: bukzor
CMD: '/bin/bash -c /bin/date >> /home/testrel/date'
SEC: ok:'/bin/bash', ok:'-c', ok:'/bin/date >> /home/testrel/date',
OUT: /bin/bash: /home/testrel/date: Permission denied
CMD: '/bin/date >> /home/testrel/date'
SEC: ok:'/bin/date >> /home/testrel/date',
OUT: sh: /home/testrel/date: Permission denied
CMD: '/usr/bin/python -c import os; os.system("/usr/bin/id -un")'
SEC: ok:'/usr/bin/python', ok:'-c', ok:'import os; os.system("/usr/bin/id -un")',
OUT: bukzor
CMD: '/usr/bin/python -c import os; os.system("/usr/bin/id -un")'
SEC: ok:'/usr/bin/python', ok:'-c', ok:'import os; os.system("/usr/bin/id -un")',
OUT: bukzor
La solution
Vous devez définir votre véritable nom d'utilisateur de l'effectif (suid-ed) une.Vous voulez probablement faire la même chose pour votre real id de groupe:
#! /usr/bin/perl -T
use warnings;
use strict;
$ENV{PATH} = "/bin:/usr/bin";
system "id -un";
system "/bin/bash", "-c", "id -un";
# set real user and group ids
$< = $>;
$( = $);
system "/bin/bash", "-c", "id -un";
Exemple d'exécution:
$ ls -l suid.pl -rwsr-sr-x 1 nobody nogroup 177 2010-07-22 20:33 suid.pl $ ./suid.pl nobody gbacon nobody
Ce que vous voyez est documenté bash
comportement:
-p
Tourner sur le mode privilégié.Dans ce mode, le
$BASH_ENV
et$ENV
les fichiers ne sont pas traitées, shell fonctions ne sont pas héritées de l'environnement, et de laSHELLOPTS
,BASHOPTS
,CDPATH
etGLOBIGNORE
les variables, si elles apparaissent dans l'environnement, sont ignorés.Si le shell est lancé avec l'utilisateur (le groupe) id n'est pas égale à l'utilisateur réel (groupe) id, et le-p
l'option n'est pas fournie, ces mesures sont prises et l'uid effectif est défini à l'identificateur d'utilisateur réel.Si l'-p
option est fournie au démarrage, l'id utilisateur n'est pas réinitialisé.La désactivation de cette option provoque l'efficacité de l'utilisateur et id de groupe à définir le réel de l'utilisateur et id de groupe.
Cela signifie que vous pourriez aussi
#! /usr/bin/perl -T
use warnings;
use strict;
$ENV{PATH} = "/bin:/usr/bin";
system "/bin/bash", "-p", "-c", "id -un";
pour obtenir
nobody
Rappelons que passer plusieurs arguments pour system
contourne la coquille.Un seul argument n'est aller à la coquille, mais probablement pas bash
—chercher à la sortie de perl -MConfig -le 'print $Config{sh}'
.