Blog de Zscaler

Reciba en su bandeja de entrada las últimas actualizaciones del blog de Zscaler

Suscribirse
Security Research

Compromised WordPress Sites Used to Distribute the Adwind RAT

image
SUDEEP SINGH
April 29, 2020 - 16 Min de lectura

With more than 60 million websites, including 33.4 percent of the top 10 million global websites, built on the WordPress platform, it is big news when a new attack aimed at this popular tool surfaces. And, as you can probably guess, the Zscaler ThreatLabZ team recently noticed another campaign targeting WordPress sites.

Since the first week of April 2020, we observed several instances of malicious Java archive (JAR) files hosted on compromised WordPress websites. These JAR files used several layers of encryption to protect its final payload—the Adwind remote access Trojan (RAT).

In this blog, we describe two aspects of this campaign. In the first part, we describe the intelligence information we gathered from this campaign, which was used for threat attribution. In the second part, we explain in detail all the steps used for decrypting the multiple layers of encryption that were used to protect the final payload.

 

Compromised sites used for hosting the payload

We observed a common pattern shared among all the compromised websites in this campaign, which are used to host the malicious JAR payload. All these websites used the Content Management System (CMS) from WordPress. Attackers often exploit vulnerabilities in WordPress plugins to get access to the admin panel of the CMS. Once the access is obtained, they can host their payload on the server.

The WordPress version can be confirmed by checking the meta HTML tag in the source code with the “name” attribute field set to “generator” as shown below for one of the compromised sites observed in this campaign.

Wordpress version mentioned in Source Code

Figure 1: WordPress version in the HTML source code.

Most of the compromised websites in this campaign were running a fairly recent version of WordPress—5.3.x. Only a few sites were running outdated versions, such as 4.5.x or 3.3.x.

The file names for the payload varied between themes ranging from Coronavirus to payment invoices and shipping delivery services, such as DHL and USPS, as shown below:

Covid-19Update.jar

Reylontransport-covid19-statement20.jar

RescheduleUSPS.jar

DHLPaket.jar

 

Threat attribution

On some of the compromised WordPress sites used to host the malicious JAR files, we were able to find PHP web shells that attackers used to control the web server as shown in Figure 2.

PHP web shell on the server.

Figure 2: PHP web shell on a compromised WordPress site.

There are some other web shells present in the same directory. After inspecting the different web shells, we located a PHP mailer script that would send a test email to attacker-specified email addresses, as shown in Figure 3.


PHP Mailing Script

Figure 3: PHP mailing script found on the compromised server.

Email addresses:

[email protected]

[email protected]

 

Technical analysis of the encrypted JAR

There were multiple layers of encryption used in the JAR files in this campaign, which made it clear that some form of crypting service was used by the threat actor to protect the final JAR payload. After decrypting several layers, we found a reference to “Qarallax”, which leads us to believe that the cryptor used can be attributed to the Qarallax crypt service.

In this section of the blog, we will go in to the details of the different layers of encryption and how we unpacked them one by one to reveal the final payload.

For the purpose of analysis, we have chosen the JAR file with MD5 hash: 0a5f34440389ca860235434eea963465

Filename of the JAR file: Covid-19Update.jar

 

Decryption: Stage 1

This JAR file contains two encrypted resources:

Resource 1: /cloud/file.update

Resource: 2: AaxIv/WEPcXKp/UBLah/kCQuJbJn

These resources will be loaded and decrypted at runtime. To understand the decryption process, let us look at the source code of rr.class present inside this JAR file. This class file is responsible for loading the above resources and calling the decryption routines. The code section is shown in Figure 4 with relevant comments added to the code.

Stage1_Stage2_Resource_Decryption.

Figure 4: Code for decrypting the resources in stage 1.

It is also important to note that the strings referenced in the above code are defined inside the gf.class file. All these strings are encrypted as shown in Figure 5.

Stage1_Encrypted_Strings

Figure 5: Encrypted strings in stage 1.

The string decryption routine is shown in Figure 6.

Stage1_String_Decryption

Figure 6: String decryption routine in stage 1.

This string decryption routine was reused in the later stages as well. So we rewrote it in Python to make the decryption process of further layers easier. The code for string decryption is mentioned in the Appendix I section of this blog.

The different steps involved in decryption in above code are:

  1. The resource, “/cloud/file.update” is read using getClass.getResourceAsStream() into a byte array.
  2. This resource is decrypted using the AES key: “Psjduiwo8wosld9O” using the AES block cipher mode: AES/ECB/PKCS5PADDING.
  3. The result of the above decryption is an XML file, which is shown in Figure 7.

Stage1_Decrypted_XML

Figure 7: XML file obtained after decryption. It contains the AES key to decrypt the second resource.

4. This resource is loaded using the loadFromXML() method which allows individual properties from the XML to be accessed to continue the decryption process.

 

Decryption: Stage 2

The XML file, which was obtained after decrypting stage 1, is used to decrypt the second layer as defined below.

  1. The SERVER entry in this XML file corresponds to the second encrypted resource called: /AaxIv/WEPcXKp/UBLah/kCQuJbJn.Tje in the JAR file. The PASSWORD entry in the above XML file corresponds to the AES key, which will be used to decrypt this second resource.
  2. The AES key used for decrypting the second resource is: xslNGppgnJmTwGGH.
  3. Second resource is decrypted to a Gzip file, which gets decompressed to another JAR file.

 

Decryption: Stage 3

In this stage, we will look at the decrypted JAR file obtained from stage 2. The class files and resource file structure for this JAR is as shown in Figure 8.

Stage3_JAR_File

Figure 8: JAR file structure of stage 3.

This JAR file has one encrypted resource called “/this/file.grt”.

Execution of this JAR file begins in the method: j2t.ple.IL as shown in Figure 9.

Stage3_Main_Method

Figure 9: The main method in stage 3.

The strings in this method were encrypted using the same string encryption method as in stage 1. The only difference was in the initial one byte XOR key, which was changed to 0x58.

After decrypting the strings in the main method, we can see a reference to the Qarallax project. Qarallax provides crypting services for encrypting JAR files on underground hacking forums, leading us to correlate this to Qarallax.

Now let us look at the method, Il() defined in il_1.class file. This method performs the resource decryption as shown in Figure 10.

Stage3_Resource_Decryption

Figure 10: Code for performing decryption of resources in stage 3.

The different steps involved in the decryption are:

  1. It loads the encrypted resource: “/this/file.grt” using getClass.getResourceAsStream() into a byte array.
  2. It uses the DES key: R5uE7enKM8wK0qOk8s9di to decrypt the above resource.
  3. The result of decryption is an XML file as shown in Figure 11.

Stage3_Decrypted_XML

Figure 11: The XML file after decryption in stage 3.

4. The SERVER_BIN file in the above decrypted XML corresponds to the next stage encrypted file. PASSWORD_CRYPTED corresponds to an encrypted AES key, which will be used to decrypt the SERVER_BIN file. PRIVATE_PASSWORD is the RSA private key, which is used to decrypt the AES key.

5. Each of the above properties are loaded from the XML using loadFromXML() and getProperty() methods.

6. The RSA private key is stored as a serialized Java object. It is unserialized using the readObject() method.

7. The unserialized RSA key is used to decrypt the AES key defined in the PASSWORD_CRYPTED section of the XML.

8. The decrypted AES key is used to decrypt the SERVER_BIN file, which results in the next stage decrypted file.

 

Decryption: Stage 4

The decrypted payload obtained from stage 3 is the final JAR payload, which was protected with multiple layers of encryption. The JAR class file structure for this payload is as shown in Figure 12.

Stage4_JAR_File_Structure

Figure 12: The JAR file structure in stage 4.

This file contains multiple encrypted resources.

Key1.json – RSA private key stored as a serialized Java object.

Key2.json – Encrypted AES key.

Config.json – Encrypted config file of the Java RAT.

Let us look at the main method—server.main.Start()—of this JAR file. We can see the use of encrypted strings in this JAR file as shown in Figure 13.

Stage4_Encrypted_Strings

Figure 13: The encrypted strings in stage 4.

Figure 14 shows the string decryption routine.

Stage4_String_Decryption

Figure 14: The string decryption routine in stage 4.

This string decryption routine is different from the previous stages we analyzed. It is a variant of XOR decryption, which derives the decryption key in an interesting way.

The first two lines of the decryption routine are:

        StackTraceElement stackTraceElement = new LinkageError().getStackTrace()[1];

        String string = new StringBuffer(stackTraceElement.getClassName()).append(stackTraceElement.getMethodName()).toString();

These lines are used to fetch the class name and the method name from which the string decryption routine was called. To find the calling class name and method name, it generates an exception using LinkageError() and then fetches the first stack frame using getStackTrace()[1]. From this stack frame, the calling class name and method name are derived.

As an example, when the string decryption routine is called by the method "ii" in the class "Start", then the XOR decryption key will be: "Startii".

Upon further analysis, we discovered that this string decryption routine is the same as the one provided by the Java obfuscator called Allatori. Usually class files obfuscated with Allatori obfuscator use the method name: ALLATORIxDEMOxhthr().

However, in this case, the method name was also obfuscated to remove any reference to Allatori.

We rewrote the string decryption routine in Python to decrypt all the strings in this JAR file. The Python script is provided in the Appendix II section of this blog.

After decrypting the strings, the resulting code is shown in Figure 15.

Stage4_Decrypted_Strings

Figure 15: The code after decrypting the strings.

 

Decryption of the config file

As a first step, we will decrypt the resources to get access to the config file. The steps involved in decryption are:

  1. Loads the serialized object from the resource: “/server/resources/Key1.json” using getClass.getResourceAsStream().
  2. Unserializes the Java object using readObject() to get access to the RSA private key.
  3. Loads the encrypted AES key from the resource called: "/server/resources/Key2.json".
  4. Loads the encrypted config file from the resource called: “/server/resources/config.json".
  5. Decrypts the AES key using the RSA private key.
  6. Decrypts the encrypted resource using the decrypted AES key.

The resulting decrypted config file is as shown below.

{"securityRetry":20,"vbox":true,"security":[],"nickName":"quarantoes","installation":{"jarName":"aDaGm","moduleFolder":"pWnmd","moduleEntry":"tUjeninoYOKbbABjEQOfwMmkkAV/iPYMlXQvBnHKdoBEfaulmhiFQGfShHjNdiXUwSYRhLsTHMKLKkpXcgQlsdxcFcRbKfmpoSbxtVnqmjBHZYAWtNtYoYCLANenc/bOErAlOWThJDSpULmJVQtqDhaxOKlhudNtTRmXmBLSbwTosaNniBOKWGfjLMnAhIFspJ.aHpXpONmqBPEdQLuDgrAhZChTbGSTZlKJXFxqBvREcuWrMTHhhIngmMTMtkrvrWhhjpluNgrCCbfVu","uniqueIDFile":".ntusernt.ini","delay":2,"jreFolder":"Oracle","active":true,"mainFolder":"pMbbW","moduleExtension":"riY","jarExtension":"class","jarRegistry":"UKikhtn"},"vmware":true,"encryptKey":"ClonWWwVrPXQwSEIcfITKNPMg","network":[{"delay":2,"port":9932,"dns":"212.114.52.236"}]}

We provide a description of the key fields present in the above configuration file.

Vbox: Indicates whether the presence of VirtualBox should be checked or not.

nickName: This is a unique identifier used while building the RAT. In our case, it is “quarantoes”.

Installation: A JSON that contains key-value pairs describing the location where the JAR file needs to be copied to on the file system.

jreFolder: Indicates the folder where all the files required for running the JAR are stored.

jarRegistry: The name of the Windows registry key used for persistence.

delay: Indicates the number of seconds to delay the execution.

Vmware: Indicates whether the presence of VMWare should be checked or not.

Port: The port number on which the RAT communicates with the server.

DNS: IP address of the callback server.

 

Activities performed by the RAT

Below are the main activities performed by the RAT.

  1. It checks the OS name and if it is not Windows, then the RAT does not execute.

  2. It copies itself to the path: C:\Users\user\pMbbW\aDaGm.class. The directory name in this path is selected from the “mainFolder” parameter of the config file and the filename is selected using the “jarName” parameter in the config file. The file extension for the JAR file is selected as “.class” based on the configured value for parameter: “jarExtension” in the config file.
  3. It sets the Windows registry key for persistence to ensure that the above JAR file is executed automatically using javaw.exe upon reboot.

Key path: HKEY_USERS\Software\Microsoft\Windows\CurrentVersion\Run

Key name: UKikhtn

Key value: "C:\Users\user\Oracle\bin\javaw.exe" -jar "C:\Users\user\pMbbW\aDaGm.class"

The key name is fetched from the config file as well.

  1. It loads the DLL from the resource section: “/server/resources” based on the system architecture. For 32-bit system, it loads x86.dll and for 64-bit system, it loads amd64.dll.

This DLL will be loaded and copied to a temporary location on the file system with the file extension, “.xml”. The DLL is then loaded using the System.load() command as shown in Figure 16.

Stage4_Load_DLL

Figure 16: The code for loading the DLL.

5. It checks the value of the “active” key in the decrypted config.json file. If the value is set to true, then the RAT delays the execution by the “delay” number of seconds as configured in the config.json file.

6. It checks for the presence of a virtualization environment, such as VMWare, Virtualbox and Qemu. If it finds the presence of such an environment, then it exits the execution.

We will not be describing the functionality of this binary in detail in this blog since the final payload is a well-known jRAT (Java-based RAT).

 

Cloud Sandbox Detection

Figure 17 shows the Zscaler Cloud Sandbox successfully detecting this JAR-based threat.

Cloud_Sandbox_Detection

Figure 17: Zscaler Cloud Sandbox detection.

In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators at various levels, as seen here: Java.Backdoor.Adwind.

 

Conclusion

This threat actor leverages compromised websites to serve heavily encrypted variants of a Java-based RAT, which makes the detection difficult over the network.

As an extra precaution, users should not run JAR files from untrusted and unknown sources since JAR files contain executable code and have the capability of infecting a system.

Web administrators who use WordPress installations should ensure that they are running the latest version of WordPress plugins and themes to prevent any vulnerability from being exploited.

The Zscaler ThreatLabZ team will continue to monitor this campaign, as well as others, to help keep our customers safe.

 

MITRE ATT&CK TTP Mapping

TacticTechnique

Persistence

Registry Run Keys / Startup Folder - T1060

Obfuscation

Obfuscated Files or Information - T1027

Software Packing

T1045

Process Discovery

Query and kill system processes - T1057

Security Software Discovery

T1063

System Information Discovery

T1082

System Network Configuration Discovery

T1016

Windows Management Instrumentation

T1047

Uncommonly Used Port

T1065

 

Indicators of Compromise (IOC)

 

URLs hosting JAR files

 

hxxp://haus-pesjak[.]at/Covid-19Update.jar

hxxps://digitaltextile.com[.]ru/lk/Deutsche%20Telekom.jar

hxxps://digitaltextile.com[.]ru/n/DHL%20paket.jar

hxxp://haus-pesjak[.]at/04-07-20Intuitinvoices.jar

hxxp://teddyshatsworld[.]pl/Reylontransport-covid19-statement20.jar

hxxp://thaivictory.co[.]th/pageconfig/album/dir/5/order.jar

hxxp://cherryemoore[.]com/USPS/RedeliveryUSPS.jar

hxxps://feylibertad[.]org/Amazon-PO20023938.jar

hxxp://mahalowood[.]com/USPS/USPSReschedulerLabel.jar

hxxps://newsha.jsonland[.]ir/wp-includes/css/DHLPaket.jar

hxxps://www.stillval[.]com/USPS/RescheduleUSPS.jar

hxxps://thediscoveryrun[.]com/UPS/ShippingInfo.jar

hxxp://jeddahcrumbly[.]com/DHLPAKET.jar

hxxps://dev.medialogistics2020[.]ca/wp-content/plugins/ubh/Quickbooks-INV5066.jar

 

Hashes of the samples

 

7e4bdf62d3ecd78b3f407f6ec1158678

0a5f34440389ca860235434eea963465

1da18ec639f7ec2a8aad58655d846e23

d7489b47e17630e5594a320b43b201db

da52c24302a03626d2175123b751f466

b766cf6695730b74a107cb73157262b1

919f2d0043f063a90702fb36887699e8

d470d5a428f99818278fb2816a8d03e9

8f5e55fbb1bee93dc5912dcbd0092519

4a97b2d004d72b69aa64f621b5b74775

051b4da1f0079c6f60d6c8eb62b3f586

2020551b5373121053abdbf3eaafa02d

a4da22e269b93148eb9857036b9a072a

876eb4208ef2eec6e9f12b13f764a975

1d77e96974e1e2301ed78cec19e8710b

 

Network Indicators

 

212.114.52[.]236:9932

unks123.duckdns[.]org:46865

lay.dubya[.]us:8181

fresh.ygto[.]com:1010

gwiza1988.hopto[.]org:6025

praisesalways.ddns[.]net:1010

wawa.cleansite[.]us:1010

dlee889.mywire[.]org:5858

 

Appendix I

 

String decryption routine

 

#! /usr/bin/python

# -*- coding: utf-8 -*-

 

import sys

 

# Replace encoded_string with the string to be decoded.

input = <encoded_string>

# Replace one_byte_key with the respective value found in the Java class file

key = <one_byte_key>

 

l = len(input) - 1

output = []

counter = l

 

while counter >= 0:

         b1 = ord(input[l]) ^ key

         t = (l ^ key) & 0x3f

         output.append(chr(b1))

         l = l - 1

         if l < 0:

                   break

         b2 = ord(input[l]) ^ t

         key = (l ^ t) & 0x3f

         output.append(chr(b2))

         l = l - 1

         counter = l

 

output.reverse()

print(''.join(output))

 

Allatori string decryption routine ported to Python

 

#! /usr/bin/python

 

import os

import sys

 

def decode(encrypted, c_method):

         base_string = c_method

         n2 = len(encrypted)

         n3 = n2 - 1

         # Replace n4_val and n5_val with the respective values used in the Allatori obfuscator.

         # These are one byte values

         n4 = <n4_val>

         n5 = <n5_val>

         n6 = n = len(base_string) - 1

         string2 = base_string

         result = []

 

         while (n3 >= 0):

                   n7 = n3

                   n3 = n3 - 1               

                   result.append(chr(n5 ^ (ord(encrypted[n7]) ^ ord(string2[n]))))

                   if (n3 < 0): break

                   n8 = n3

                   n3 = n3 - 1

                   result.append(chr(n4 ^ (ord(encrypted[n8]) ^ ord(string2[n]))))                  

                   m = n

                   n = n - 1

                   if (m < 0):

                            n = n6;

                   n9 = n3

                  

         return result

                  

if __name__ == "__main__": 

         # Replace encrypted_string with the string to be decrypted

         encrypted_str = <encrypted_string>

         # Replace calling_class_name and calling_method_name with the names of the Class and Method from where the decryption routine was invoked

         c_method = <calling_class_name> + <calling_method_name>

        

         print ''.join(decode(encrypted_str, c_method))[::-1]

form submtited
Gracias por leer

¿Este post ha sido útil?

Reciba las últimas actualizaciones del blog de Zscaler en su bandeja de entrada

Al enviar el formulario, acepta nuestra política de privacidad.