Introduction
Over the last few months, several cyber gangs (BlackCat, Hive, Revil, etc.) have built Linux versions of their ransomware, specifically targeting the VMware ESXi. The reason is that a single command could encrypt all the data contained on the virtual machines. In autumn 2021, the Avoslocker operators announced their new Linux variant of AvosLocker. The sample has been publicly available since January 2022.
This article is a detailed analysis of the Avoslinux piece of ransomware. The main objectives were to show the differences with the Windows variant, to understand the encryption mechanisms and to see if any anti-reverse engineering techniques were used.
ELF Analysis
The analyzed sample was found on the public platform MalwareBazaar and its sha256sum is 10ab76cd6d6b50d26fde5fe54e8d80fceeb744de8dbafddff470939fac6a98c4
. Based on the ELF header, it was compiled with GCC 4.4.7.
$ readelf -p .comment 10ab76cd6d6b50d26fde5fe54e8d80fceeb744de8dbafddff470939fac6a98c4.elf
String dump of section '.comment':
[ 0] GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)
Obviously, the binary is stripped and does not contain any symbols:
$ nm 10ab76cd6d6b50d26fde5fe54e8d80fceeb744de8dbafddff470939fac6a98c4.elf
nm: 10ab76cd6d6b50d26fde5fe54e8d80fceeb744de8dbafddff470939fac6a98c4.elf: no symbols
The ELF header also contains the sections .ctors
and .dtors
. The .ctors
section contains a list of functions ran before the main function to initialize dynamic non-local variables.
$ readelf -S ./10ab76cd6d6b50d26fde5fe54e8d80fceeb744de8dbafddff470939fac6a98c4.elf
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400200 00000200
000000000000001c 0000000000000000 A 0 0 1
....
snip
....
[19] .ctors PROGBITS 0000000000757000 00157000
00000000000000a0 0000000000000000 WA 0 0 8
[20] .dtors PROGBITS 00000000007570a0 001570a0
0000000000000010 0000000000000000 WA 0 0 8
The last constructor function called initializes three strings, the ransom notes, the sample ID, and base64 strings:
The decoded base64 strings are 88-byte long and at first sight I couldn't figure out what it was.
$echo -en "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9U+h7UA0Do9mVDFVJM9Gj5Qi/5zn2b/5dH9qFMApEmVngoc4zlLk49U1iWc2l+in2CtyQb+/s+JKvyPvack9gw==" | base64 -d | xxd
00000000: 3056 3010 0607 2a86 48ce 3d02 0106 052b 0V0...*.H.=....+
00000010: 8104 000a 0342 0004 f54f a1ed 4034 0e8f .....B...O..@4..
00000020: 6654 3155 24cf 468f 9422 ff9c e7d9 bff9 fT1U$.F.."......
00000030: 747f 6a14 c029 1265 6782 8738 ce52 e4e3 t.j..).eg..8.R..
00000040: d535 8967 3697 e8a7 d82b 7241 bfbf b3e2 .5.g6....+rA....
00000050: 4abf 23ef 69c9 3d83 J.#.i.=.
Then, by digging further in the binary, it appears to be an elliptic curve public key generated using the secp256k1 curve.
$echo -en "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9U+h7UA0Do9mVDFVJM9Gj5Qi/5zn2b/5dH9qFMApEmVngoc4zlLk49U1iWc2l+in2CtyQb+/s+JKvyPvack9gw==" | base64 -d | openssl asn1parse -inform DER -dump
0:d=0 hl=2 l= 86 cons: SEQUENCE
2:d=1 hl=2 l= 16 cons: SEQUENCE
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
13:d=2 hl=2 l= 5 prim: OBJECT :secp256k1
20:d=1 hl=2 l= 66 prim: BIT STRING
0000 - 00 04 f5 4f a1 ed 40 34-0e 8f 66 54 31 55 24 cf ...O..@4..fT1U$.
0010 - 46 8f 94 22 ff 9c e7 d9-bf f9 74 7f 6a 14 c0 29 F.."......t.j..)
0020 - 12 65 67 82 87 38 ce 52-e4 e3 d5 35 89 67 36 97 .eg..8.R...5.g6.
0030 - e8 a7 d8 2b 72 41 bf bf-b3 e2 4a bf 23 ef 69 c9 ...+rA....J.#.i.
0040 - 3d 83 =.
Finally, three objects are initialized, two of them will hold a public and private key, and one is for the random generator. These objects come from the crypto++ library.
Main function
No technique has been set up to obfuscate and protect the ransomware. The ransomware is basic and accepts two parameters, the number of threads to be used and the directories to encrypt:
If one of the given paths contains the strings "esxi" or "vmfs", a global variable is set to true and the running VMs (virtual machines) are killed using the esxcli command line:
Finally, it will browse the given lists of directories recursively, load the attackers' public key and build a list of files that the encryption thread routine will consume.
Generating the list of files to encrypt
The function that builds the list of files to encrypt is simple. First, it calls "opendir" with the directory path name to encrypt, and then, using "readdir", it iterates through the files in the directory. If it is a regular file and the name is not "README_FOR_RESTORE" or it does not end with the ".avoslinux" or ".avos2" extension, it is added to the global list. If the esxi global variable is set to true, only files that end with ".vmdk", ".vmem", ".vswp", ".vmsn" or ".log" are added to the list:
Unlike most Windows pieces of ransomware, that only encrypt data files based on their extension name using a whitelist or a blacklist, this Linux variant may encrypt all the files, including system files.
Load the attackers' public key
Because the ransomware uses the crypto++ library, we recognize the particular concept of filters and pipes used by the library in the reversed code. Similarly to Unix, Pipes allows data flows from a source to a sink and filters them to transform them. The original function that decodes and loads the base64 attackers' public key would probably look like this:
AutoSeededRandomPool prng;
string encoded = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9U+h7UA0Do9mVDFVJM9Gj5Qi/5zn2b/5dH9qFMApEmVngoc4zlLk49U1iWc2l+in2CtyQb+/+JKvyPvack9gw==";
string decoded;
StringSource ss(encoded, true, new Base64Decoder( new StringSink(decoded)));
ECIES<ECP>::Encryptor e0;
e0.AccessPublicKey().Load(decoded);
e0.GetPublicKey().ThrowIfInvalid(prng, 3); // Validate the public key
Encryption
To encrypt files on the disk, Avoslinux uses the Salsa20 stream ciphers using the 12-round variant. For each file to encrypt, it generates a 32-byte long Salsa key and an 8-byte long nonce.
The generated key and nonce are passed to the function "ECIES_n_b64" to be encrypted using the ECIES (Elliptic Curve Integrated Encryption Scheme) crypto scheme, and then base64-encoded.
The function would probably look like this:
string key_nonce;
StringSource ss1 (key_nonce, true, new PK_EncryptorFilter(prng, e0, new Base64Encoder( new StringSink(b64_ecies_key_nonce))));
The ECIES-encrypted output is bigger than the original: 125-byte long. Based on the crypto++ ECIES documentation, "The output of the encryption function is the tuple {K,C,T}, where K is the encrypted common secret, C is the ciphertext, and T is the authentication tag."
The number of Salsa rounds is set:
The file is encrypted using the Salsa20/12 algorithm, and the key with the previously encrypted nonce (ECIES and base64) is appended to the end of the file.
Then, the file is renamed by appending the ".avoslinux" extension to the file.
Finally, the Salsa key and the nonce are erased from the memory:
Conclusion
The Linux variant is very simple and has no special features like network encryption or any anti-reverse techniques to obfuscate codes. The encryption process is not common for a piece of ransomware and it is different from the Windows variant, which uses the RSA and AES combination. Another thing to note is that unlike most Windows pieces of ransomware, that only encrypt data files based on their extension name using a whitelist or a blacklist, this Linux variant may encrypt all the files, including system files.
IOCs
Sample hash
- SHA256: 10ab76cd6d6b50d26fde5fe54e8d80fceeb744de8dbafddff470939fac6a98c4
- SHA1: 9c8f5c136590a08a3103ba3e988073cfd5779519
- MD5: f659d1d15d2e0f3bd87379f8e88c6b42
Elliptic curve public key
- MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9U+h7UA0Do9mVDFVJM9Gj5Qi/5zn2b/5dH9qFMApEmVngoc4zlLk49U1iWc2l+in2CtyQb+/s+JKvyPvack9gw==