In the end of January 2019, I received a new malware for analysis and would like to tell more details about this malware. After conducting a full analysis, it turned out that this is one of the latest versions of the SmokeBot Loader. Further in the analysis I will show how the malware checks virtual environments, how work its numerous methods of survival in the wild, that malware needs for a long lifetime on users computers.
In the title of this analysis, I described an attack on the business of Ukraine and in the report I will demonstrate specific facts, that will prove this and I hope you will agree with my opinion.
Sandbox / online checkers
Analysis of malware on analysis services (including services like virustotal.com) showed that this malware is identified by various antivirus programs as various types of malware (from ransomware to viruses), but any of antivirus product identified it 100% . Analysis in virtual machines / sandboxes showed no activity. This indicates that the malicious code has methods for detecting virtual environments and blocking its dark work in a virtual environment.
liter.exe MD5 (liter.exe) = dbba4d0f4aa3fd7ab63fd2cb3889ae57 Size = 209920 Byte
Part one - removing the envelope (cryptor)
The cryptor encrypts the main loader code in two steps. At the beginning of the cryptor there are garbage commands that repeat many times in the cycle to stop the work of emulators.
Also for loading of libraries (example kernel32.dll) dynamically generated lines are used instead of static, thereby bypass the static analysis of lines.
Further we smoothly approach the code of the first stage of decrypting the envelope.
The decryption algorithm is quite simple.
And immediately proceed to the second stage of decryption. For the second stage, the authors chose the algorithm TEA with the key 128bit. The key for decryption is static and is in clear view in the data area.
Next comes the control to the beginning of the decrypted code only. where is getting through PEB addresses of kernel32.dll and getting addresses of various APIs that are needed for further work.
Anti-debug functions are also present in the code.
Next, the memory is allocated and the executable file is unpacked into it, which is present at the end of the decrypted block earlier After unpacking, executable is copied to the liter.exe address space and the header is configured, the sections are loaded over the old ones, then the control goes to the new entry point is made - 402d03 At this stage, you can dump the executable and get a workable code without an envelope and load it into the disassembler for further study.
Part 2. jmp,jz/jnz obfuscation
We immediately get into the code, which is jmp obfuscated.
The task of deobfuscation is solved in different ways, from writing scripts to writing tracers-analyzers of the utility of instructions in a particular block. If we look at this code closely, we can understand, that we are dealing with obfuscation of unconditional jmps and a pair of conditional jumps going one after the other to the same address jz, jnz (the same unconditional jump as a result). If you can view this code under the debugger and disassembler, you will not see difficulties with understanding the code;
At the beginning of the code there is a check for the OS version using Process Environment Block (PEB)
Further, through PEB, the presence of the debugger is checked and, if it exists, then the control goes to the incorrect address
Smoke’s further work is divided into logical blocks, each block contains a prologue and an epilogue. In the prologue, the block code is decrypted, then the control goes to the decrypted part of the block and in the epilogue again its encryption and the control to the next block. This gives Smokey all the time in memory to be encrypted and decrypted as needed. That works very well against those who want to take a dump and quickly see content. Two methods are used to encrypt code and data, one is a single-byte key byte xor (004012F9) - used to encrypt / decrypt blocks of code, the other method is xor with dword key, used to decrypt data (004013CE)
Encryption / Decryption malware code with 1 byte key length
Encryption / Decryption malware code with 4 byte (dword) key length
The whole code is broken into about 20 blocks with a prolog-decryptor and an epilogue-cryptor
function ZZZ prolog - decryptor (XXXXXXX) XXXXXXXXX - epilogue - encryptor (XXXXXXX) ret
I wrote a small script that decrypts the blocks of code (XXXXX blocks) and data needed for the analysis, with the result of that you can safely analyze the code in IDA
After checking for the presence of a debugger, a control goes to the code that decrypts the first data block to the address (402d9f)
This data block contains table of dword. Each dword - hash(API function name) of various libraries, whose addresses are located through the export table of the corresponding library.
To find the Native API, Smoke, through PEB, finds the address of the ntdll.dll module in memory, then through parsing the ntdll PE header, gets to the export table, which it passes through sequentially, calculating the hashes of all export names and comparing them with the hash that is in the decrypted block data. If found, the address is stored in a table for future use.
In addition to ntdll, Smoke gets api from kernel32, user32, advapi32, shell32 by the same method, preloading them using api LdrLoadDll
Next, a checksum of a 402e6f block of 0x2c65 bytes size is checked. Checksum xor’ed with 69D99D17h (if all fine, result must be zero)
The result is added to the value of the esp register. Therefore, if the block is changed and the checksum does not match, the malware will not complete correctly after the first ret.
Next, check for the presence of hooks and patches in the native api code. Thus various antivirus and monitoring software are in effect, and if there are any, then there will be no further cases.
We got to the “delicious” topic, namely, checking the keyboard layouts installed in the system. Smokebot will work ONLY, if the Ukrainian layout is installed in the system, and if there is none, then the presence of Russian is checked. This is one of the factors in my approval of the attack on the Ukrainian business. You may reasonably notice about the Russian layout, BUT, this is not my only argument)) In any case, we understand which users in the world can be affected as much as possible by this attack. Also, such a test can discard various kinds of sandboxes in which there are no such layouts.
Having touched on the subject of sandboxes and virtual machines, we just got to their checks) Verification consists of finding a module in the system and analyzing registry branches.
Check on Sandboxie
Check registry keys for the presence of keys with the words virtio, vmware, vbox, xen
Check the checksum of the 405ad4 block with the size of 398Dh according to the scheme described above. Checksum xored with 0A8923A34h
As well as checking the process rights, if they are not less than 0x2000 (Medium), otherwise it tries to start itself in a loop through runas
Where we run x64 or x86 system? Determined by the value of the GS segment register. Under x64 this register is not zero, in x86 the value is zero. Depending on the register, this or that data block is selected, with which we will work further. These are exactly the blocks whose checksums were checked above.
The selected block is decrypted by the algorithm for decrypt data, then a block of memory is allocated and the newly decrypted block is unpacked there via api RtlDecompressBuffer
Start of Inject decompressed code to explorer.exe
Inject algorithm looks something like this
1. GetShellWindow 2. GetWindowThreadProcessId 3. NtOpenProcess 4. NtCreateSection 5. NtMapViewOfSection - explorer 6. NtMapViewOfSection - my process
The unpacked code is a trimmed and cleaned PE file, it contains part of the PE header (I’m going to restore such files later in this analysis, but this is not important now). Using this data, sections of PE are copied and configured in memory, relocs are configured and control pass directly to injection shellcode using new technic called PROPagate. Among all the сhild windows of the shellwindow, it looks for a UxSubclassInfo property. If it is, Smoke create another section in the explorer and copy to it the shellcode with callback function for SetPropA
callback function, create a thread, the start address of the thread is the entry point in the unpacked file.
Part 3. Under the wing of the explorer.exe
This part of Smoke is devoid of any obfuscations and crypters It starts with initialization, which receives the addresses ntdll and kernel32 via PEB. Decrypts the list of dll that loads and then through the hash table find out the addresses of a large number of apis bot-hash For normal analysis in statics, you need to restore all these api, so I wrote a small script that matches the api names of their hashes, thereby making the analysis comfortable.
Next Smoke creates two threads.
The first thread goes through the list of running processes, and if the hash of process name == hash from the table, then process terminate. The second thread passes through all windows and if the hash from the name of the window class == hash from table one, then the process terminate. I did not check the entire list, but ProcessHacker.exe is exactly on this list - it hash 0x5920de3d))
All text lines are located in one block which is encrypted (03DE1304h). The block looks like:
length str0 str0 length str1 str1 ....
For encryption, the authors chose the RC4 algorithm and a 4-byte key. To analyze this data, I wrote a script that decrypts it. The key 27F9051Bh was chosen for encryption.
decrypted data is as follows
After the launch of the killer-thread, the Smoke goes into a loop that checks the internet connection and if it is not, then falls asleep for a certain time and tries to repeat the check again. In case of success, it make connection with the command center. For checking the network and communication to with CC, the Smoke uses the same function.
To check the connection, Smoke tries to load the page by number 1 from the encrypted block - http://www.msftncsi.com/ncsi.txt
In case of success, Smoke forms a unique computer hash (BotId) consisting of the computer name and the serial number of the system disk.
then it is installed in the system, the new file name is generated based on the first 8 BotId symbols, which are mutated using a substitution algorithm.
Through SHGetFolderPathW, it receives the path to User startup directory and forms a string with the name of the link file for the user startup directory.
Tries to expand the path %APPDATA%\Microsoft\Windows, if it does not work, then it expand %TEMP%, creates a directory named from BotId there and copies the file from which it was launched, also under a new name and sets the hidden attribute. The file date and time is the same as file advapi32.dll. Removes the NTFS stream Zone.Identifier from the new file (which means that the file was downloaded from the Internet)
Through the COM object, IShellLink creates a prepared lnk file.
And for launches a new file, Smoke through TaskSheduller creates a task in the scheduler with name ‘Opera scheduled Autoupdate 1765637818’, with property interval = 10 minutes (PT10M),
In the process of installtion, the command center url is also decrypted and its crc32 is checked, if it has been modified, the process of system installation is interrupted.
Also Smoke blocks deletion of a new file and link file by opening them with read permissions.
Part 7. Botnet.
Smoke takes the current URL of the command center and decrypts it.
In my case, two command center addresses are used:
http: //aviatorssm.bit/ http: //anotherblock.bit/
Checks whether there is a plug-in file in the system (in the same folder as above), if it exists, then decrypt it with the rc4 algorithm and key BotId and reads the first 32 bytes - the hash of the plug-in, which is later used not to download plug-ins again
Generates a package to send to the server and sends it.
usually the packet is 63 bytes in size, if there is a plugin in the system, the packet size is increased by the size of the plug-in hash
The package structure looks like this
+0 dw 2018 (magic) +2 - ascii bot id +2b - ascii 6 byte array +31 db OS dwMajorVersion << 4 + dwMinorVersion +32 db Zero +33 db proc rights 1 < 2000 +34 dw bot command (10001 - init, 10002 - get task, 10003 - confirmation) +36 dd bot subcommand +3a dd Installed in system (1) +3e plugin hash
Let’s look at the code that is responsible for working with the network (sending and receiving)
First of all, Smoke checks which domain the command center uses. If this is a .bit domain, then to resolve ip, a list of hardcoded dns-servers will be used, which respond to requests for resolving .bit domains
Further work goes according to the following scenario. checkproxy - Checks whether the proxy server address is set in the system and if so, saves it for work winhttpopen winhttpcrackurl resolvebitdomain
If the bit is a domain, then this list of DNS servers is used for resolving
and again small script
Next, a POST-request to the server is formed with the following headers:
"Content-type": "application / x-www-form-urlencoded", "Accept": "* / *", "Referer": "http: //aviatorssm.bit/", "Host": "aviatorssm.bit"
Then the generated packet is encrypted using the rc4 algorithm with the key 4E29AB2Ch
and sent to the server
and receives data in response from the server.
A received packet consists of blocks, at the beginning of a block there is a field of 4 bytes - this is the size of block, followed by data The first block is decrypted by the rc4 algorithm and the key 693D7EBAh
the first block is text-block and carries commands from the server, settings for various plugins At the beginning of this block, word magic should be - 2018, if everything is in order, then the plugin_size string is searched and its value is received. if everything is ok, the second block is encrypted with rc4 and the BotId key and saved to a file in the AppData\Roaming\Microsoft\Windows\xxxx\xxxx (xxxx - names that were previously generated) - plugin file. Later in the report we will examine this text header in detail.
further analysis of the command received from the server Smoke supports the following commands:
i, r, u u - upgrade r - remove i - install file or task number up to 100
in my variant was task number 1. Then sequential loading of tasks takes place, the command 10002 is used for loading
which are further decrypted by the same rc4 with the key 693D7EBAh stored in appdata and started
After this, control passes to the main loop, in which the plugins processing procedure begins
Part 8. Plugins.
First of all, the plugins file is readed from the disk (where it was previously saved) and decrypted by the rc4 algorithm with BotID key
Let’s talk more about plugins block. So… The block of plugins begins with a general header of all plugins, which contains information about what size of all plugins, plugins identifier = 0xF9B91548 and the number of plugins in the block. In my case, there are 11 plugins in the block. It is worth noting that some of them do not start because they have the Disabled attribute.
Plugin Block Header +0 dd size of all plugins +4 dd magic (hardcoded in binary) +0xc db plugins count in block +0xd - First plugin in block
After the general header there are plugins that also have their own header, which contains the size of the plugin and a 15-byte key for decryption algo RC4, in order to decode the plugin code
Plugin +0x0 Plugin Header +0 dd plugin size +4 db[0xf] - rc4key for plugin decrypt +0x15 Encrypted plugin code
After the plugin is decrypted, an information block is created in the memory for the plugin, which is passed as a parameter when it is launched. This block contain BotId, the url of the command center, the rc4 key for encryption, useragent string to connect and shellcode
Plugins are launched by injecting into the process explorer.exe, which Smoke launches suspended. Then two sections are created in the process. The first one contains the information block and the shellcode for transferring control to the plugin entry point, the second one sets up the plugin code — code sections are created
then at entrypoint of explorer.exe jump to shellcode is written and the process starts
The analysis of the Smoke loader code itself can be completed and proceed to the analysis of plug-ins.
Part 9. Plugin PE-reconstruction.
After analyzing the code for injecting the plugin, I saw that the decrypted plugin in memory is an executable file, with a partially overwritten PE header with zeros. The beginning of the plugin is the offset 0x3c of a regular PE file. On this offset is the field (in DOS Header) in which the PE Header offset is located. When I began to analyze the code of the injected plug-in in the debugger, I saw that its code was packed. Of course, for analysis you can make a memory dump and work with it - analyze packaging methods, see what the plugin code does. BUT we have 11 plugins, so I decided to take the path of restoring a workable PE file, which can be analyzed without problems in a disassembler. Script for the reconstruction of all plugins will also be attached in this report.
If you, like me, have been analyzing malware for more than one year, then you will be able to determine visually what kind of packer you have in front of your eyes, with a high probability, especially if this UPX packer :) or you can also use various packer detectors.
Therefore, our task is to restore the plugin header in such a way as to run upx -d and it could recognize the packaged file and unpack it.
I will not dive into the description of the PE file structure for this there is a lot of information on the Internet. I will only touch on the fields that we need to restore.
I will recover pe file in two stages, the first stage is the creation of a minimal PE header. With such a PE header, PE-editors should open this file and understand that this is PE-File. The second stage is the recovery of data needed for the unpacker upx. which will be perceived by PE editors. The second stage is the recovery of data needed for the unpacker.
As I wrote above, the plugin code starts with DOS Header + 0x3c
Therefore, restore dos stub
stub[0:1]='MZ' stub[0x18]=0x40 stub[0x3c]=first dd from plugin
Next, go to the PE header In the plugin, only part of the fields are not overwritten with zeros, let’s consider them. We have the following fields from the plugin
Num of Sections, SizeOfUninitializedData, EntryPoint, ImageBase, Imagesize and a partial section description
image base, which in all plugins is 0x10000000, says as a rule that this is a dll file
So let’s restore these PE header fields here.
0x0 - PE sign 0x4 - CPU type - 0x14c - i386 0x14 - Size Of Optional Header 0x16 - Characteristics - 0x210e - dll with relocs 0x18 - Magic - 0x010b 0x38 - Section Alignment - 0x1000 0x3c - File Alignment - 0x200 0x54 - Header size - 0x400 0x74 - Number Of Rva And Sizes
Already after these changes, the file can be opened in the peeditor and analyzed, but this data is not enough for UPX to consider that the file is correctly packed. To determine which fields still need to be recovered, I analyzed the source code of the UPX unpacker (https://github.com/upx/upx) and determined what other data we need to recover. These are the names of the sections, which must contain ‘UPX’ and the upx header itself, which is necessary for the correct unpacking of the packed file.
# recovery upx header sign + packer version (13) 0x3e0 - 'UPX!'+chr(13)
Now the file can be unpacked and analyzed, but this no longer applies to this article. :)
Let’s go back to the title of the article and why I believe that this is a direct attack on the business of Ukraine. As I mentioned before, malware starts only on systems where the Ukrainian or Russian keyboard layout is installed. Now let’s take a look at the textual header of the plugins.
1|:|procmon_rules=tiny.exe|0?81,ifobsclient.exe|0?82,start.corp2.exe|0?83,eximclient.exe|0?84,clibankonlineua.exe|0?85,upp_4.exe|0?86,srclbclient.exe| 0?87,clibankonlineru.exe|0?88,cb193w.exe|0?89,clibank.exe|0?90,pionner.exe|0?91,mtbclient.exe|0?92,mebiusbankxp.exe|0?93,|:||:| keylog_rules=start.corp2.exe,javaw.exe,jp2launcher.exe,java.exe,node.exe,runner.exe,mtbclient.exe,ifobsclient.exe,bank.exe,cb193w.exe, clibankonlineen.exe,clibankonlineru.exe,clibankonlineua.exe,eximclient.exe,srclbclient.exe,vegaclient.exe,mebiusbankxp.exe,pionner.exe,pcbank.exe, qiwicashier.exe,tiny.exe,upp_4.exe,iexplore.exe|:||:| plugin_size=349428
If you look at it carefully, you can see the names of executable files - processes. These files are included in the software packages that Ukrainian banks use in remote service systems! If you list which banks of Ukraine use these systems, then you can see almost all banks in the country will have to list this.
The combination of these two factors gives a confident assumption of an attack on the business of Ukraine, since the systems of remote banking services use small, medium and large businesses in their work.
I also wanted to add that in attacks of this level, the real ip address of the command center is hidden behind a chain of proxy servers to which the loader is trying to connect.
At the time of this writing, the IP addresses of the proxy servers for the command center were
nslookup aviatorssm.bit 22.214.171.124 126.96.36.199 - Russia 188.8.131.52 - Russia 184.108.40.206 - Russia
These proxies change periodically, but most of them are located in Russia.
This is another indirect factor, the attacks precisely on Ukraine.
p.s. downloaded plugins file you can find on my github page