Tuesday, November 3, 2009

Checking and fixing Unix file modes

Among other tasks in my sysadmin role at a web hosting provider, I work with fallout from poorly designed PHP code - which is ubiquitous - and I use Perl 5 to perform a bunch of semi-automated tasks.

If you just want to see how I utilize the Fcntl module, look further down.

One of the many things that tend to go wrong is the assumption that PHP always runs as mod_php (blatantly disregarding php-cgi, suphp, and other per-user PHP frameworks), and therefore directories (folders) and files used by PHP "must be" prepared with chmod 777 chmod 666. The latter number is a BIG FRIGGIN' HINT, it's the number of the beast.

Whenever documentation tells you to use the number of the beast for your chmod command, that documentation is also telling you to lube up and bend over.

Unfortunately, customers and users don't necessarily see this gotcha; no matter how good the hosting provider's documentation is, they'll naturally trust the software monger's instructions.

That means that the hosting provider ought to have tools available for identifying and fixing such writable directories and files. There are many wrong ways of doing it, one is:chmod -R, since that touches ALL files, recursively, overwriting the ctime stamp. An okay way is to use find, which (in most versions) allow you to fix things up quite neatly (bash/sh compatible syntax, GNU find compatible options, $dir represents the directory to fix recursively, $user is the username whose files you want to change):
find $dir -xdev -user $uname \
\( \( -perm /og=w -exec chmod og-w {} \; \) -o \
\( -perm /g=w -exec chmod g-w {} \; \) -o \
\( -perm /o=w -exec chmod o-w {} \; \) \)
Phew, that was quite a mouthful, but it's rather nice in resource usage, and it doesn't cross filesystem boundaries (-xdev).

So why would I want to do this in Perl, you ask?

"Eeerrr. Good question, let me tell you why!"

There are many other problems to look for, which it is sensible to look for while you're at it, just to mention a few:
  • Backdoors
  • Hidden IFRAMEs
  • Malicious JavaScript
  • Malware URLs
  • Malware redirects (e.g. in .htaccess)
  • Outdated software versions
  • Root exploits
  • Spamming scripts
  • Viruses
These things belong in a program, not a teeny weeny Unix one-liner, or even a set of them.

While you're at it, you might want to create a log of what you found, and perhaps which line numbers are relevant for which files, both for pointing out where to consider fixing things, as well as having something to use for debugging your false positives.

The code

Here's how I identify the files with too liberal write permissions, utilizing the Fnctl module. The filename is stored in $_, the user's real UID in $r_uid, and I also store various file information and what kind of file we're looking at.
use Fcntl ':mode';

my %badperms;
my ($dev,$ino,$mode,$nlink,
$uid,$gid,$rdev,$size) = lstat($_);
my $g_write = $mode & S_IWGRP;
my $o_write = $mode & S_IWOTH;
my ($isfile,$isdir,$islink) = (-f _, -d _, -l _);

my $filetype = $isfile ? "File" : \
$isdir ? "Directory" : \
$islink ? "Symlink" : "Other";
my $fn = $_;

# Writable for others
if (!$islink && ($g_write || $o_write)) {
$badperms{$fn} =
sprintf ("%s: [%04o] %s\n", $filetype,
S_IMODE($mode), $fn);
In a future post, I'll try to get back with how this fits in my bigger picture of vulnerability detection.

As always, suggestions for improvements and questions are very welcome.

1 comment:

Chris said...

Hi, I have a couple jobs that I would like to advertise on your site or via an email list to inform your readers about Perl programming jobs. Please get back to me as soon as you get a chance.

Look forward to hearing from you.