Writing

Feed Software, technology, sysadmin war stories, and more.

Tuesday, March 27, 2012

mod_php means you harm

Anyone who uses mod_php in a shared Linux environment needs to try something right now. Go write a small script in your document root. Call it omg.php or similar, and drop this into it:

<?php system("ls -la /proc/self/fd") ?>

Then load it up and watch your world end. On a stock RHEL 5 installation of Apache (2.2.3 + patches) and PHP (5.1.6 + patches), you should find one fd for every log file being used by the web server. These should be obvious, since ls will render them as symlinks from a number to a path. Notice that you can see every other site on the machine.

You'll also notice a few "pipe:[nnn]" entries. Those aren't too interesting. Some of them probably came about as part of the system() that php is running on your behalf.

Finally, there are a couple of "socket:[nnn]" entries. Some of them will be boring, but a couple of them will be spectacularly interesting. If you run them through lsof while in that shell, you will discover they are the listening sockets: port 80, port 443, and any others you might be using.

So what can we do with this information? Well, at the very least, you can hang onto one or more of those listeners. This will keep the web server from restarting during its weekly (or even daily) log rotation events in the early morning hours. This is how we used to find a great many PHP-injected bind shells, in fact: the customer's server would go down and something would have 80 and/or 443 still bound up.

What else can you do? Well, there's only so much you can do from PHP itself, but since you can drop into the full environment of your host machine, you can have some real fun. Notice that you have all of those logs open. Those logs contain the information on whatever you've been doing to the web server up to this point. It would be a pity if something came along and ... truncated them so they didn't contain any trace of your tests!

Some places try to work against this by making sure the httpd user can't find a place which is both writable and executable. They set /tmp to noexec or similar. There are a number of problems with this. First of all, you don't need to make it executable to run something.

/tmp$ cat fun.c
#include <stdio.h>
int main() {
  printf("hi\n");
  return 0;
}
/tmp$ gcc -o fun fun.c
/tmp$ chmod -x fun
/tmp$ ./fun
-bash: ./fun: Permission denied
/tmp$ /lib64/ld-linux-x86-64.so.2 ./fun
hi

Also, they tend to forget about other writable places like /dev/shm. All it takes is one.

The worst part is that none of this is new. You can go back nearly ten years (!!!) in the archives of SecurityFocus and the bug trackers of certain projects (ahem) and find people talking about this. I first stumbled across it while feeling particularly evil one day in 2006. It was what originally inspired last summer's post about troubleshooting Horde web mail on Plesk boxes.

It was after yet another one of these "oops, too many FDs" events that I decided to go poking around to see what was possible. It turns out you can do quite a bit if you really want to.

My advice? Turn off mod_php and go on with your life, then start auditing your other modules for similar signs of insanity.

Or, you know, just kick off all of your users. If you don't, the repeated exploits will do it for you.


March 28, 2012: This post has an update.