Recently we have been seeing a rash of WordPress website compromises with attackers abusing the plugin upload functionality in the wp-admin dashboard to redirect visitors and website owners to malicious websites.
The payload is the following bogus plugin located here:
./wp-content/plugins/plugs/plugs.php
At first glance these appear to be very unorthodox domains:
hxxp://xn--o1aofd[.]xn--p1ai
hxxp://xn--80ady8a[.]xn--p1ai
hxxp://xn--80adzf[.]xn--p1ai
hxxp://xn--g1aey4a[.]xn--p1ai
hxxp://xn--g1asqf[.]xn--p1ai
hxxp://xn--i1abh6c[.]xn--p1ai
However, they are using what is known as “punycode”, where everything after the xn-- is unicode. When we run these domains through our SiteCheck tool they appear even more strange:
пщшц[.]рф
увка[.]рф
мвак[.]рф
зщйс[.]рф
цзчр[.]рф
Кймщ[.]рф
The symbols “.рф” are actually the Cyrillic country code top-level domain for the Russian Federation. Referred to as “punycode” this allows for non-standard (or rather, non-English) characters to be used in domain names. Taken from Wikipedia:
“Punycode is a representation of Unicode with the limited ASCII character subset used for Internet hostnames. Using Punycode, host names containing Unicode characters are transcoded to a subset of ASCII consisting of letters, digits, and hyphens, which is called the Letter-Digit-Hyphen (LDH) subset. For example, München (German name for Munich) is encoded as Mnchen-3ya.”
This also allows for emojis to be used in domains!
This plugin author was falsely attributed to a software engineer at Amazon and linked to their LinkedIn profile page (whose name we have redacted to protect their privacy). We began to see quite a few websites compromised with this malware so we decided to dig a little deeper to find what was causing this.
Draw a Line
We were worried that there might be a plugin or theme vulnerability being actively exploited. We decided to cross-reference the plugins installed on each of the infected websites to see if we could draw a comparison between these websites. Sure enough, every single one of them had the following plugin installed:
Thinking that this plugin may contain a file upload vulnerability, a couple of other analysts and I took a look at the plugin code. Normally we’d expect to find move_uploaded_file, file_put, or file_copy functionality that attackers abuse for their own purposes. However, this plugin didn’t have any of those functions present. So why did each compromised site have this plugin?
It turns out that this plugin was not the vulnerability but the method of delivering the payload itself. Let me show you!
Logs Never Lie
We determined that in every case we found so far the last modified date on the plugs.php payload file was June 1st. This gave us somewhere to start at least! Checking through the logs of multiple websites we found some suspicious entries:
[REDACTED IP] – – [01/Jun/2021:04:35:07 +0000] “GET /wp-admin/plugin-install.php HTTP/1.0” 200 55265 “-” “python-requests/2.25.1”
[REDACTED IP] – – [01/Jun/2021:04:35:16 +0000] “POST /wp-admin/update.php?action=upload-plugin HTTP/1.0” 200 47147 “-” “python-requests/2.25.1”
[REDACTED IP] – – [01/Jun/2021:04:35:23 +0000] “GET /wp-admin/plugins.php?action=activate&plugin=plugs/plugs.php&plugin_status=all&paged=1&s=&_wpnonce=a5418a3154 HTTP/1.0” 302 676 “-” “python-requests/2.25.1”
—
[REDACTED IP] – – [31/May/2021:09:40:36 -0700] “POST /wp-login.php HTTP/1.1” 302 – “-” “python-requests/2.25.1” **2/2031482**
92.39.231.99 – – [31/May/2021:09:40:50 -0700] “GET /wp-admin/plugin-install.php HTTP/1.1” 200 55118 “-” “python-requests/2.25.1” **7/7833181**
[REDACTED IP] – – [31/May/2021:09:41:15 -0700] “GET /wp-content/uploads/2021/05/plugin.zip HTTP/1.1” 200 750 “https://www.[REDACTED].com/wp-content/uploads/2021/05/plugin.zip” “WordPress/5.7.2; https://www[REDACTED].com” **0/1646**
[REDACTED IP] – – [31/May/2021:09:41:10 -0700] “POST /wp-admin/update.php?action=upload-plugin HTTP/1.1” 200 46285 “-” “python-requests/2.25.1” **7/7677876**
[REDACTED IP] – – [31/May/2021:09:41:22 -0700] “GET /wp-admin/plugins.php?action=activate&plugin=plugs/plugs.php&plugin_status=all&paged=1&s=&_wpnonce=5a5ba92539 HTTP/1.1” 302 – “-” “python-requests/2.25.1” **5/5017388**
Note the user agent “python-requests/2.25.1”. This is very peculiar and not at all a standard user agent. Normal user agents tend to look something like this:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246
What we see here is actually a lot more simple than some sort of 0-day vulnerability getting exploited. It was just a simple wp-admin compromise.
Admin Panel Compromise
As we have said in other blog posts: A compromise of a wp-admin administration panel is a full-blown website compromise. This infection is a perfect example.
What do we see happening in these logs? It’s pretty simple, really. Once the attackers establish their foothold in the wp-admin area they install a backdoored version of the log-http-requests plugin through the “upload plugin” feature of the wp-admin dashboard. They also install their bogus “plugs.php” payload plugin which they hide with the name of “PHP Everywhere”. Once that plugin is installed the website owner and site visitors get redirected to those spam domains.
The log-http-requests plugin was identical to the one hosted on WordPress with one crucial difference: The following web shell is present.
./wp-content/plugins/log-http-requests/includes/include.php
This FilesMan webshell provides the attackers with full control of the file system. This allows them to reinfect the website if the admin passwords are changed.
This infection is about as straightforward as you can get: One backdoor file, one payload file.
Avoiding Compromise
As a WordPress website owner one of the best things that you can do to avoid falling victim to such an attack is to place some additional protections on your wp-admin panel.
By default, the only thing between the attackers and control of your website is the admin account password that is set when initially installing WordPress. If this password is easily brute forced or guessed then it’s probably only a matter of time before the panel is compromised. Remember, folks: If your password is a dictionary word followed by a number you’re gonna have a bad time.
There are quite a few additional protections that you can place on your wp-admin panel, including but not limited to:
- IP access restrictions
- Two factor authentication
- CAPTCHA
- Secondary password
- A limit to the number of failed login attempts
- Non-standard URL
- All of the above!
There are a number of different security plugins which can achieve these ends, and our website firewall can help you employ most of them.