Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

Re: semi secure sudo script to allow restricted copy ability

by 5mi11er (Deacon)
on May 07, 2008 at 18:16 UTC ( #685289=note: print w/replies, xml ) Need Help??

in reply to semi secure sudo script to allow restricted copy ability

Well, since I won't be able to reboot some of the machines for the foreseeable future, I just went ahead and banged this thing out. Only minor testing has been done thus far, and it doesn't handle all the potential special characters that a filename could possibly have, but it does handle spaces and dollar signs.

Maybe this will help someone else someday.

#! /usr/bin/perl # ==================================================================== +===== # sudo-copy - perl script to allow semi-secure file copying ability +for # sudo. Giving pretty much any copy rights to an sudoer makes it triv +ial # for the sudoer to escalate to full root permissions. # # This script attempts to make it difficult to carry out a copy operat +ion # exploit to full root access. # ==================================================================== +===== # Task Description: # De-obfuscate the source and destination files # No recursive or directory copies # No symbolic links allowed in the source or destination # Check /etc/sudo-copy.conf file for valid target list # If all rules pass, allow the copy to happen. # ==================================================================== +===== # Written by Scott L. Miller # # Born on date: 05/08/2008 use strict; use warnings; use File::Basename; use IO::Tee; use Getopt::Std; use vars qw($opt_h $LOG); getopts('h'); sub usage { print <<ENDUSAGE; $0 [-h] <source> <destination> Allow restricted copy operations to suduoers. Why? Unrestricted copy operations make it trivial for any sudoer to escalate to full root permissions. The more restricted the copy operations are, the harder it is to exploit. Output goes both to STDOUT and /var/log/sudo-copy.log file. Options: -h -h or no parameters will print this message. ENDUSAGE exit 1; } my $argc = scalar(@ARGV); usage() if $opt_h || not $argc; die "Need one source and one destination\n" if ($argc != 2); my $logfile = "/var/log/sudo-copy.log"; $|=1; my ($starttime, $startdate) = GetTime(); $LOG = IO::Tee->new(">> $logfile", \*STDOUT) or die "Error during IO::Tee->new\n $!\n"; print $LOG "========================================================== +=======\n"; print $LOG "Started @ $startdate $starttime\n"; sub get_actual_path { my ($path) = @_; if(chdir "$path") { # CDing into a directory and then calling /bin/pwd sho +uld normalize whatever # strange input might be given to us by the user. my $actual_dir = `/bin/pwd`; chomp $actual_dir; return $actual_dir; } else { print $LOG "Can't cd into <$path>: $!\n"; print $LOG "Copy failed.\n"; die; } } my ($src, $dst) = @ARGV; my ($srcFileName, $srcPath, $srcFileExt, $dstFileName, $dstPath, $dstF +ileExt); ($srcFileName, $srcPath, $srcFileExt)=fileparse($src); ($dstFileName, $dstPath, $dstFileExt)=fileparse($dst); $srcPath = get_actual_path($srcPath); $dstPath = get_actual_path($dstPath); $src = $srcPath.'/'.$srcFileName.$srcFileExt; $dst = $dstPath.'/'.$dstFileName.$dstFileExt; if($src =~ /\$/) { $src =~ s/\$/\\\$/g; #escape the dollar signs } if($dst =~ /\$/) { $dst =~ s/\$/\\\$/g; #escape the dollar signs } foreach ($src,$dst) { lstat($_); if ( -l _ ) { print $LOG "Error: symbolic links not allowed\ +n"; print $LOG "Copy failed.\n"; die; } if ( -d _ ) { print $LOG "Error: directory copies not allowe +d\n"; print $LOG "Copy failed.\n"; die; } if ( -e _ && ! -f _ ) { print $LOG "Error: file is not a plain file\n" +; print $LOG "Copy failed.\n"; die; } } if(check_valid_destination($dst)) { my ($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($dst); dosystem("cp -f \"$src\" \"$dst\""); if($uid ne '') { dosystem("/bin/chown $uid.$gid \"$dst\""); chmod $mode, $dst; } print $LOG "Copy complete.\n"; } else { print $LOG "Copy not allowed\n"; } my ($endtime, $enddate) = GetTime(); print $LOG "Finished @ $enddate $endtime\n"; exit; sub check_valid_destination { my $fname = $_[0]; my $cfgfile = '/etc/sudo-copy.conf'; open (CFGFILE, $cfgfile) || die "Couldn't open \"$cfgfile\"\n $!\n"; while(<CFGFILE>) { s/(^\s+)//; #remove indentation if any if(/^$/) { next; } #ignore blank lines if(/^#/) { next; } #ignore comments chomp; if (eval "\'$fname\' =~ $_") { close CFGFILE; return 1; } } close CFGFILE; return 0; } sub dosystem { my $rc = system(@_) & 0xFFFF; if ($rc == 0) { return; } if ($rc == 0xff00 ) { print $LOG "Command [@_] failed: $!\n"; exit 0; } if ($rc > 0x80) { $rc >>= 8; print $LOG "Command [@_] ran with exit code $rc\n"; return; } if ($rc & 0x80) { printf $LOG "Core dumped from signal %d\n", $rc & 0x7 +F; exit 0; } else { printf $LOG "Interrupted by signal %d\n", $rc & 0x7F; exit 0; } print $LOG "Should never be able to get here\n"; } sub numerically { no warnings; $a <=> $b; } sub GetTime { my ($sec,$min,$hr,$mday,$mon,$yr,$wday,$yday,$isdst) = localti +me(time()); my $date = sprintf("%02d/%02d/%04d",$mday,$mon+1,$yr+1900); my $time = sprintf("%02d:%02d:%02d",$hr,$min,$sec); return ($date, $time); }
Example /etc/sudo-copy.conf
# A list of perl regexes of files that sudo-copy is allowed # to copy to (the destination) #test entry m#/tmp/test.*# #ntp.conf should be relatively safe m#/etc/ntp\.conf# #ldap.conf could be exploited if an attacker created a new ldap instan +ce m#/etc/ldap\.conf# #resolv.conf could be exploited in many subtle ways (new ldap instance +, yum, etc) m#/etc/resolv\.conf# #yum could be exploited if an attacker created a new yum repository m#/etc/yum\.conf# m#/etc/yum\.repos\.d/.*#

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://685289]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2017-06-26 09:55 GMT
Find Nodes?
    Voting Booth?
    How many monitors do you use while coding?

    Results (576 votes). Check out past polls.