In 1999, a new virus, Win32/Crypto, was discovered. It was using brute-force attacks against its encryption key to decrypt its body. Today, in 2011, variants of Win32/Alureon are bringing this old-school technique back to life, with some extra naughtiness, as you will see below.
While working recently on different Win32/Alureon samples, we noticed some behaviour that deviated from what we’ve seen before. A particular set of files was taking longer to exhibit malicious behaviour than others. We started looking for why this was so, and ended up with a blast from the past. This time the malware was using Win32/Crypto-style decryption to elude anti-virus scanners.
The algorithm it uses is straight forward:
- Generate a random byte used in the decryption algorithm as a seed for the decryption key.
- Decrypt the input buffer with the generated key, in place.
- Hash the “decrypted” data.
- Encrypt back the buffer.
- Check the computed hash value against the correct one.
- Repeat steps 1 to 5 until hash matches.
- Decrypt the buffer with the obtained key.
- Decrypt the buffer again, but with a hardcoded magic value.
The decryption function keeps a record of all previously tried keys to avoid using the same key over and over again and so running for an exceptionally long time on a user's machine. This means that the function will try at most 255 times before successfully recovering the key. This magic value used in the last decryption step is previously retrieved from the header of the encrypted file (see structure ENCRYPTED_FILE_HEADER below at Figure 4).
Another interesting observation is that the encryption function is its own inverse; hence the same code is used for both encryption and decryption. We’ll call this function crypt_decrypt():
crypt_decrypt(crypt_decrypt(plain_text)) = plain_text, if the same key is used.
Interestingly enough, the encrypted buffer supplied as input for the decryption function is not found as a contiguous memory region but instead is scattered throughout the PE's image, being spread between code, data, resources, etc. This makes static recovery of the encrypted file more complicated.
Each chunk of data stores a set of metadata information about its data. Details below on the structure we called ALUREON_CHUNK:
Figure 1 – Alureon chuck structure
The field next_block is of interest as it is an RVA pointer to the next data chunk. Next_block is stored XOR-ed with a hardcoded value, different for each variant of the malware. In this specific case it is 0xFBC084D6, hence next_block = 0x21B4.
The ALUREON_CHUNK structure is a linked list item, which the malware will process until next_block is null. The address of the first item of the list is found in the PE header, in the IMAGE_NT_HEADERS.FileHeader.NumberOfSymbols structure member, as seen below:
Figure 2 - IMAGE_NT_HEADERS.FileHeader.NumberOfSymbols structure member
So the first chunk is at RVA 0x17C8. Here it is (the per structure member is shown in Figure 2 above):
Figure 3 – ALUREON_CHUNK at RVA 0x17C8
But we're not done yet! The data member of each ALUREON_CHUNK is encrypted using the crypt_decrypt() function we mentioned earlier. Of special importance is the data member of the first chunk because it contains additional information about the encrypted file, which is required to fully recover and execute the encrypted file. To help you understand better we've decrypted it and recovered the structure, which we named ENCRYPTED_FILE_HEADER:
Figure 4 – Data member of the first chunk
Just like in the ALUREON_CHUNK structure, the members of the ENCRYPTED_FILE_HEADER are further obfuscated, this time using the field header_key.
The ofs_inflate_code member is an address relative to the end of the allocated buffer, which contains an inflate routine. Why? Because after the brute-force decryption is successfully performed, the resulted buffer needs to be decompressed. In this case the implementation used was aPLib, but it could be anything.
After inflation is done, the relocations are applied (see rva_relocs), imports are resolved, resources are updated, and the entry-point is called.
We detect this Alureon version as Trojan:Win32/Alureon.DX. (SHA1: 3c5e639cd6637a7938e9cb59cd6045910db0810c). It’s worth noting that even if the obfuscation layer we just described is specific to Alureon, it may also be employed by other malware families. Another interesting tidbit is that an initial version of this obfuscator first arrived in our lab in the first half of 2009, using only a single layer of basic XOR encryption.
We're closely monitoring Alureon to ensure that our users are always protected. In fact, Alureon has been part of the Microsoft Malicious Software Removal Tool (MSRT) since April 2007.
A decade ago, the Win32/Crypto file infector was using a similar, though much simpler, technique. These days, malware authors go a long way in their attempts to evade detection.
We hope you enjoy reading our technical analysis!
Marian Radu & Daniel Radu