The study of one malicious

image

I came across a recently malicious doc file that was sent with phishing emails. I decided that this is a good reason to practice reverse engineering and write something new on Habr. Under the cat - an example of parsing a malicious macro dropper and no less malicious dlls.

Macro


Sha256 from the file - abb052d9b0660f90bcf5fc394db0e16d6edd29f41224f8053ed90a4b8ef1b519 . In the doc file itself, on the first page there is a picture informing that this file is protected and explaining how to enable macros; there are two large tables with numbers in the file. Numbers are written in decimal form, the longest are ten-digit, there are both positive and negative.

When the victim allows the execution of macros (there are those who have it enabled by default?), A chain of actions is launched, which ultimately performs the updatedb_network function, which changes the current directory to a temporary one and creates the file "icutils.dll" in it, in which it writes down the numbers from the first table in a row as 32 bit integer with a sign. The result is the correct dll. The clone function is imported from this dll :

Declare PtrSafe Function clone Lib "icutils.dll" _
(ByVal Saved As String, ByVal Time As Integer) As Boolean

And it starts with two parameters:

R1 = Module1.clone("Cream", 0)

If the clone call returns False, the icutils.dll file is overwritten with data from the second table and clone is called again with the same parameters.

Looking ahead, I’ll say that the first dll is 64 bit and will not run on 32 bit systems. Thus, the macro selects the correct binary code architecture.

Interestingly, the updatedb_network function has a piece of code that has no functional purpose:

Sub updatedb_network()
...
Dim query_to_change As Variant
    Set query_to_change = CurrentDb.QueryDefs("query_name")
    
    query_to_change.SQL = "SELECT * FROM Table ORDER BY ID Asc"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table ORDER BY ID Asc"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE Fashion"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE '" & something & "'"
...
End Sub

Perhaps he is here to give the appearance of useful work for those who quickly scroll through the code, see some lines in SQL and think that everything is OK? I do not know. Also, most functions and variables have random or non-real-name names (such as updatedb_network , which does not interact with the database or network). Although there is, for example, the dump_payload function , which stores 4 bytes in icutil.dll. But in any case, the presence of the Document_Open function should immediately alert , authors of the malware cannot arbitrarily rename it (though they can use another automatically launched function instead).

So, the macro functionality is more or less clear, it's time to unload the dll and proceed to their analysis.

First dll


The first dll (sha256 7427cc4b6b659b89552bf727aed332043f4551ca2ee2126cca75fbe1ab8bf114 ) 64 bit.

The list of imported functions contains the functions CreateProcessW (program start), CreateRemoteThread (creating a thread in another process), VirtualAllocEx (allocating a block of memory in another process), WriteProcessMemory (writing to the memory of another process), which immediately suggests the injection of code into another process. Now let's see what exactly she does using IDA Free and Ghidra.
The main entry point simply returns 1, does nothing else. The second exported function is clone, which is called by the macro and contains malicious code.
The parameters with which it is called do not seem to affect anything. The application decrypts two data blocks. The first 0x78 data block with the following contents (already decrypted):

https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\xf2i\xe0\x1d\x95h\xbc\x03\xe4#\xe0\x1d<\x04\xe0\x1d\xe6\x00\xde\x01\xa4\x17\xbc\x03x\x01\xe0\x1d\xe2\x16x\x07Qy\xbc\x03@Fx\x07Df\xbc\x03\x89a\xde\x01q\x11\xe0\x1d|Ix\x07D@\xbc\x03\x8a\x01\xde\x01^9\xde\x01\xf2i\xe0\x1d\x95h\xbc\x03\xe4#\xe0\x1d\xab

The second data block is 0x1D4C long and contains executable code.

In addition, a pointer to the kernel32 module, the result of the GetTickCount () function, and the address of the ZwDelayExecution () function from kernel32 are written to the 0x90 byte structure .

Then the process ( CreateProcessW ) “cmd.exe” is created. Using VirtualAllocEx , two buffers are allocated in it: with RW permissions with a length of 0x108 and with RWX permissions with a length of 0x1D4C. The RW buffer copies the above data block and the aforementioned structure with a length of 0x90. The structure also writes a pointer to the decrypted data block (in the address space of the child process (cmd.exe)). In RWX, the buffer is copied ( WriteProcessMemory) decrypted data block with code.
Then, in the cmd.exe process, a stream ( CreateRemoteThread ) is created with an entry point at the beginning of the RWX buffer, a pointer to the RW buffer is passed as an argument. This completes the clone function, the action continues in the cmd.exe process.

Interestingly, the clone function seems to be like an unattainable piece of code that imports ( LoadLibraryW ) the " WorkPolyhistor " library .

The code injected into cmd.exe


It performs the following actions:

  • finds the addresses of the necessary functions from kernel32.dll (its address is obtained from the parent process)
  • loads ntdll , Ole32 , User32 libraries
  • Finds addresses of necessary functions in these libraries.

It is interesting that for most of the functions in the code there is no function name, but only CRC32 on behalf of (all function names from the loaded library are searched until there is a function with the desired CRC32 on behalf of). Perhaps this is protection against getting the list of imported functions by the strings utility, although it is strange that code stored in encrypted form has such protection, while the dll itself imports functions simply by name. In total, the following functions are detected:

kernel32:

  • GetProcAddress
  • Loadlibrary
  • Globalalloc
  • GetTempPath
  • GetFileAttributesW
  • CreateProcessW
  • Globalfree
  • GlobalRealloc
  • Writefile
  • CreateFileW (found by name)
  • Writefile
  • Closehandle
  • Gettickcount
  • ReadFile
  • GetfileSize

ntdll:

  • RtlCreateUnicodeStringFromAsciiz
  • ZwDelayExecution
  • ZwTerminateProcess
  • swprintf

ole32:

  • CoInitialize (found by name)
  • CoCreateInstance (found by name)

msvcrt:

  • rand
  • srand

Next, the process using GetTempPath obtains the path to the temporary directory, creates a file in it with the name 26342235.dat, where the file name is the decimal record of TickCount received from the parent process and iterates on each new attempt (i.e. if the download failed on the first attempt, then on the second attempt a file will be created with the name 26342236.dat). After that, the wininet library is loaded and there are pointers to the following functions:

  • InternetCloseHandle (crc32: 0xe5191d24)
  • InternetGetConnectedState (crc32: 0xf2e5fc0c)
  • InternetOpenA (crc32: 0xda16a83d)
  • InternetOpenUrlA (crc32: 0x16505e0)
  • InternetReadFile (crc32: 0x6cc098f5)

Using InternetGetConnectedState it checks if there is a network, if not, the application calls the function at a nonexistent address and crashes (such protection against determining the address from where the payload is obtained using a machine isolated from the network. This is the only case when the application terminates abnormally, in the rest 3 attempts are made , after which cmd.exe terminates using ZwTerminateProcess ). If there is a network, then using the found functions, the payload is downloaded from the URL passed from the parent process (https://pastebin.com/raw/Jyujxy7z) and saved to the previously created file with the .dat extension.

Next, the payload is read from the .dat file, decoded (base64), decrypted using XOR with CRC32 from the URL, check that the first 2 bytes of the decrypted data are 'MZ', if so, the result is saved to a file with the same name but extension. exe

Python decryption code
from binascii import crc32
from base64 import b64decode


def decrypt_payload(payload_b64: bytes, url: str):
    payload_bin = b64decode(payload_b64.decode())
    key = str(crc32(url.encode())).encode()
    decrypted = bytearray()
    for i, b in enumerate(payload_bin):
        decrypted.append(b ^ key[i % len(key)])
    return bytes(decrypted)

Using the CreateProcessW function, the saved file is launched. If all goes well, the cmd.exe process exits using ZwTerminateProcess . If something went wrong (except for the lack of a network), then everything is repeated anew, a maximum of 3 attempts are made, the names of the dat and exe files are increased by 1 each time.

Second dll


The second dll (sha256 006200fcd7cd1e71be6c2ee029c767e3a28f480613e077bf15fadb60a88fdfca ) 32 bit.

In it, the main malicious functionality is implemented in the clone function . It also decrypts 2 buffers. The first buffer is 0x78 (120) bytes in size, such data is decrypted and written to it (in decrypted form):

https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\x1e(\xf0\x0e\xc5r\xc0;\x12)\xc0;Jr\xc0;Y4\xbc\x03/Mx\x07\x038\xde\x01\x9e\x05\xe0\x1d#\x08\xbc\x03\xeeU\xf0\x0e\x18{x\x078\x1a\xf0\x0e\xccg\xf0\x0eze\xde\x01\x89&\xe0\x1d\xf6\x1f\xe0\x1d

It can be seen that in the beginning is the same URL as in the x64 version.

A second buffer of size 0x4678 bytes is allocated with RWX permissions. The code is decrypted into it, after which a function is called from it with an offset of 0x4639 from the beginning of the buffer.

I did not analyze this code in detail. He also finds functions on CRC32, launches notepad.exe, injects code there that downloads payload from the same URL on pastebin.

Payload with pastebin


The decrypted payload with pastebin is a 32-bit exe file (sha256 9509111de52db3d9a6c06aa2068e14e0128b31e9e45ada7a8936a35ddfaf155f ) I did not disassemble it in detail yet due to lack of time.

Conclusion


In general, the malware impressed me rather poorly written, with a lot of errors (I do not call them here intentionally). As if they were whipping up.
dll finds in memory functions that are later not used anywhere. Probably this code, like a macro, is regularly rewritten by cybercriminals every time it starts to be detected by antiviruses, as a result of which such artifacts remain in the code.

It is also interesting that in the “dll” version of the dll that is “dropped” onto the x64 disk, the names of the “dangerous” imported functions are not masked (you can at least see them strings), and in the code that is decrypted in memory and does not fit on the disk, they are located on CRC32 from name, not just by name.

The pastebin payload was removed a few days later.

PS KDPV taken from here

All Articles