Here is a handy tool to make a collection of hard links to suid or sgid binaries and keep the
collection up to date. Use as
% mkdir suid_collection
% suid_harvester /sbin /bin /usr/sbin /usr/bin /usr/local/bin suid_collection
#!/usr/bin/perl
use warnings;
use strict;
use 5.010;
use File::Find;
use File::stat;
use Fcntl qw(:mode);
use File::Spec;
sub interesting {
my $sb = shift;
my $mode = $sb->mode;
my $suid = $mode & S_ISUID;
my $sgid = $mode & S_ISGID;
my $uid = $sb->uid;
my $gid = $sb->gid;
my $xusr = $mode & S_IXUSR;
my $xgrp = $mode & S_IXGRP;
my $xoth = $mode & S_IXOTH;
if ($suid) {
if ($xoth) {
# suid, executable by all.
return 1;
}
elsif ($xgrp) {
# suid, executable by some group.
return 1;
}
}
elsif ($sgid) {
if ($xoth) {
# sgid, executable by all.
return 1;
}
elsif ($uid != 0 and $xusr) {
# suid, executable by someone non-root.
return 1;
}
}
return 0;
}
die "usage: $0 source... dest\n"
if @ARGV < 2;
my $dest = pop @ARGV;
my @source = @ARGV;
my $dest_abs = File::Spec->rel2abs($dest);
chdir $dest or die "cannot chdir to $dest: $!";
my %already;
foreach (glob '*') {
if (/\A([0-9]+)[.].+\z/) {
my $ino = $1;
$already{$ino}++ && warn "two files in $dest beginning $ino.\n";
my $sb = lstat $_;
warn("cannot lstat $dest/$_, skipping\n"), next if not $sb;
if (not interesting $sb) {
warn "sadly, $_ no longer has useful permissions\n";
}
}
}
sub for_each_file {
my $sb = lstat $_;
warn("cannot lstat $File::Find::name, skipping\n"), return if not $sb;
my $ino = $sb->ino;
$already{$ino}++ && return;
return if not interesting $sb;
my $link_to = "$ino.$_";
say "$File::Find::name -> $link_to";
link $File::Find::name, "$dest_abs/$link_to"
or warn "cannot link to $link_to: $!\n";
}
find \&for_each_file, @source;