A step-by-step analysis of the new malware used by APT28/Sofacy called SkinnyBoy

Summary

The malware extracts configuration information about the machine that it infects using the systeminfo command, and then it retrieves the list of processes by spawning a tasklist process. The content of the following directories, along with the processes’ output, is base64-encoded and exfiltrated to the C2 server updaterweb[.]com:

  • Desktop folder
  • C:\Program Files
  • C:\Program Files (x86)
  • C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools
  • C:\Users\<User>\AppData\Roaming
  • C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Templates
  • C:\WINDOWS
  • C:\Users\<User>\AppData\Local\Temp

The user agent used during the network communication is set to “Opera”, and the following is the structure of the POST request: “id=<hostname>#Username#<Serial number in decimal>&current=1&total=1&data=<data to be exfiltrated>”. The “cmd=y” command is used to download a DLL file from the C2 server, which is loaded using the LoadLibraryW API, and the first ordinal function is executed.

Analyst: @GeeksCyber

Technical analysis

SHA256: ae0bc3358fef0ca2a103e694aa556f55a3fed4e98ba57d16f5ae7ad4ad583698

The DLL has 2 exports (DllEntryPoint and RunMod). We have used rundll32.exe to run the DLL by calling the RunMod function:

Figure 1

The malware creates an unnamed event object by calling the CreateEventW API:

Figure 2

Two new threads are created by the process using the CreateThread function:

Figure 3
Figure 4

The GetMessage routine is utilized to retrieve a message from the thread’s message queue:

Figure 5

The malicious process enumerates all the messages, and it breaks the loop if the message is equal to 0x16 (WM_ENDSESSION – inform the application whether the session is ending):

Figure 6

Thread activity – StartAddress function

The malware creates an anonymous pipe using the CreatePipe API:

Figure 7

GetStartupInfoA is used to retrieve the content of the STARTUPINFO structure from when the calling process was created:

Figure 8

The binary creates a new process that runs the systeminfo command, which displays configuration information about the computer and its OS:

Figure 9

The pipe created earlier is used as an inter-process communication mechanism. The output of the systeminfo command is read via a ReadFile function call:

Figure 10
Figure 11

The list of processes is retrieved by creating a new process that runs the tasklist command:

Figure 12

The output of the tasklist command is transmitted to the main process using the ReadFile API:

Figure 13
Figure 14

The binary gets the path of the Desktop folder using the SHGetFolderPathW routine:

Figure 15

The process enumerates the files/directories from the Desktop directory using the FindFirstFileW and FindNextFileW functions:

Figure 16
Figure 17

The binary adds 18 characters of “#” before and after the folder name, as following:

Figure 18

The list of files and directories extracted before is concatenated with the above string, as shown in figure 19:

Figure 19

The following directories are also targeted by the backdoor: “C:\Program Files”, “C:\Program Files (x86)”, “C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools”, “C:\Users\<User>\AppData\Roaming”, “C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Templates”, “C:\WINDOWS” and “C:\Users\<User>\AppData\Local\Temp”. The SHGetFolderPathW function is utilized to obtain some of these folder names (0x2a = CSIDL_PROGRAM_FILESX86, 0x30 = CSIDL_ADMINTOOLS, 0x1a = CSIDL_APPDATA, 0x15 = CSIDL_TEMPLATES and 0x24 = CSIDL_WINDOWS):

Figure 20
Figure 21
Figure 22
Figure 23
Figure 24

The GetTempPathW API is utilized to retrieve the path of the %TEMP% directory:

Figure 25

The file initializes the use of the WinINet functions using the InternetOpenW API (the user agent is hard-coded as “Opera”):

Figure 26

The send and receive timeouts are set to 600 seconds using the InternetSetOptionW routine (0x6 = INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT and 0x5 = INTERNET_OPTION_CONTROL_SEND_TIMEOUT):

Figure 27
Figure 28

The malicious process establishes a connection to the C2 server updaterweb[.]com on port 443:

Figure 29

The NetBIOS name of the local computer is retrieved using the GetComputerNameA API:

Figure 30

GetUserNameA is utilized to extract the name of the user associated with the current thread:

Figure 31

The malware extracts the volume serial number of the root of the current directory via a function call to GetVolumeInformationW:

Figure 32

The process decrypts some important strings using the XOR algorithm, the keys being “CEJ&V%$84k839y92m” and “qpzoamxiendufbtbf3-#$*40fvnpwOPDwdkvn”. The strings “id=%s#%s#%u&cmd=y” and “id=%s#%s#%u&current=%s&total=%s&data=” have been computed:

Figure 33
Figure 34

The output of the systeminfo command + output of the tasklist command + the list of targeted directories and their content are base-64 encoded using the CryptBinaryToStringA API (0x1 = CRYPT_STRING_BASE64):

Figure 35
Figure 36

The HttpOpenRequestW routine is utilized to create an HTTP POST request handle:

Figure 37

The malware adds one HTTP request header (“application/x-www-form-urlencoded”) to the HTTP request handle:

Figure 38

The request is sent to the HTTP server using the HttpSendRequestExW API, as displayed in figure 39:

Figure 39

In the case of failing to connect to the C2 server on port 443, the process tries to connect on port 80:

Figure 40

The information extracted before is exfiltrated to the C2 server (id=<hostname>#Username#<Serial number in decimal>&current=1&total=1&data=<base-64 encoded data computed above>):

Figure 41

The thread sets the event created earlier to the signaled state:

Figure 42

Thread activity – sub_6BD71960 function

This thread sets the event created earlier now to the nonsignaled state using the ResetEvent routine:

Figure 43

There is a similar workflow starting with calling the InternetOpenW function up until connecting to the C2 server on port 443 (or port 80 if the first one is unsuccessful). The POST request is different this time because it contains the “cmd=y” command that is used to download a DLL file:

Figure 44

The malware queries the server to determine the amount of data available using the InternetQueryDataAvailable routine:

Figure 45

The potential DLL file is read from the handle using the InternetReadFile API (the first 4 bytes would represent the data size and there will also be 32 bytes that represent the SHA256 hash value of the content, as we’ll describe in the upcoming paragraphs):

Figure 46

The expected DLL is base64-encoded because the process tries to decode it using the CryptStringToBinaryA function (0x1 = CRYPT_STRING_BASE64):

Figure 47
Figure 48

CryptAcquireContextA is utilized to acquire a handle to the Microsoft RSA and AES Cryptographic Provider (0x18 = PROV_RSA_AES):

Figure 49

The CryptCreateHash routine is used to create a handle to a CSP (cryptographic service provider) hash object (0x800c = CALG_SHA_256):

Figure 50

After the base64-encoded DLL file is decoded, then the malware hashes the buffer that is supposed to contain a DLL file using the SHA256 algorithm:

Figure 51

The hash value is extracted by calling the CryptGetHashParam API, as shown in figure 52 (0x2 = HP_HASHVAL):

Figure 52
Figure 53

The malicious process verifies if the hash value computed above coincides with a 32-byte buffer that comes with the DLL file (of course that the response is emulated in our case, but we can adjust it to pass the comparison):

Figure 54
Figure 55

GetTempPathW is utilized to retrieve the path of the %TEMP% directory:

Figure 56

The malicious process creates a file called fvjoik.dll in the %TEMP% directory, as shown below:

Figure 57

The newly created file is populated with the potential DLL downloaded from the C2 server:

Figure 58

The DLL file is loaded into the address space of the current process using the LoadLibraryW routine:

Figure 59

The malware will execute the exported function with ordinal 1, as highlighted in the next figure:

Figure 60

After the function finishes, there is a call to WinExec that deletes the DLL file created earlier:

Figure 61

The process communicates again with the C2 server, and we believe that it transmits the result of the DLL execution (we won’t go into too much details here because it’s pretty much the same activity described so far). The parameters of the request are again as follows: “id=<hostname>#Username#<Serial number in decimal>&current=1&total=1&data=<data to be transmitted>”.

Main thread activity

The main thread sets the event created before to the signaled state:

Figure 62

The malware retrieves the termination status of the 2 threads using the GetExitCodeThread API:

Figure 63
Figure 64

References

MSDN: https://docs.microsoft.com/en-us/windows/win32/api/

VirusTotal: https://www.virustotal.com/gui/file/ae0bc3358fef0ca2a103e694aa556f55a3fed4e98ba57d16f5ae7ad4ad583698/detection

Fakenet: https://github.com/fireeye/flare-fakenet-ng

Cluster25: https://cluster25.io/wp-content/uploads/2021/05/2021-05_FancyBear.pdf

INDICATORS OF COMPROMISE

C2 server: updaterweb[.]com

SHA256: ae0bc3358fef0ca2a103e694aa556f55a3fed4e98ba57d16f5ae7ad4ad583698

User-Agent: Opera