I took a very quick look at the sample and found it interesting because no packing has been applied, which is quite popular among malicious samples nowadays. The sample will require administrator rights, enforced by its manifest file (you can manipulate this with a resource editor however the sample won't work afterwards if you do this, keep reading to find out why).
When the sample starts up, one of the first things it will do is create a file within the Windows directory ("
C:\Windows\infpub.dat") with the CreateFile function (Win32 API - KERNEL32). The desired access rights for the file handle of the newly created file will be sufficient to perform write operations using the handle if a handle is successfully acquired. Once the handle has been acquired (therefore the file was created), the sample will make a call to WriteFile (also Win32 API and exported by KERNEL32) so it can write data to the new file. The file write operation is essential because the sample will hold the bytes of a Portable Executable within the newly created file (which will be empty prior to this write operation).
Below are the bytes for the executable content held within the newly created file thanks to the WriteFile operation the sample performs.
Code:
4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00
00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 e8 00 00 00 0e 1f ba 0e 00 b4 09 cd
21 b8 01 4c cd 21 54 68 69 73 20 70 72 6f 67 72 61 6d
20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20
44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00
00 00 ee c2 74 25 aa a3 1a 76 aa a3 1a 76 aa a3 1a 76
a3 db 8f 76 ae a3 1a 76 1f 3d fa 76 ac a3 1a 76 1f 3d
c5 76 ab a3 1a 76 a3 db 89 76 b1 a3 1a 76 aa a3 1b 76
7d a3 1a 76 b1 3e b5 76 b7 a3 1a 76 b1 3e 81 76 ab a3
1a 76 b1 3e 87 76 ab a3 1a 76 52 69 63 68 aa a3 1a 76
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 45
00 00 4c 01 05 00 85 03 ec 59 00 00 00 00 00 00 00 00
e0 00 02 21 0b 01 0a 00 00 c0 00 00 00 4c 05 00 00 00
00 00 38 79 00 00 00 10 00 00 00 d0 00 00 00 00 00 10
00 10 00 00 00 02 00 00 05 00 01 00 00 00 00 00 05 00
01 00 00 00 00 00 00 80 06 00 00 04 00 00 b8 fc 06 00
03 00 40 01 00 00 10 00 00 10 00 00 00 00 10 00 00 10
00 00 00 00 00 00 10 00 00 00 c0 2c 01 00 3b 00 00 00
ac 1a 01 00 18 01 00 00 00 90 01 00 00 d6 04 00 00 00
00 00 00 00 00 00 00 10 06 00 88 34 00 00 00 70 06 00
a0 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0
00 00 50 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 2e 74 65 78 74 00
00 00 d3 bf 00 00 00 10 00 00 00 c0 00 00 00 04 00 00
00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 2e 72
64 61 74 61 00 00 fb 5c 00 00 00 d0 00 00 00 5e 00 00
00 c4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00
00 40 2e 64 61 74 61 00 00 00 70 53 00 00 00 30 01 00
00 0a 00 00 00 22 01 00 00 00 00 00 00 00 00 00 00 00
00 00 40 00 00 c0 2e 72 73 72 63 00 00 00 00 d6 04 00
00 90 01 00 00 d6 04 00 00 2c 01 00 00 00 00 00 00 00
00 00 00 00 00 00 40 00 00 40 2e 72 65 6c 6f 63 00 00
90 0d 00 00 00 70 06 00 00 0e 00 00 00 02 06 00 00 00
00 00 00 00 00 00 00 00 00 00 40 00 00 42 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
We know that the bytes are for an PE (Portable Executable) because the PE format is being used. 4d 5a represents MZ which is the header for all PE files, and the bytes representing the compatibility for MS-DOS and the PE sections are present.
This explains why the initial launcher required administrator rights. The Windows folder is a protected directory and therefore to create a new file within the directory you will need the correct privileges... Without a bypass for User Account Control (if it is enabled)/another form of privilege escalation, you will be stuck like a fruit loop trying to perform this operation unless you go through the normal method of acquiring elevation.
Once the sample has successfully created a new file within the Windows folder (with the file extension of *.dat regardless of it being used to hold executable code) and has successfully written to the file, it will attempt to spawn a another program with custom command line arguments. It will attempt to run rundll32.exe ("
C:\WINDOWS\system32\rundll32.exe") and will use the command line arguments so rundll32.exe will make use of the infpub.dat file.
This tells us that the executable content held within the infpub.dat file is actually for a DLL and not an EXE (both file types follow the PE format though since they are both for executable code).
The full command line for the process start-up operation is below (taken from a break-point on NtCreateUserProcess).
Code:
C:\WINDOWS\system32\rundll32.exe C:\Windows\infpub.dat,#1 15
rundll32.exe can be used with arguments by specifying the DLL file on disk and then a comma (',') and then the entry point data (the desired entry point will be where the exported function is located within the DLL which must be executed by rundll32.exe).
The sample makes use of rundll32.exe to execute code from the DLL (which is masked by the .dat file extension) because it is more concealing than the sample loading the DLL into its own address space and then executing the code within the target exported DLL function. This way the behavior will be coming from a trusted Windows process instead, without Remote Code Execution (RCE) behavior needing to be carried out.
If you re-name the .dat file to .dll in another location you'll be able to view the file properties and have the OS treat it like any other PE file (since it really is a PE). This will allow you to see that it is digitally signed with the same certificate which was used for the initial launcher which created this DLL file. The DLL is 32-bit compiled by the way, there is no 64-bit compiled version regardless of whether you are on a 32-bit or 64-bit environment. Luckily for us, no "anti-reversing" has been applied for the dropped DLL file either! Which means we can perform static analysis at ease and can go back to dynamic analysis in a bit.
The DLL exports three functions: infpub_1, infpub_2 and DllEntryPoint. DllEntryPoint is for the usual DLL entry point which typically holds the switch statement for checking if the DLL has just been attached to a process/thread or if it is being detached from a process/thread. There is nothing interesting for us at this function, I've checked it.
The functions we need to be focused on are the infpub_1 and infpub_2 functions which the DLL exports.
Looking at the imports of the DLL I already have suspicions that the two used exported functions will be involved with the ransomware encryption operation. The imports shows us that the DLL may potentially make use of the CryptoAPI (Crypt32.dll), and many functions related to Cryptography exported by Advapi32.dll are imported (statically). Normal Win32 API functions are statically imported which can be used for a wide variety of things (e.g. file operations, process enumeration/termination, token privilege adjustments, and more).
We will continue looking at the threat by inspecting the first exported function, infpub_1.
The function takes 4 parameters. We know this because IDA tells us (" int __cdecl infpub_1(int, int, LPCWSTR lpCmdLine, int)"). According to IDA, the data-types for the 4 parameters are: int (integer), int, LPCWSTR (wchar_t -> WCHAR*) and another int.
The first thing the function does is call another function; we can see that a call is being made to sub_10007897. This happens before some of the function arguments (the parameters) are pushed off the stack (EBP is used for the parameters, and we can see that after the call to the function, push [ebp + .....] is performed.
This function will be related to adjusting token privileges so the code can perform more privileged operations. Just to be clear, this function does not actually adjust the token privileges itself; this function is nothing but a wrapper which will call another function (which will perform the task) with parameters so the called function knows which privilege to target. You can see this from the below images.
The function responsible for actually adjusting the privileges will be
sub_10007CC5 (I shall re-name it to "
ModifyTokenPrivileges". The sample will want to acquire privileges for:
SeShutdownPrivilege, SeDebugPrivilege and SeTcbPrivilege.
We can see from the above images that the functions used for the token privilege adjustments are Win32 API functions:
OpenProcessToken, LookupPrivilegeValueW and AdjustTokenPrivileges. Before any of these three functions are called, GetCurrentProcess (KERNEL32) is called to get a handle to the current process (which would be rundll32.exe) so it can be used for the OpenProcessToken call. We can also see that variables for things like the
TOKEN_PRIVILEGES structure is setup at the top of the function.
The LookupPrivilegeValueW call is performed to get the privilege value based on the privilege being requested (passed in as the parameter with a data-type form of LPCWSTR (WCHAR*). If the call is successfully, the contents of the TOKEN_PRIVILEGES structure value is changed so everything is setup for the AdjustTokenPrivileges call.
Now we've taken a look at this, let's go back to the wrapper function which will call the routine for adjusting the token privileges. It will call another function after the token privilege adjustment operation which would be
sub_1000855F. The return result of this function call is put into a variable.
The function
sub_1000855F is used for enumerating through a snapshot of running processes on the system. The function will perform comparisons with the file name of the found processes. I do not know which processes it searches for specifically just yet, however if we check the Strings of the DLL we can get some ideas of which processes it may attempt to find (if they are active) for various reasons.
Strings:
Code:
Fast decoding Code from Chris Anderson
invalid literal/length code
invalid distance code
invalid distance too far back
1.2.8
incorrect header check
unknown compression method
invalid window size
unknown header flags set
header crc mismatch
invalid block type
invalid stored block lengths
too many length or distance symbols
invalid code lengths set
invalid bit length repeat
invalid code -- missing end-of-block
invalid literal/lengths set
invalid distances set
invalid literal/length code
invalid distance code
invalid distance too far back
incorrect data check
incorrect length check
0\aw,a
Qkkbal
wn>Jj
Z* ,\t\a\vm8
H$6t\x1B\t
\a52F*
\a\t#jT$\b+e?
\n=G\\\vp
[-&LMb#{'
)q\n>(
w+OQvr
\b\r}!b
R1h58
)\\ZEo^m/
gC\bru&o
H*0\"ZOW
l!;b\tF
\rmj>zjZ
l6qnk
6?r\ag
IiGM>nw
\x1B1A26b
ewh/?y
6~_'\x1B-
*F2\a1wsHp
-\n\v\\G=
#bML\"
`F(>\nq-q
vQO+t
^oEZ_
OZw3(?
V_:X1:
NJ2\"v
O*9y]
need dictionary
stream end
file error
stream error
data error
insufficient memory
buffer error
incompatible version
inflate 1.2.8 Copyright 1995-2013 Mark Adler
wkssvc
svcctl
scerpc
srvsvc
samr
spoolss
ntsvcs
netlogon
lsarpc
eventlog
browser
atsvc
NT LM 0.12
?????
IPC$
clr_optimization_v%d.%d.%d
rundll32 %s,#2 %s
ADMIN$
cscc.dat
IsWow64Process
GetExtendedTcpTable
255.255.255.255
%u.%u.%u.%u
WaitForMultipleObjects
kernel32
KERNEL32.dll
USER32.dll
ADVAPI32.dll
SHELL32.dll
ole32.dll
CRYPT32.dll
SHLWAPI.dll
IPHLPAPI.DLL
WS2_32.dll
MPR.dll
NETAPI32.dll
DHCPSAPI.DLL
msvcrt.dll
infpub.dat
123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
0\aw,a
Qkkbal
wn>Jj
Strings which might be appropriate right now to take interest in would be:
Other strings near where those ones are on the list, such as:
scerpc, srvsvc and samr are interesting as well. They may be used for IPC (Inter-Process Communication) via shared names pipes. For the record, the PE does import functions for operating services and working with named pipes (e.g. ConnectNamedPipe is imported -> ends up calling NtCreateNamedPipeFile internally).
I was right about suspecting that the infpub functions might be related to the encryption routine. The infpub_1 function we are currently looking at does perform operations regarding cryptography, and it is very clear via the static analysis...
The function
sub_1000554A will perform some cryptography related tasks. It will call the following functions:
CryptAcquireContextW, CryptGenRandom and CryptReleaseContext. The result of the CryptGenRandom function will be returned (BOOL data type -> int).
If this operation is successful (v2 == TRUE and not FALSE) then another function called
sub_1000636B will be called. This will be even more involved with the encryption routine. It also checks if a value is equal to something or not, so it isn't only dependent on the success state from that function.
In the above routine, we can see functions like
GetLogicalDrives and
GetDriveTypeW being called. The sample will get a list of the logical drives and will enumerate through each of them. It will check the drive type in the iteration of the found logical drives and depending on the return it will create a new thread which will be responsible for file encryption.
The reason the sample checks the drive type and compares it to 3 (integer) is because
3 represents DRIVE_FIXED. This means that the drive is fixed, like a hard-drive or inserted removable device. It seems the threat will not proceed with encryption for the drive if the drive is on a remote network (
4 - DRIVE_REMOTE) or others.
If the sample decides to proceed with the encryption based on the conditional statements, it will create a thread targeting the
sub_10006299 routine. The sample uses different threads to speed up the operations; the process will use more CPU usage however the operations will be performed as quickly as possible instead of "slowly".
This routine will end up creating another thread, but beforehand many other functions are called and are used in conditional statements (return value - if it is >= 1 then it keeps going through the if statements nested and eventually reaches the next thread creation operation).
The StartAddress for the next thread creation after all the above functions are carried out points to a function in memory which is used to drop the read me file about how the users files had become encrypted. It uses CreateFile to create the file and then uses WriteFile to write the data to the file. The file name is readme.txt.
Functions are used to destroy data (e.g. the key used for the encryption) after each operation has been completed.
The infpub_1 function will create a scheduled task before the function has completely finished.
@cruelsister has already mentioned about scheduled tasks, and she was 100% right (always is always will be)!
schtasks (schtasks.exe) is used by Windows for operating the Windows Task Scheduler. When you use APIs to work the scheduled tasks, schtasks.exe is doing the operations in the background.
The sample uses cmd.exe to operate schtasks, check the routine below.
The sample uses CreateProcessW to spawn cmd.exe with the correct command line to operate schtasks.exe into creating the scheduled task.
--------------------------------------
I briefly took a look at the infpub_2 exported function, the routine is below.
It will use the same .dat file with rundll32.exe again, I'll have to find more time to look into this more if I am capable of doing so.
Here are some routines which might interest you anyhow:
Regarding named pipes in general, set a break-point on NtCreateNamedPipeFile. It is a function exported by NTDLL but the actual function resides in kernel-mode memory (it is not exported by ntoskrnl.exe -> the address can be acquired from the System Service Dispatch Table -> the function will call IoCreateFile). If you set a break-point on it you can check the data within the OBJECT_ATTRIBUTES parameter. This will reveal the pipe name being targeted... This way you can verify all the named pipes the threat will try to make use of (for whichever reason). You can also monitor NtFsControlFile to identify when a handle to a named pipe is being used for connection/disconnection, and you can monitor NtReadFile and NtWriteFile to keep track of operations being performed with the named pipe (e.g. data being read and written from/to it).
The threat is capable of doing much more than mentioned in this post, and this post is not very detailed at all when you consider how much is packed inside the payload.
The initial launcher is likely to do a lot more than work the infpub.dat file via rundll32.exe, and the DLLs code has a lot more it can do than what was covered here.
I'm ending it here because I have to do other things today but hopefully so far it was helpful to some.
-----------------------------------------------------------------------------------------------------------
Ideas for mitigation (both user and software development):
-
Create a file in the Windows folder called infpub.dat. In my environment, I forgot to disable Windows Defender properly and it terminated the threat while I still had my break-point set for NtCreateUserProcess. After disabling it and re-starting up the threat again and setting my break-points, the sample crashed because the file infpub.dat already existed. I can look into this further, but an update to the malware may prevent such a mitigation from working.
- Monitor usage of rundll32.exe. Anti-Ransomware components in security solutions could be more aggressive towards rundll32.exe and file operations. I would say it would be unusual for genuine software to use rundll32.exe for a DLL file (especially if it is not named to have a *.dll extension) for adjusting privileges and using Crypt functions aggressively. Just my opinion.