Table of Contents
2.1. Basic static analysis
2.2. Encrypted strings
2.3. Environmental awareness
2.4. Ransom note
2.5. Blocking network communication
2.6. Service and process termination
2.7. Deleting Volume Shadow Copies
2.9. Encrypted files
EKANS malware is a ransomware which was first detected in December 2019 and while ransomware attacks are nothing new, EKANS had a functionality which made it stand out. In the list of processes, that it tries to terminate, there were some which are related to Industrial Control Systems (ICS).
During the security incidents last month, that hit the news, about the EKANS Ransomware, I decided to look at the inner workings of the malware and share my findings with the security community. My analysis was part of a research done in our ASOC team, part of TBS
We intended to publish the results earlier, but for various reasons, this blog post was delayed quite a bit. Nevertheless I think the information is still worth publishing.
The analyzed sample was obtained from the abuse.ch project, MalwareBazaar. Although the sample is publicly available, some parts of the analysis are anonymized to prevent harming victims reputation in any way.
If you’re interested, you can check the EKANS string decrypton tool I wrote:
The binary contains lots of strings referencing Go source files. The reason for this is that the EKANS malware is written in the Go programming language.
I wasn’t familiar with Go, so before proceeding with the analysis, I had to learn to program in Go, read about the specific features that the language provides and understand how they’re implemented on the assembly level.
If you’re not familiar with Go, I recommend GOing through the official tutorial A Tour of Go. I went through all exercises to get some basic understanding of the language. Then, I compiled them locally and loaded them in a disassembler in order to compare the assembly with the actual code. Several online resources and blog posts explaning low level details about the Go compiler and how to reverse Go binaries also proved really helpful.
Information about the debugging symbols in Go binaries cannot easily be stripped completely, and so the original function names can be recovered.
I used the Go Reverse Engineering Toolkit library to write a script with which to restore the symbol names.
Unfortunately, EKANS has all its non-library functions obfuscated.
Another thing that can be seen from the strings is the Go project folder, which sits under the path C:\Users\Admin3\, meaning that the username the attackers used on their development machine was Admin3.
Checking the entropy with Detect It Easy, we can make an assumtion that the binary is not packed.
With the help of redress we check that it was compiled with go1.10.8.
Almost all strings which are used by the program logic of EKANS are encrypted using a simple XOR cipher. Every string is encrypted using different key and has its own dedicated function which decrypts that string specifically. This means that there are as many string decryption functions as there are strings (over 2000).
Below is the disassembly of the string decryption function used by EKANS.
And here’s the implementation in python:
I wrote a string decryption tool which decrypts all the strings in the binary. It can also create an IDA IDC script in order to rename all decryption routines. The script is available at: https://github.com/idafchev/EKANS-String-Decryptor
One of the first things EKANS ransomware does is to lookup the IP address of a hardcoded domain name, which belongs to the victim. Unlike other strings, the domain name is stored in plaintext.
The resolved IP address of the domain is then compared to a hardcoded IP address. In this specific sample the IP address was a private address, possibly belonging to an internal host.
If the resolved address does not match the hardcoded one, the ransomware terminates without doing anything.
After that, EKANS checks if the host it’s executing on is a domain controller. To do this it queries information from the Win32_ComputerSytem Windows Management Instrumentation (WMI) class, using the WQL query “select DomainRole FROM Win32_ComputerSytem”.
According to the Microsoft documentation DomainRole property can have the following values:
0 – Standalone Workstation
1 – Member Workstation
2 – Standalone Server
3 – Member Server
4 – Backup Domain Controller
5 – Primary Domain Controller
In order to check if the host is a domain controller, EKANS compares if the value of DomainRole property is larger than 3.
If the host is a domain controller the malware does not encrypt the files. Instead it drops the ransom note and exits. If the host is not a domain controller, then it proceeds with encrypting the files and without leaving a ransom note.
It also creates a global mutex called “EKANS” in order to prevent several instances of the malware to run at the same time. If another instance is already running, the string “There can be only one”, is decrypted and execution stops. The string looks like a reference to the movie Highlander, though it might not be intentional.
The ransom note is dropped only on domain controllers. It’s written in the paths C:\Users\Public\Desktop\Decrypt-Your-Files.txt and C:\Decrypt-Your-Files.txt
Interestingly it does not contain how much ransom the attackers want.
Before proceeding further, the malware blocks all inbound and outbound network communication. In order to do this, it executes the following two commands:
netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound netsh advfirewall set allprofiles state on
Below you can see the strings which get decrypted and then concatenated in order to construct the commands.
The resulting command is then executed with os.exec.Command().Run()
This behaviour can also be seen with Process Monitor, during basic dynamic analysis:
Once all network communication is blocked, it starts searching for specific service and process names running on the host. If a match is found it tries to terminate them.
It contains an exhaustive list of services and processes. The number of services which are searched for is over 300 and the number of processes is over 1100. Only a very small subset of those are included in this blog post. Many of those are services/processes related to anti-malware software, backup and database software, log collectors and forwarders, etc. There are also some ordinary user processes in the list, like steam.exe, MS Office applications and web browsers.
Services are stopped using the WinAPI functions OpenServiceW and ControlService, while processes are terminated using OpenProcess and TerminateProcess.
Subset of the services:
Sophos File Scanner Service BackupExecAgentBrowser MSExchangeMTA MSSQLSERVER avast! Antivirus SentinelAgent Eventlog NtLmSsp AdobeARMservice MySQL80 FireEye Endpoint Agent nxlog SplunkForwarder
Subset of the processes:
firefox.exe chrome.exe excel.exe mysqld.exe steam.exe avastsvc.exe avguard.exe fortisslvpndaemon.exe nortonsecurity.exe auth8021x.exe clamscan.exe fortifw.exe msmpeng.exe
EKANS then queries WMI using the WQL query “SELECT * FROM Win32_ShadowCopy” to enumerate any existing volume shadow copies (VSC). After the VSC enumeration, it proceeds with their deletion, again using WMI.
This can be seen from the output from API Monitor during dynamic analysis.
We’re approaching the end of EKANS. The final function calls in the main function are shown below:
Before the actual encryption, strings representing file extensions, folders and files are decrypted. These are used to check which files to encrypt and which files or folder to exclude.
Some system files and folders are excluded from encryption to prevent the system from crashing and thus interrupting the encryption process.
EKANS enumerates the logical drives and then starts walking the filesystem on each drive. Each file is checked against the above-mentioned lists with extensions, filenames and folders in order to determine whether it should be encrypted.
When the encryption starts, EKANS waits for all encryption threads to finish and all files to be encrypted. After that it iterates through all encrypted files and starts renaming them.
Each file is checked if it is already encrypted by checking whether it has the “EKANS” signature at the end of the file. Files which are already encrypted are skipped.
New 16-byte Initialization Vector (IV) and 256bit key are generated for each file, so each file is encrypted using different key. The IV and key are generated using the rand.Read() function which on Windows systems uses the CryptGenRandom WinAPI function internally.
Files which are already encrypted are skipped:
Generating 16-byte IV using rand.Read():
Generating 256bit AES key using rand.Read():
The AES algorithm is used in CTR mode and the contents of the files are encrypted using the method ctr.XORKeyStream(). The contents of the files are read in chunks of 0x19000 bytes and when all data in the file is encrypted, they get overwritten with the new content.
AES in CTR mode:
Files are read in 0x19000 byte chunks:
Encrypting the buffer with ctr.XORKeyStream method:
After the file is encrypted, the AES key gets encrypted using the rsa.EncryptOAEP function. OAEP stands for Optimal Asymmetric Encryption Padding which is a padding scheme for RSA which adds a level of randomness to the algorithm.
EKANS then appends a structure to the end of the file containing the original filename, IV and encrypted AES key. The structure is in a gob encoding which is a binary go-specific encoding used for serialization. The low level details about the encoding are described in the go documentation.
The encryption of the AES key with the rsa.EncryptOAEP function:
Data is appended at the end of the file using gob:
When encryption is finished, before the ransomware process terminates it restores network communication with to following commands:
netsh advfirewall set allprofiles state off
The structure which is appended to the end of the encrypted files is shown below. At the end the “EKANS” signature is appended and before that is the size of the gob structure in little-endian format.
The coloured regions in the picture are as follows:
- The “EKANS” Signature
- Length of the gob structure in little-endian format
- The RSA encrypted AES key
- The original filename before encryption.
No privilege escalation, network communication or spreading mechanisms were found. This means that the attackers who wrote EKANS, compromise the environment manually and probably make sure they have the necessary privileges to execute the malware.
The ransom note is dropped only on domain controllers which could mean that the attackers try to compromise the whole domain before deploying the malware.
The AES keys used to encrypt the files are encrypted with the public RSA key of the attackers. Decryption is not possible without the private RSA key.
It is not known how the attackers compromise the victims initially, but it is suspected that it’s probably through Internet exposed RDP. The general recommendations when it comes to a ransomware attack are:
- Maintain offline backups for critical systems.
- Use strong passwords.
- Monitor the servers and network environment for suspicious security events.
- Update software versions and apply patches whenever possible.
- Do regular vulnerability scans.
- Disable any services used for administration (SSH, RDP, etc.) accessible from the internet. Use VPN to connect to the internal network and then connect to the intended services.