Zeppelin Ransomware Targets Healthcare, Technology Companies, And More
Zeppelin is the newest member of the Delphi-based Ransomware-as-a-Service (RaaS) family initially known as Vega or VegaLocker. Although it’s clearly based on the same code and shares most of its features with its predecessors, the campaign that it’s been part of differs significantly from campaigns involving the previous versions of this malware.
Vega samples were first discovered in the beginning of 2019, being distributed alongside other widespread financial malware as part of a malvertising operation on Yandex.Direct – a Russian online advertising network. This campaign was aimed at Russian speaking users (with apparent focus on the people working in accounting) and was designed to have a broad reach, as opposed to careful targeting. The binaries were often signed with a valid certificate and hosted on GitHub. During a course of this year, several new versions of Vega appeared, each bearing a different name (Jamper, Storm, Buran, etc.), some of them offered as a service on underground forums.
The recent campaign that utilizes the newest variant, Zeppelin, is visibly distinct. The first samples of Zeppelin – with compilation timestamps no earlier than November 6, 2019 – were discovered targeting a handful of carefully chosen tech and healthcare companies in Europe and the U.S. In a stark opposition to the Vega campaign, all Zeppelin binaries (as well as some newer Buran samples) are designed to quit if running on machines that are based in Russia and some other ex-USSR countries.
Zeppelin appears to be highly configurable and can be deployed as an EXE, DLL, or wrapped in a PowerShell loader. The samples are hosted on water-holed websites and, in the case of PowerShell, on Pastebin. There are reasons to believe at least some of the attacks were conducted through MSSPs, which would bear similarities to another recent highly targeted campaign that used a ransomware called Sodinokibi.
The major shift in targeting from Russian-speaking to Western countries, as well as differences in victim selection and malware deployment methods, suggest that this new variant of Vega ransomware ended up in the hands of different threat actors – either used by them as a service, or redeveloped from bought/stolen/leaked sources.
All sensitive strings in Zeppelin binaries are obfuscated with a different pseudo-random 32-byte RC4 key, prepended to each encrypted string:
Figure 1: Obfuscated string
The string obfuscation acts as a crude polymorphism mechanism, as each generated sample will use different RC4 keys. It also helps Zeppelin evade detection and complicates analysis.
Although the majority of samples are not packed, BlackBerry Cylance researchers have come across Zeppelin executables protected by attackers using additional polymorphic obfuscation software.
In these cases, the Zeppelin executables were wrapped in three layers of obfuscation:
- Code of varying size using a set of random APIs (often associated with benign software) and several stalling loops to deceive heuristic mechanisms and outrun sandboxes.
- First stage shellcode, encoded with simple XOR using a static 1-byte key derived from a hardcoded DWORD value. This shellcode decodes the payload binary, together with its loader, using 1-byte XOR, but this time the key is mutated for each decryption round.
- Second stage shellcode which injects the payload binary into memory and executes it:
Figure 2: Example of a stalling loop in the first layer of obfuscation
Figure 3: Payload decoding shellcode
The ransomware appears to have the following Boolean options:
|1||(none)||Run as DLL: one instance encrypting all drives and shares (as opposed to EXE); incompatible with “Startup” option.|
|2||IP Logger||Use IPLogger service (iplogger[.]ru or iplogger[.]org) to track victim’s IP address and country code.|
|3||Startup||Copy itself to another location, set persistence, launch with “-start” parameter.|
|4||Delete backups||Execute specified commands; used to stop certain services, disable recovery, delete backups and shadow copies, etc.|
|5||Task-killer||Kill specified processes.|
|6||Auto-unlock busy files||Try to unlock files that appear locked during encryption.|
|7||Melt||Before exiting, inject self-deletion thread to notepad.exe (deletes the executable, as well as all added registry values). Exit with 0xDEADFACE code.|
|8||UAC prompt||When re-running try elevating privileges (only used when “Startup” set).|
These options, along with the public RSA key and other configurable strings, can be set from the Zeppelin builder user-interface during generation of the ransomware binary:
Figure 4: Example configuration
All configurable data is stored in the .itext section of the Zeppelin binary and includes:
- Hardcoded public key (modulus and exponent separately)
- GUID (differs for each sample)
- URL address for IPLogger check-in
- Excluded folders list
- Excluded files list
- Excluded extensions list
- List of processes to kill
- List of commands to run
- Readme file name
- Readme file content
The ransomware binary can be executed with the following parameters:
|<path to an existing file>||Encrypt one file|
|<path to an existing directory>||Encrypt files in the specified directory|
|-start||Skip installation and execute the second stage of malicious code (i.e. file encryption)|
|-agent <int>||Run as an agent; encrypt files in the path specified in a value under HKCU/Software/Zeppelin/Paths key, where <int> is the name of the value (consecutive numbers starting with 0)|
|(no parameters)||Default encryption routine|
Upon initial execution (without parameters), the malware will check the victim’s country code to make sure it’s not running in one of the following countries:
- Russian Federation
Depending on the options set during the building process, it will either check the machine’s default language and default country calling code or use an online service to obtain the victim’s external IP address:
Figure 5: Checking victim’s country
The malware creates an empty file in the %TEMP% directory with the “.zeppelin” extension and a name that is a CRC32 hash of the malware path.
If the “Startup” option is set the malware will copy itself to the %APPDATA%\Roaming\Microsoft\Windows directory using a name randomly chosen from the list of active processes (ignoring any processes that were invoked with an “install” or “setup” command-line argument).
The chosen name is then encrypted with a randomly generated 32-byte RC4 key, base64 encoded (together with the prepended key) and saved to a registry value called “Process” under HKCU\Software\Zeppelin.
After setting persistence via the HKCU\Software\Microsoft\Windows\CurrentVersion\Run key in the registry, the ransomware will re-execute itself from the new path with the “-start” argument. If the “UAC prompt” option is set, it will try to run with elevated privileges.
If the “Melt” option is set, a self-deletion thread will be injected into a newly spawned notepad.exe process and the malware will exit with the code 0xDEADFACE. Otherwise, it will simply exit with code 0.
Like its predecessors, Zeppelin allows attackers to track the IP addresses and location of victims via the IPLogger web service. If the relevant option is set, the ransomware will try to check-in by sending a GET request to a hardcoded URL that was generated by using the IPLogger URL Shortener service. The User-Agent field id set to “ZEPPELIN” and the referrer field contains a unique victim ID, created during the key generation phase:
Figure 6: GET request with custom headers
To prevent a victim from checking in more than once, a “Knock” value of 0x29A (666) is written under HKCU\Software\Zeppelin. If the value already exists, the malware will not try to contact the URL on subsequent runs.
Attackers can use the IPLogger web service to view a list of victims and use the shortened URL to redirect users to other malicious content.
The encryption algorithm has not changed substantially compared to previous versions of Buran. It employs a standard combination of symmetric file encryption with randomly generated keys for each file (AES-256 in CBC mode), and asymmetric encryption used to protect the session key (using a custom RSA implementation, possibly developed in-house).
First, the malware will generate a pair of 512-bit RSA keys for the victim and save them to memory in the following format:
Figure 7: Example of encryption keys: attacker’s public key (blue), generated victim’s public key (green) & private key (red), their encrypted and base64 encode versions (yellow)
The private key from this pair will be encrypted using the attacker’s 2048-bit public RSA key hardcoded in the .itext section of the binary. Both the victim’s RSA encrypted private key and its corresponding public key will then be further obfuscated with a randomly generated 32-byte RC4 key, base64 encoded (together with the prepended RC4 key) and saved to the registry under HKCU\Software\Zeppelin\Keys as “Public Key” and “Encrypted Private Key” respectively:
Figure 8: Encryption of the victim’s private key
A unique victim ID is then created using the first 11 bytes of the victim’s RSA public key modulus and replacing the third and seventh character with a dash “-” character. An example ID for the keys shown above would be 389-04C-3D7.
Zeppelin will enumerate files on all drives and network shares to build a list of directories. Depending on the binary type, it will either use the WNetEnumResource API (if running as an EXE) or the following command (if running as a DLL):
|chcp 1250 && net view|
For each file that doesn’t match the excluded files/extensions list, the malware will perform the following actions:
1. Save the original file attributes and access times to memory and set FILE_ATTRIBUTE_ARCHIVE
2. Prepend a “666” string to the plain text file
3. Generate a random 32-byte AES symmetric key and 16-byte Initialization Vector (IV)
4. Encrypt the file using AES-256 in CBC mode (only the first 0x10000 bytes, the rest of the file content remains unencrypted)
5. Encrypt the AES key with the victim’s public RSA key and then further obfuscate it with a randomly generated 32-byte RC4 key:
Figure 9: AES key encryption
6. Prepend a hardcoded marker string to the encrypted file, together with the 8-byte length of encrypted data and 8-byte length of original data (including previously added 3-byte “666” string):
Figure 10: Encrypted file header; marker string (green) and file sizes (red), followed by encrypted content
7. Append the following information after the encrypted file content:
|4||Length of the next field|
|0x28 (40)||32-byte RC4 key followed by 8 encrypted zero bytes|
|4||Length of the next field|
|0xBB (187)||RC4 obfuscated, RSA-encrypted AES key|
|4||Length of the next field|
|0x4F4 (1268)||Victim’s private key asymmetrically encrypted with the attacker’s public key|
|4||Size of data to encrypt|
|8||Original file size|
|4||Size of all appended data|
Figure 11: Encrypted file footer