# A step-by-step analysis of the new malware used by APT28/Sofacy called SkinnyBoy **cybergeeks.tech/skinnyboy-apt28/** 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\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools C:\Users\\AppData\Roaming C:\Users\\AppData\Roaming\Microsoft\Windows\Templates C:\WINDOWS C:\Users\\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=#Username#¤t=1&total=1&data=”. 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](https://twitter.com/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\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools”, “C:\Users\\AppData\Roaming”, “C:\Users\ \AppData\Roaming\Microsoft\Windows\Templates”, “C:\WINDOWS” and “C:\Users\ \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¤t=%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=#Username# ¤t=1&total=1&data=): 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= ----- #Username#¤t=1&total=1&data=”. **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/](https://docs.microsoft.com/en-us/windows/win32/api/) VirusTotal: https://www.virustotal.com/gui/file/ae0bc3358fef0ca2a103e694aa556f55a3fed4e98ba57d16f 5ae7ad4ad583698/detection Fakenet: [https://github.com/fireeye/flare-fakenet-ng](https://github.com/fireeye/flare-fakenet-ng) Cluster25: [https://cluster25.io/wp-content/uploads/2021/05/2021-05_FancyBear.pdf](https://cluster25.io/wp-content/uploads/2021/05/2021-05_FancyBear.pdf) INDICATORS OF COMPROMISE C2 server: updaterweb[.]com SHA256: ae0bc3358fef0ca2a103e694aa556f55a3fed4e98ba57d16f5ae7ad4ad583698 User-Agent: Opera -----