Recently I came across an interesting phishing page which when I browsed to, showed nothing but a white blank page. There was no 404, or server not found error, just an empty page.
I found this curious, so I observed the source code for the page. Much of the page was junk, however, after scrolling to the bottom, I did notice a script that looked of interest.
<script>
var canvas = document.createElement('canvas');
var gl = canvas.getContext('webgl');
var debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
var vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
var renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
console.log(vendor);
console.log(renderer);
var width = screen.width;
var height = screen.height;
var color_depth = screen.colorDepth;
setTimeout(function(){
if (true) {
// seems we use data above to check render!
if (/swiftshader/i.test(renderer.toLowerCase()) || /llvmpipe/i.test(renderer.toLowerCase()) || /virtualbox/i.test(renderer.toLowerCase()) || !renderer) {
// blacklist!
console.log("Virtual Machine / RDP");
}
else if (color_depth < 24 || width < 100 || width < 100 || !color_depth) {
alert('bot detected')
console.log("No Display (Probably Bot)")
}
else {
$.get("m3dularbh/ajax.php?n=m3d", function(data, status){ window.location.href = 'main/';}); // document ready
}
}
}, 200);
It appears the script above is attempting to detect whether the user is viewing the webpage in a virtual machine or not. In short, it’s a virtual machine aware phishing page. It does this by checking a few values.
First, it attempts to get the value for two constants in relation to debugging the graphics driver used on a host. The WebGL API WEBGL_debug_renderer_info has two constants, which are debug Info.UNMASKED_RENDERER_WEBGL and debugInfo.UNMASKED_RENDERER_WEB. The script checks these for three values, swiftshader/llvmpipe/virtualbox. If the value matches one of these values, the phishing page is not shown.
Secondly, the script looks to check information around the user’s display, like size and colour depth, again this is to check whether the user is running in a VM. The default colour depth for a VirtualBox Virtual Machine is 24bit, which is what is being checked here.
If these conditions and checks are not met, then the page ” m3dularbh/ajax.php?n=m3d ” is loaded.
Why would a phishing page check if the user is viewing the page in a virtual machine? Well, most analysts analyse malicious pages in a dirty virtual machine to avoid causing an infection to their main device.
Bypassing Anti-analysis
So we now know what the script is looking for. When these conditions are met, we’re shown a blank page. But how do we bypass these anti-analysis techniques so we can see the actual phish page?
Well, it’s actually pretty easy, all we need is a Browser Extension. WebGL Fingerprint Defender allows you to spoof your Browser Fingerprint. Doing so allows us to see that the phishing page in this case was a well crafted DHL card harvester.
The extension WebGL Fingerprnt Defender is available for both Firefox and Chrome. There are also alternative extensions that do the same thing.
Threat Actor
When the phishing page is successfully loaded, a script called m3dularbh is run. I found this to be an interesting string, so I performed some OSINT on it. It would appear that m3dular is the name of the threat actor/phish kit used to generate this page and other similar courier based phishing campaigns.
Below we have another example of a post office based phish campaign with the same strings seen. Here we can see a bit more information on the structure of the phishing page.
I then came across this Tweet which showed a telegram channel with what appears to be the threat actor advertising their services.
The phish pages from this phishing kit all seem to be high quality and it’s likely they all contain virtual machine aware phishing pages.
Today we’re going to look at a malware campaign made up of multiple stages, with the end goal of establishing a C2 connection to a Cobalt Strike server. There are a few cool techniques that this campaign uses that we’re going to look at. I happened to come across the initial first stage phishing attachment while browsing for samples on VirusTotal and found it interesting as you do not commonly see JNLP attachments used for phishing. So, let’s get started.
Stage 1: Attachment Analysis
A JNLP file is a java web file, which when clicked, the application javaws.exe will attempt to load and execute the file. Javaws.exe is an application that is part of the Java Runtime Environment and is used to give internet functionality to java applications. JNLP files can be used to allow for applications hosted on a remote server to be launched locally. It is worth noting that to be susceptible to phishing via a JNLP the user will have to have java installed on their machine.
They are generally quite simple and are not difficult to analyse. You can easily view the content of a JNLP file by changing the extension to XML and loading the file in a text editor like notepad++. As shown in the XML code below, we can see that this JNLP file will be used to load and execute the JAR file FedEx_Delivery_invoice.jar from the domain hxxp://fedex-tracking.fun
As we know the name and location of the 2nd stage payload, we can try and download it. The domain hxxp://fedex-tracking.fun is still up, so we can download the FedEx_Delivery_invoice.jar file from here. Once we have the file, we will analyse it with JD-GUI. JD-GUI is a simple tool that allows you to decompile and view the code of JAR files. (I copied the code into Atom after opening with JD-GUI as I like the syntax highlighting there.)
As the code snippet above shows, the FedEx_Delivery_invoice.jarfile is going to attempt to download the file fedex912.exe from the domainhxxp://fedex-tracking[.]press. The executable will be placed into the Windows temp directory, where it will then be executed. The JAR file will also load the legitimate FedEx tracking website which is most likely to try and reassure the user that the file they have downloaded is a legitimate one.
Executable Analysis: Stage 2
Unfortunately, at the time of writing, the domain hosting the fedex912.exe is no longer active meaning we cannot download the file from here. However, there is a sample on Virus Total that we can download. I ran the executable in my analysis environment with process monitor and regshot and there were a few things of note. Firstly, the file fedex912.exe drops a new file called gennt.exe, which is basically just a copy of itself, into the directory C:\ProgramData\9ea94915b24a4616f72c\. The reason for placing the file here is that it is a hidden directory and not normally visible to the user. It then deletes the fedex912.exe file from the filesystem.
I used RegShot to take a before and after snapshot of the registry to compare the two after running the executable. The entry below shows the malware’s persistence mechanism. Adding the gennt.exe executable to the registry key here ensures that the malware is started every time Windows is restarted.
After doing some additional research on the executable, I found that it is supposed to launch cmd which then launches PowerShell. However, that did not occur on my test machine when running the executable. There could be a few reasons for this, one could be that the malware has anti-analysis capabilities and knows when it is being run in a standard VM. As my lab is not currently set up to counter VM aware malware, we are going to cheat slightly and use data from a sample that was run on AnyRun.
On the AnyRun analysis, we can see that cmd did launch "C:\Windows\System32\cmd.exe" /c powershell -nop -w hidden -encodedcommand” where a Base64 command was parsed to PowerShell. AnyRun records the command line, so let’s have a look into this. You can see the AnyRun anlysis here.
PowerShell Analysis: Stage 3
As is usually the case, the command line was encoded with Base64 so I used CyberChef to decode the text. Often when you decode Base64 text there will be a “.” between every single character. This is annoying but can easily be fixed by also adding a decode text operator to the recipe and setting the value to UTF-16LE(1200).
We can see that the command is further encoded with Base64, and if we scroll further down to the bottom, we can also see that it has been compressed with GunZip.
I used CyberChef to once again decode the Base64 and to decompress the GunZip Compression.
After running the above CyberChef recipe there was finally some human-readable text. There’s a lot of interesting stuff happening here. So we essentially have three parts to the PowerShell script, there’s the first chunk with a couple of functions. The middle section with a Base64 Encoded block and a “for” statement. And then there’s the final section with some defined variables and an “if” statement. We’ll tackle the Base64 Encoded block first and look at the rest of the PowerShell script a little later.
NOTE: I had to split the code screenshots into two, as there is too much code to fit into one image. I’d much rather just post the raw code, rather than screenshots, but that would result in my site being flagged for hosting malware 😂. You can download the code samples at the bottom of this post.
One thing that immediately stands out is a “for” statement underneath the Base64 encoded text in the “Powershell Script part 2” image.
The “for” statement suggests that the Base64 block is encrypted with xor with a key of 35. We can also use CyberChef to decrypt this.
As shown in the above output, a lot of it is not human-readable but we can see what looks like an IP address and information about a User-Agent. The rest of the code that we cannot understand looks to be shellcode. Let us try and do some basic shellcode analysis to see what is going on here.
I used CyberChef to convert the code above into Hex. This is straight forward to do, and only requires an additional two operators to our current CyberChef recipe. One operator converts our code into Hex, and the other is a find and replace to remove the spacing.
Once we have our Hex code, you can save the output as a .dat file. Next, I used the tool scdbg to analyse the shellcode. This tool emulates basic Windows behaviour and can intercept what Windows API calls the shellcode is requesting by emulating the Windows API environment.
After parsing the .dat file to the tool, the output below is given. The shellcode loads the wininet API library and imports two functions which are used to establish an internet connection. We can see that the connection is established to the IP address we saw earlier over port 8080.
As the shellcode does not import any other functions, it would appear that this is a simple beacon program that establishes a remote connection to the malicious IP. Additional commands are likely to be sent from the C2 server. The C2 IP address is a Ukrainian address, with ports 80, 8080 and 22 open.
Injecting into memory with PowerShell
So we’ve looked at our Base64 encoded block and determined that it’s some simple shellcode which is used to establish a connection to the C2 server. The one question we still have to answer is how is the shellcode executed? From looking at the rest of the PowerShell script, we can see that the shellcode is injected directly into memory. Below gives a basic summary of how it does this.
First the script imports two functions GetModuleHandle and GetProcAddress from system.dll, and it does this by importing them directly from memory, so it does not load the DLL from disk. These are both Windows UnsafeNativeMethods. This method of loading DLLs in this way is called Run-Time Dynamic Linking, and you can read more on it here.
These functions are then used to allocate space in memory for the function “var_va” which is the function which contains our shellcode.
Then the script decodes and decrypts the shellcode, in the same way that we did earlier with CyberChef
Next, the VirtualAlloc writes the shellcode function to space in memory for the calling process. In this case, that would be PowerShell. So, the shellcode is essentially injected into the memory space used by PowerShell.
And finally, the shellcode is then executed, where it establishes a C2 channel with the Cobalt Strike server.
What is Coablt Strike?
AnyRun attributed the PowerShell activity to Cobalt Strike and the PowerShell script and the shellcode that we analysed matches the profile and behaviour of a Cobalt Strike Beacon. Cobalt Strike is a tool used for adversary simulations and red team operations. A key feature of the tool is being able to generate malware payloads and C2 channels. The Cobalt Strike Beacon that we saw is fileless, meaning that the PowerShell script injects the Beacon straight into memory and never touches disk. Once a Cobalt Strike Beacon is present on a device, the attacker has significant capability to perform additional actions including stealing tokens and credentials for lateral movement.
Conclusion
So that brings this post to an end. I hope you found the information here useful. It’s a simple example of fileless malware and I think a good introduction for those who are maybe not very familiar with the area. It’s certainly a topic that I’m interested and something I want to research further, so expect more posts on this in the future!