SPAM SPAM GO AWAY!
It seems the new trend for successfully exploited weak web applications is that they will only show their seedy content to google or other search engines.
They will only be triggered to show their content when they are accessed by GoogleBot from a Google IP (yeah they are getting that specific).
When you click and view a page that google say's is full of Viagra spam, you won't see anything, its tricky and VERY frustrating and hard to troubleshoot.
So far, the common sign i've seen of successful exploits have been:
1. .bak files (installed as wordpress plugins, you have to scour your 'active_plugins' field in the database
2. .pngg .giff .jpgg and .old files, trying to upload malicious PHP and get around unsecure uploaders
3. the use of the base64_decode PHP function, while there are legit uses for this function, it can be a sign of a baddie
4. Use of the 'eval' function in PHP. Also, legit uses are out there, but i've seen it used for the dark side of the force.
5. a 'WordPress' user in your Wordpress user table.
If you want to scan a *nix system for the file names i've found to be 'bad' use the following commands.
find -name *_old.php*
find -name *.php.jpgg
find -name *.php.giff
find -name *.php.pngg
To look for those functions i talked about your can use your friend 'grep'
grep -inrH "eval(base64_decode(" <your dir here>
grep -inrH "gzinflate(base64_decode(" <your dir here>
For anyone interested I've recently installed mod_security with their core rule sets on our Apache webserver and after tweaking the config files and creating some white-lists I have be able to ward off a number of baddies and exploit attempts.
http://www.modsecurity.org/
Its worth the hassle of setting it up. It also has a 'detection only' mode which does a great job letting you know what you have running and tweak the rules before it starts to block requests.
Managing multiple Debian linux servers
At CIAS here we manage around 20 linux machines, of which only a handful of them are in a cluster.
This means that conventional tools such as puppet and other cluster management software suits don't fit well into what we want to do.
So what am i to do? I have 20 machines to manage, secure, audit, monitor, update and any number of other tasks. Any good system administrator will have his hands knee deep in BASH and perl and <insert favorite scripting language here>. I personally happen to be a web guy. I live and breath in PHP and MySQL. My specialty over the years has been to create nice little one off web applications that parse data, manage it, and present in a useful manner to myself and my co workers. Lately i've start to go a step beyond that and create web services which my machines begin to interact with. I've created clever little command line apps that do specific tasks, and are usually generic enough that they work on all our machines. I've even created a deployment method for these apps! So now i update a single repository of our scripts and auto-magicly our servers have the latest scripts.
This system has really started to work well, and its been growing day by day.
So far i've created systems for the following solutions:
- A global iptables blacklist - add an ip to the list and all our machines block that IP
- A command to block an IP from any of our hosts, which then is put in the global list
- A script that audits SSH attacks and blocks those ips
- A web interface for all those blocked IP's (Add, Edit and Delete from the list)
- A web interface to show all available APT updates on a host, and the ability to approved updates and have them install automatically at a certain time.
- A web reporting tool that monitors all our servers disk usage and sends warning on full or near full disks
- A interface to a long term archive solution we are custom building
- A script and web interface which aggregates all of our logwatch reports, and then converts them to RSS
As more and more problems need solution i keep creating and distributing these systems. To what end?
In the future i would love to create a portal based system where I log in and i can manage and interact with all of my servers from one place. Much like an Altiris Notification server for my linux machines.
So here's my question to the great internet... Does anyone else do anything kinda like this? Is this a set of solution that I should think about packaging up and creating an open source project for? Does anyone care? Does anyone else have a better solution than the hacks i've been working on? I want to hear your feed back!!!
And now for something completely random:
Force SSL connection using PHP
When place before all other output on your PHP script this little snipped will force you PHP page to be presented over SSLThis is very useful on things like Login forms.
if($_SERVER['SERVER_PORT'] != '443') {
//Force SSL upon this page
header("Location: https://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);
}
Logwatch & RSS – A Perfect Union
Here at CIAS we have a bunch of linux servers, and we run logwatch on them to give us a daily look at our servers.
There are few options with logwatch and how you want to get the information, you can have it email you, which for one or two servers that is pretty standard, but we are managing 10+ servers, and i don't want to clog up my email and i don't want to use my inbox as a way to archive all of our logwatch reports.
So what did I come up with?
On The Server
Everything that happens here is using a simple upload script i created to facilitate the aggregation of the scripts. Machine's logwatch runs as a cron scripts and outputs its report to a directory on the machine. Right after that script runs another scripts run and copies the logwatch report to a central web server.
On the Web Server
The 'logwatch' app is just a nice simple front end to a bunch of directories from this structure
archive
server1
12-3-07
logwatch.txt
12-2-07
logwatch.txt
server2
12-3-07
logwatch.txt
I created a nifty little PHP class that takes care of this structure, and allows easy access to the files through code.
RSS and <insert favorite news reader here>
Cool, so now i have a nifty little web app that organizes our logwatch reports... so whats the next step?
I already run NetNewsWire on my Mac and RSS would be a perfect solution for seeing updated logwatch reports as they come in from the servers. So create the RSS is was pretty easy...
1. use the PHP class
2. output in RSS format the last 5 logwatch reports for the specified server
3. RSS MONEY!!
I'm pretty happy with this elegant solution. I always have a hard time remembering to 'go' to a web app... I always keep NetNewsWire open. So this solution makes me a better System Administrator
Securing your PHP Upload scripts
Security should always be on the mind of any developer of web technologies, but sadly in todays rush toward rapid application development and fast approaching deadlines, security takes a backseat sometimes. Here at CIAS we need to walk the thin line between a locked down and secure web environment, and an open and available environment which allows Students, Faculty and Staff the ability to freely explore all aspects of web programming.
We have had a few incidents involving unsecured uploads scripts on the cias.rit.edu student web server, so we need to start taking a harder look at security. These incidents have involved both spam, and hacking attempts.
There are two ways I can look at the security of the cias.rit.edu web applications.
- I can lock down apache and php and make it nearly impossible for hacking attempts, at the expense of your ability to create flexible web applications.
- I allow the responsibility to come down to the individual account owners to secure their applications and keep security of the server as their primary priority.
I would much rather latter option, the first option creates an environment that is very much limiting and locked down.
With that said, what can you as a developer do to help secure our environment in your file upload scripts??
Here are some simple steps you can take in your scripts:
- Check the referrer of the form submission: make sure the information being sent from your script actually come from your script, not being faked from an outside source
- Restrict file types: This is the most important step you can do to secure your file upload scripts. if you are only expecting jpg images, make sure the file being uploaded has the .jpg and/or .jpeg extension. Note to watch out for double extensions (i.e. image.jpg.php )
- Rename the file being uploaded: If the image comes in as MyCutePuppy.jpg, rename it to something like timestamp_MyCutePuppy.jpg, or even better yet, if you are keeping your images in a database, you can easily track the names of your files if you rename them to something completely random, such as a timestamp or md5 checksum
- Permissions: 777 is not a good idea for your unless you REALLY need it. The best idea is at least 644 ( Owner can read/write/, everyone else can only read) This is a perfect for image uploads, if you are doing things in your web app such as deleting images, you may need to loosen up your permissions, but make sure you don't just blanket 777 everything, as this 'is a bad idea'
- You will need your upload directory to be 777, but all the files in it should be 644
- Make your users login, or at least give some information that you can use to track them down. Actions like this will deter users (or bots) from doing 'bad' things
Ok, so with these nice little tips, how can we create a nice easy php upload script that won't allow the server to be owned?
Well, to understand that, I think we need to understand how an unsecured upload script can be exploited, here is a great example, and exactly how we've been attacked before.
- Hacker finds an unsecured upload script
- Hacker uploads a .php file
- Hacker then finds his .php that was uploaded to the server, not renamed and with 777 permissions. By just visiting this PHP script in his web browser the attack has started
- That PHP scripts then proceeds to download from the internet binaries and other php scripts that can be used to gain a command line on the machine
- The hacker now has the run of the server as the user www-data who runs the apache server. This is a bad situation, this has now put every account on the web server at risk, because the www-data user has permissions to everyone's directory.
- The hacker at this point can either launch attacks on other servers, using our good name as a proxy, or can continue to exploit our server and install software that gives the hacker a backdoor.
As you can see, this 'is a bad thing'
So how do we stop this from happening?!
The following script gives you everything you need! (Read the comments to understand what everything does)
The sample script implements many of the points i talked about earlier in this post, ejoy
You can try out the script, and see the source at: http://bjcpgd.cias.rit.edu/upload.php
[php]
CIAS Secure PHP File Upload Example
#This is an array which you can manage to determine which file types you want to allow to upload
$allowedFileTypes = array("jpg","png","bmp","gif","pdf");
#This is where you want to upload, this directory much be writable by the webserver (777 on cias.rit.edu)
$uploaddir = 'sample_upload_directory/';
#This is a statement that will kill your script if your upload directory doesn't exist and isn't writeable by the webserver
if(!is_dir($uploaddir) && !is_writable($uploaddir)) {
echo "Your upload directory has not been created or setup properly!!";
die();
}
#We define and rename the file here, I've simply tacked on a timestamp, but you can get creative if you want
$uploadfile = $uploaddir . time()."_".basename($_FILES['userfile']['name']);
#We now get some information about the file, so we can check its extension
$uploadfileinfo = pathinfo($_FILES['userfile']['name']);
#In this line we are making sure that our upload script is being run from... our upload script,
#You can replace $_SERVER['SCRIPT_URI'] with the name of the URL you expect your script to be run from. (ie a sperate upload.html file)
if($_SERVER['SCRIPT_URI'] == $_SERVER['HTTP_REFERER']) {
#Lets make sure our POST variable exists and that the form has been submitted
if(isset($_POST)) {
#This checks if our uploaded file has an extension that we set up in the $allowedFileTypes array above
if(in_array($uploadfileinfo['extension'],$allowedFileTypes) ) {
#Ok, this statement actually moves the uploaded from the tmp directory to our final destination
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
chmod($uploadfile, 0644);
#Ok, everything has been uploaded just fine, this is where you would update your MySQL database,
#or any other manipulation you need to do
echo "File is valid, and was successfully uploaded.
\n";
echo "View file: here";
echo "
";
print_r($uploadfileinfo);
echo "
";
} else {
#This else statement really just means, something happened in your upload and you got an error
echo "Possible file upload attack!\n";
}
} else {
#This is where you would handle your 'not allowed' extentions.
echo "The file: ". $uploadfileinfo['basename'] ." is not an allowed file type";
}
}
}
?>
[/php]
PHP – RIT LDAP Authentication function
This is a little bit of code i've written over the years here at RIT to connect to the rit ldap server, authenticate a username, password and then return some information about them in a session if you want (commented out right now). Go ahead and put it in your code, and call it when you are doing things like login pages.
Return 0 = Wrong username or password
Return 1 = Username and password are good!
Update: updated the scripts per Mario's suggestions
[php]function authenticate($username,$password) {
########################
## RIT LDAP Authentication Function
## Bradley Coudriet - bjcpgd@rit.edu ########################
$server="ldap.rit.edu"; //RIT LDAP Server
$basedn="ou=people,dc=rit,dc=edu";
//Base DN $script=$_SERVER['SCRIPT_NAME'];
$filter = "(uid=$username)";$dn = "uid=$username, ";
if (!($connect = ldap_connect($server))) { return 0; }
//The LDAP functions will always return an error if the username and password are not correct, this line disables the error messages
if ( !(@ldap_bind($connect, "$dn" . $basedn, $password)) || empty($password) ) {
$error = "You either have a wrong username or wrong password";
return 0;
}
$sr = ldap_search($connect, $basedn,"$filter");
$info = ldap_get_entries($connect, $sr);
/* Uncomment these lines if you are using sessions and want to put some of the information you got LDAP in your session
$_SESSION['accountUserName'] = $username;
$_SESSION['accountFirstName'] = $info[0]['givenname'][0];
$_SESSION['accountLastName'] = $info[0]['sn'][0];
$_SESSION['accountPhone'] = $info[0]['telephonenumber'][0];
$_SESSION['accountEmail'] = $info[0]['mail'][0];
$_SESSION['accountType'] = $info[0]['riteduaccounttype'][0]; */
return 1;
}[/php]
