BlackBerry Cylance Threat Researchers recently discovered obfuscated malicious code embedded within WAV audio files. Each WAV file was coupled with a loader component for decoding and executing malicious content secretly woven throughout the file’s audio data. When played, some of the WAV files produced music that had no discernible quality issues or glitches. Others simply generated static (white noise).
Our analysis reveals some of the WAV files contain code associated with the XMRig Monero CPU miner. Others included Metasploit code used to establish a reverse shell. Both payloads were discovered in the same environment, suggesting a two-pronged campaign to deploy malware for financial gain and establish remote access within the victim network.
The WAV file loaders can be grouped into the following three categories, which we will discuss in detail:
- Loaders that employ Least Significant Bit (LSB) steganography to decode and execute a PE file.
- Loaders that employ a rand()-based decoding algorithm to decode and execute a PE file.
- Loaders that employ rand()-based decoding algorithm to decode and execute shellcode.
Each approach allows the attacker to execute code from an otherwise benign file format. These techniques demonstrate that executable content could theoretically be hidden within any file type, provided the attacker does not corrupt the structure and processing of the container format. Adopting this strategy introduces an additional layer of obfuscation because the underlying code is only revealed in memory, making detection more challenging.
It is worth noting that the steganography loader we discovered is also identified in Symantec's June 2019 analysis of Waterbug/Turla threat actor activity. In addition, Symantec identified WAV files containing encoded Metasploit code. These similarities may point to a relationship between the attacks, though definitive attribution is challenging because different threat actors may use similar tools. Also, our analysis focuses primarily on loaders, which are an initial stage of execution used to launch additional code. Different threat actors may use the same publicly available loader to execute unrelated second-stage malware.
Loaders
Steganography PE Loader
Overview
The first category of loaders employs steganography to extract executable content from a WAV file. Steganography is the practice of concealing a file or message within another file, ideally without raising suspicion about the target file. Attackers have used steganography techniques to hide data in the past; in fact, BlackBerry Cylance Threat Research published a report in April that describes how the OceanLotus Threat Group leveraged steganography to conceal malicious backdoor payloads within image files. In this instance, code is hidden within the audio file using the Least Significant Bit (LSB) technique, where the right-most bit of an individual byte contains the data of interest. One publicly available WAV file loader we discovered has the following characteristics:
SHA-256 | 595A54F0BBF297041CE259461AE8A12F37FB29E5180705EAFB3668B4A491CECC |
File Type | PE32+ executable (GUI) x86-64, for MS Windows |
Size | 107,008 bytes |
Compile Timestamp | Tuesday, May 29, 2018 13:07:05 UTC |
PDB Path | f:wxmrigiskanderx64releaseiskander.pdb |
Version Information | Language: U.S. English Company Name: Microsoft Corporation File Description: Microsoft MediaPlayer File Version: 12.6.7600.16385 Legal Copyright: Microsoft Corporation. All rights reserved. Original Filename: MSTCSS.EXE Product Name: Microsoft Windows Operating System Product Version: 15.3.7600.16385 |
Note that the 32-bit version of this loader is also publicly available, though it will not be discussed in detail here.
Unlike other loaders mentioned in this article, this loader contains hardcoded strings that specify the filename to load (“Song.wav”) and, once decoded, the exported function to execute (“Start”). The Song.wav file is publicly available within a zip file:
SHA-256 | DB043392816146BBE6E9F3FE669459FEA52A82A77A033C86FD5BC2F4569839C9 |
Filename | Song.wav |
File Type | RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz |
Size | 15,179,596 |
Upon execution, the loader will read Song.wav (assuming it is in the same directory), extract a DLL in memory, and execute the “Start” export. The extracted file is associated with the XMRig Monero CPU miner:
SHA-256 | A2923D838F2D301A7C4B46AC598A3F9C08358B763B1973B4B4C9A7C6ED8B6395 |
File Type | PE32+ executable (DLL) (console) x86-64, for MS Windows |
Size | 733,696 bytes |
Compile Timestamp | Monday, May 28 2018 19:45:53 GMT |
PDB Path | n/a |
Exports | Start |
Version Information | Language: Neutral Company Name: wwwxmrigcom File Description: XMRig CPU miner File Version: 2.6.2 Legal Copyright: Copyright (C) 2016-2018 xmrigcom Original Filename: xmrig.exe Product Name: XMRig Product Version: 2.6.2 |
Attackers deploy CPU miners to steal processing resources and generate revenue from mining cryptocurrency. Cryptocurrency miners are a popular malware payload since they provide financial benefit and aim to operate in the background without the user’s knowledge. An effective cryptocurrency botnet can yield thousands of dollars per month for an attacker.
Technical Details
The header of a WAV (RIFF) file is 44 bytes long, and the last four bytes indicate the size of the data section. In Song.wav, the data size is 0xE79F20 (little endian) or 15,179,552 bytes:
Figure 1: WAV file header - data size
In the code snippet below, the loader reads in these four bytes and uses the value to allocate space in memory. Then, it reads the data and closes the WAV file. Finally, the do-while loop iterates over every other byte within the first 64 bytes (i.e., it skips one byte) and extracts LSBs to determine the size of the decoded data:
Figure 2: Decode file size
To better understand the code above, recognize that the loop executes while counter < 32 and counter increments by one with each iteration. Also, data_offset represents the offset within the encoded data, and its value increments by two with each iteration, beginning with zero. In total, this loop will cover the first 64 bytes of data (32 * 2):
Figure 3: WAV file data - 64 bytes
For each byte processed, the loader will extract the LSB and assign it to appropriate bit position in decoded_size, beginning with 31(i.e., the left-most bit) and decrementing by 1 with each iteration. Applying this algorithm to the first 64 bytes of data gives us the following (skipped bytes not included):
Hex | Binary (LSB underlined) | Bit Position | LSB assigned to bit position |
FC | 11111100 | 31 | 00000000 00000000 00000000 00000000 |
E6 | 11100110 | 30 | 00000000 00000000 00000000 00000000 |
06 | 00000110 | 29 | 00000000 00000000 00000000 00000000 |
06 | 00000110 | 28 | 00000000 00000000 00000000 00000000 |
00 | 00000000 | 27 | 00000000 00000000 00000000 00000000 |
00 | 00000000 | 26 | 00000000 00000000 00000000 00000000 |
00 | 00000000 | 25 | 00000000 00000000 00000000 00000000 |
00 | 00000000 | 24 | 00000000 00000000 00000000 00000000 |
02 | 00000010 | 23 | 00000000 00000000 00000000 00000000 |
02 | 00000010 | 22 | 00000000 00000000 00000000 00000000 |
00 | 00000000 | 21 | 00000000 00000000 00000000 00000000 |
00 | 00000000 | 20 | 00000000 00000000 00000000 00000000 |
FF | 11111111 | 19 | 00000000 00001000 00000000 00000000 |
FE | 11111110 | 18 | 00000000 00001000 00000000 00000000 |
FF | 11111111 | 17 | 00000000 00001010 00000000 00000000 |
FF | 11111111 | 16 | 00000000 00001011 00000000 00000000 |
FE | 11111110 | 15 | 00000000 00001011 00000000 00000000 |
FE | 11111110 | 14 | 00000000 00001011 00000000 00000000 |
FF | 11111111 | 13 | 00000000 00001011 00100000 00000000 |
FF | 11111111 | 12 | 00000000 00001011 00110000 00000000 |
FE | 11111110 | 11 | 00000000 00001011 00110000 00000000 |
FE | 11111110 | 10 | 00000000 00001011 00110000 00000000 |
FF | 11111111 | 9 | 00000000 00001011 00110010 00000000 |
FE | 11111110 | 8 | 00000000 00001011 00110010 00000000 |
FC | 11111100 | 7 | 00000000 00001011 00110010 00000000 |
FC | 11111100 | 6 | 00000000 00001011 00110010 00000000 |
FC | 11111100 | 5 | 00000000 00001011 00110010 00000000 |
FC | 11111100 | 4 | 00000000 00001011 00110010 00000000 |
FE | 11111110 | 3 | 00000000 00001011 00110010 00000000 |
FE | 11111110 | 2 | 00000000 00001011 00110010 00000000 |
00 | 00000000 | 1 | 00000000 00001011 00110010 00000000 |
00 | 00000000 | 0 | 00000000 00001011 00110010 00000000 |
The final 32-bit binary value 00000000 00001011 00110010 00000000 has a hex representation of 0xB3200 or decimal value of 733,696. This value is placed into decoded_size.
Next, memory of size decoded_size is allocated and various calculations are performed. Default label names were modified to indicate the results of all calculations. These numbers will be used as offsets into the encoded data in the upcoming decoding loop:
Figure 4: Allocate memory and calculate offsets
Following the above code, a do-while loop begins to decode the remainder of the encoded data:
Figure 5: Decode file contents
Similar to the previous decoding loop, this one extracts the LSB of every other byte. One difference, however, is that it begins assigning bits at the lowest (right-most) bit position. Each iteration extracts the LSB from 8 bytes to form 8 bits (1 byte) of decoded data. Let’s apply this algorithm to generate two bytes of decoded data. Since 8 bits comprise a single decoded byte, and one LSB is extracted from every two encoded bytes, generating two decoded bytes requires 32 encoded bytes (2 decoded bytes * 8 LSBs per decoded byte * 2 = 32):
Figure 6: 32 bytes of encoded data
The first byte of decoded data will be produced as follows (skipped encoded bytes not included):
Hex | Binary (LSB underlined) | Bit Position | LSB assigned to bit position |
01 | 00000001 | 0 | 00000001 |
00 | 00000000 | 1 | 00000001 |
FF | 11111111 | 2 | 00000101 |
FF | 11111111 | 3 | 00001101 |
FC | 11111100 | 4 | 00001101 |
FC | 11111100 | 5 | 00001101 |
FD | 11111101 | 6 | 01001101 |
FC | 11111100 | 7 | 01001101 |
The second byte of decoded data is produced as follows (again, skipped encoded bytes not included):
Hex | Binary (LSB underlined) | Bit Position | LSB assigned to bit position |
FE | 11111110 | 0 | 00000000 |
FF | 11111111 | 1 | 00000010 |
00 | 00000000 | 2 | 00000010 |
01 | 00000001 | 3 | 00001010 |
FF | 11111111 | 4 | 00011010 |
FE | 11111110 | 5 | 00011010 |
FB | 11111011 | 6 | 01011010 |
FA | 11111010 | 7 | 01011010 |
As a result, the first two bytes have binary values of 01001101 and 01011010, respectively. Hex representations for both bytes are 0x4D5A, referring to the well-known “MZ” bytes typically found at the beginning of a Windows Portable Executable (PE) file.
The do-while loop will continue iterating until the XMRig DLL is produced in memory. Finally, the decoded DLL is mapped into memory and the “Start” export is executed to launch cryptomining activity.
Rand()-based PE Loader
Overview
The second category of loader uses a rand()-based decoding algorithm to hide shellcode. One example of this loader is publicly available and has the following characteristics:
SHA-256 | 843CD23B0D32CB3A36B545B07787AC9DA516D20DB6504F9CDFFA806D725D57F0 |
File Type | PE32+ executable (GUI) x86-64, for MS Windows |
Size | 156,672 bytes |
Compile Timestamp | Tuesday, June 06 2018 11:09:52 UTC |
PDB Path | d:sourceminingwavdllplayerx64releasewavdllplayer.pdb |
Version Information | Language: U.S. English Company Name: Microsoft Corporation File Description: Host Process for Windows Tasks File Version: 10.0.16299.15 Legal Copyright: Microsoft Corporation. All rights reserved. Original Filename: taskhostw.exe Product Name: Microsoft® Windows® Operating System Product Version: 10.0.16299.15 |
To load a WAV file with this loader the following command line must be used:
<Loader EXE> <WAV File> <Decoded PE File Entry Point>
An example of a compatible (and publicly available) WAV file has the following characteristics:
SHA-256 | 7DC620E734465E2F5AAF49B5760DF634F8EC8EEAB29B5154CC6AF2FC2C4E1F7C |
Filename | click.wav |
File Type | RIFF (little-endian) data, WAV audio, Microsoft PCM, 8 bit, mono 44100 Hz |
Size | 733,740 bytes |
Unlike the first WAV file discussed, this audio file has legitimate headers but no music when played – the audio sounds like white noise.
When executing the loader with the above WAV file, the loader will read the file, extract a DLL in memory, and attempt to execute the specified entry point. Similar to the first scenario, the extracted file is associated with the XMRig Monero CPU miner:
SHA-256 | ED58FDB450D463B0FE3BBC6B9591203F6D51BF7A8DC00F9A03978CECD57822E1 |
File Type | PE32+ executable (DLL) (console) x86-64, for MS Windows |
Size | 733,696 bytes |
Compile Timestamp | Monday, May 28 2018 19:45:53 GMT |
PDB Path | n/a |
Exports | Start |
Version Information | Language: Neutral Company Name: wwwxmrigcom File Description: XMRig CPU miner File Version: 2.6.2 Legal Copyright: Copyright (C) 2016-2018 xmrigcom Original Filename: xmrig.exe Product Name: XMRig Product Version: 2.6.2 |
In fact, this file is almost identical to the DLL decoded from Song.wav, with the exception of four bytes at the end of the file (additional details are in the next section).
Technical Details
Upon execution, this loader will read the WAV header, extract the data size, allocate memory accordingly, and store the WAV data in the newly allocated memory. Next, the loader approaches the code responsible for decoding the WAV file’s data content:
Figure 7: Rand()-based PE loader decoding loop
Note that size_of_data represents the data size extracted from the WAV header and wave_data contains the address of the encoded WAV data. The loader uses an initial call to srand() and repeated calls to rand() to extract a PE file from the WAV data. As described on Microsoft’s website, the srand() API “sets the starting point for generating a series of pseudorandom integers in the current thread”, and the rand API “retrieves the pseudorandom numbers that are generated”. Given a seed supplied to srand(), calls to rand() will return the same pseudorandom numbers each time it is called.
The do-while loop traverses each byte of the encoded data, replacing the byte with the result of subtracting rand() output from the encoded byte. For example, let’s decode the first two bytes of data shown here:
Using the loader and WAV file specified earlier in this section, the chart below shows values for the first two iterations with a srand() seed value of 0x309:
Loop Run | WAV Data (Byte) | rand() Output (lower byte) | Difference |
1 | 0x5C | 0x0F | 0x5C - 0x0F = 0x4D |
2 | 0x99 | 0x3F | 0x99 - 0x3F = 0x5A |
These first two bytes represent the “MZ” characters typically present at the beginning of a Windows executable. Once the loop runs over all data bytes, the result is a 64-bit DLL associated with the XMRig Monero CPU miner. The resulting DLL differs by only four bytes from the DLL decoded from Song.wav:
Figure 8: Decoded click.wav vs. Song.wav
While it is unclear why these bytes differ, they do not contribute to the functionality of the DLL, so the XMRig DLL files are virtually the same.
Next, the loader acquires the address of the export specified in the command line. If it exists, the loader will launch a thread to execute it:
Figure 9: Identify address of export and launch thread to execute it
Rand()-based Shellcode Loader
Overview
The third category of loader uses a rand()-based decoding algorithm to hide PE files. One example of this loader is publicly available and has the following characteristics:
SHA-256 | DA581A5507923F5B990FE5935A00931D8CD80215BF588ABEC425114025377BB1 |
File Type | PE32+ executable (GUI) x86-64, for MS Windows |
Size | 90,112 bytes |
Compile Timestamp | Monday, June 18, 2018 18:54:48 UTC |
PDB Path | D:sourceminingwavPayloadPlayerx64ReleasewavPayloadPlayer.pdb |
Version Information | Language: U.S. English Company Name: Microsoft Corporation File Description: Host Process for Windows Tasks File Version: 10.0.16299.15 (WinBuild.160101.0800) Legal Copyright: Microsoft Corporation. All rights reserved. Original Filename: taskhostw.exe Product Name: Microsoft Windows Operating System Product Version: 10.0.16299.15 |
To load a WAV file with this loader, the following command line must be used (no entry point necessary):
<Loader EXE> <WAV File>
Similar to the previous loader, any audio files paired with this loader only contained white noise with no musical content. Upon execution, this loader opens a compatible WAV file, reads its data, decodes its contents, and attempts to execute the shellcode.
While no public samples of compatible WAV files are available, both files discovered contained Metasploit code that launches a reverse shell to a specified IP address.
Technical Details
Similar to the previous loader, this one will read the WAV header, determine the data size, allocate memory, and read the data. The decoding code is virtually identical, including the use of the same srand() seed:
Figure 10: Rand()-based shellcode loader decoding loop
The only notable difference is the immediate call to CreateThread after the decoding loop completes. No supporting code is necessary to parse the file structure since this loader only handles shellcode.
An analysis of the decoded shellcode from two WAV files revealed code strikingly similar to the Metasploit reverse TCP and reverse HTTPS code. In both cases, the shellcode attempts a connection to the IP address 94.249.192.103. The reverse TCP connection occurs over port 3527 while the reverse HTTPS connection occurs over port 443.
Conclusion
Attackers are creative in their approach to executing code, including the use of multiple files of different file formats. We discovered several loaders in the wild that extract and execute malicious code from WAV audio files. Analysis revealed that the malware authors used a combination of steganography and other encoding techniques to deobfuscate and execute code. These strategies allowed attackers to conceal their executable content, making detection a challenging task. In this case, attackers employed obfuscation to both perform cryptomining activities and establish a reverse connection for command and control. The similarities between these methods and known threat actor TTPs may indicate an association or willingness to emulate adversary activity, perhaps to avoid direct attribution.
Appendix
Indicators of Compromise (IOCs):
Indicator | Type | Description |
595A54F0BBF297041CE259461AE8A12F37FB29E5180705EAFB3668B4A491CECC | SHA-256 | Steg Loader |
843CD23B0D32CB3A36B545B07787AC9DA516D20DB6504F9CDFFA806D725D57F0 | SHA-256 | PE Loader |
DA581A5507923F5B990FE5935A00931D8CD80215BF588ABEC425114025377BB1 | SHA-256 | Shellcode Loader |
DB043392816146BBE6E9F3FE669459FEA52A82A77A033C86FD5BC2F4569839C9 | SHA-256 | WAV File |
7DC620E734465E2F5AAF49B5760DF634F8EC8EEAB29B5154CC6AF2FC2C4E1F7C | SHA-256 | WAV File |
94.249.192.103 | IP address | Shellcode IP |
YARA Rule for Rand() Encoded WAV Files:
rule rand_encoded_wav { strings: $RIFF = "RIFF" $WAVE = "WAVE"$SHELLCODE = {0B 87 06 53 DF 3A} $MZ = {5C 99 13 6F F2 52}condition: $RIFF at 0 and $WAVE at 8 and ($MZ at 44 or $SHELLCODE at 44) } |
MITRE AT&CK Techniques:
Tactic | ID | Name | Description |
Defense Evasion | T1027 | Obfuscated Files or Information | Steganography is used to hide a PE file within a WAV audio file. |
Execution | T1059 | Command-Line Interface | Encoded shellcode executes Metasploit code to initiate a reverse shell. |
Command and Control | T1043 | Commonly Used Port | The reverse HTTPS connection occurs over port 443. |
Command and Control | T1065 | Uncommonly Used Port | The reverse TCP connection occurs over po 527. |
Citations
Video linkVideo link https://www.virustotal.com/gui/file
/595a54f0bbf297041ce259461ae8a12f37fb29e5180705eafb3668b4a491cecc/detection
https://www.virustotal.com/gui/file
/d0b99353cb6500bb18f6e83fe9eed9ce16e5a8d5b940181e5eafd8d82f328a59/detection
https://www.virustotal.com/gui/file
/4fd1d0671395b1b0815b0704d31af816779a6a0a516ea6f82a9196f18bf513cc/detection
https://www.virustotal.com/gui/file
/843cd23b0d32cb3a36b545b07787ac9da516d20db6504f9cdffa806d725d57f0/detection
https://www.virustotal.com/gui/file
/7dc620e734465e2f5aaf49b5760df634f8ec8eeab29b5154cc6af2fc2c4e1f7c/detection
https://www.virustotal.com/gui/file
/da581a5507923f5b990fe5935a00931d8cd80215bf588abec425114025377bb1/detection
Author Anuj Soni is a Senior Threat Researcher on the Threat Intelligence team at BlackBerry Cylance. Read more BlackBerry Cylance blogs here.