Posted: March 28, 2018 by

Last updated:

QuantLoader is a Trojan downloader that has been used in campaigns serving a range of malware, including ransomware, Banking Trojans, and RATs. In this post, we'll take a high-level look at the campaign flow, as well as a deep dive into how the malware executes.

This guest post is written by Vishal Thakur, CSIRT/Salesforce. For more on Vishal, read his bio at the end of the blog.

QuantLoader is a Trojan downloader that has been available for sale on underground forums for quite some time now. It has been used in campaigns serving a range of malware, including ransomware, Banking Trojans, and RATs. The campaign that we are going to analyze is serving a BackDoor.

In this post, we’ll take both a high-level look at the campaign flow, as well as a deep dive into how the malware executes, with a focus on the networking functions. We’ll dig into the binary to analyze how the malware executes and how it connects back to the C2. We’ll also analyze some interesting calls the malware makes, like calling and executing the netsh command to change local firewall rules.

The latest version of QuantLoader is being served through a phishing campaign using some interesting techniques. The campaign starts with a phishing email that comes with a link serving the victim the initial JS downloader. What’s interesting is that they’ve opted for a file:// (SMB) protocol rather than the traditional http://—maybe in order to get past some proxies/firewalls.

Analysis

First of all, let’s have a look at the campaign flow:

Phish > JS downloader > QuantLoader (> C2) > Payload (Backdoor) > C2

The JS downloader, as always, has lots of code, all obfuscated:

We go ahead and print the output of the main function (AXXA in this case) and we get the stage 2 URLs:

That was a quick analysis of the JS downloader, to get us the URLs.

QuantLoader executable

We are going to start by following the execution flow as much as possible:

Let’s take a look at the assembly code:

Next, it will copy itself to the above location before execution:

Setting the right permissions (ACL):

Here, we can see that the permission for the user has been set to “Read.”

Stack view:

Let’s have a look at the process execution and persistence mechanisms.

As you can see above, the process spawns a new process after it has successfully copied itself to a different location. It is important not to confuse it with dwm.exe, a legit Windows process (Desktop Window Manager). Note that the persistence mechanism has also been initiated.

Let’s take a deeper look into this process that is spawned, and how it is spawned.

First, the directory is created:

Once that is completed, CreateFile is called to create a null file.

At this point it’s a null file—no data in it. That will be copied over later.

Note that the size of the file at this point is 0 bytes.

Then the file is copied over:

Now you can see that the file has been copied over and the size is 46080 bytes:

Now the process will be launched from this location.

You can modify it if you want:

Once the process has been successfully launched, we want to look at the next important step. It will call the WININET dll to start establishing a connection back to the admin.

After execution, it will try to connect out to its admin server:

And here is the connection:

TCP localhost:49690 49.51.228.205:http ESTABLISHED

This is the host you can see is loaded into the stack below.

We will now take a deeper look into how that unfolds in the stack.

The first step is to load the WININET DLL. It is called through the LoadLibrary function:



And now, let’s take a look at the functions that are of interest to us (highlighted and commented) in the disassembler. We will dive into a couple of these later:



Here’s the stack, where the above functions can be seen in action (variable values added):

At this point, let’s move on to the next DLL that is called: WINHTTP.dll.

Now let’s have a look at the functions that are called from here on:

As you can see, all of the above functions are “WinHttp”.

Let’s have a look at some of the more interesting functions:

WinHttpCreateUrl

This will put together the complete URL for the malware by combining the host and the path. Let’s step into it.



And here’s the complete URI with jsproxy.dll being called in for WinInet’s auto-proxy support:

Finally, we should have a look at the memory dump to see how the URI loaded into the memory:

Have a look at the stack screenshot below. You can see that the URL is loaded onto the stack and ready to be called.

And let’s have a look at the memory in parallel. You can see that the URL has been successfully loaded, and is ready to be called upon, using the URLDownloadToFile call.

Interesting ASCII strings that you can see in the above screenshot show you how the malware is adding a rule to the firewall, specifying the process and then the direction (out) for the action “Allow.” This is to make sure that the outbound request from the malware is allowed and is successful in checking in with the admin.

And here’s the view from the stack:



This is what it looks like in the CPU:

The command used is: netsh.

Here’s a view of the process image:

And here are the rules created and deployed successfully on the firewall:

Some other interesting calls:

Anti-VM

77028A50 >-FF25 F4030877 JMP DWORD PTR DS:[< &api-ms-win-core-file>; KERNELBA.GetDiskFreeSpaceExA

0018F234 |7029160E )p ; RETURN from KERNEL32.GetTickCount to WININET.7029160E

Environment ID

77028DA0 >-FF25 A8070877 JMP DWORD PTR DS:[<&api-ms-win-core-proc>; KERNELBA.GetEnvironmentStringsA

Networking

0018E9B8 |7029818C ; ASCII "getaddrinfo"

*/protocol-independent translation from an ANSI host name to an address

0018E9C0 |70298198 ; ASCII "getnameinfo"

*/protocol-independent name resolution from an address to an ANSI host name and from a port number to the ANSI service name

0018FB84 [70272C72 ; /RETURN from DNSAPI.DnsGetProxyInformation to WININET.70272C72

0018FB88 0051E4B0 °äQ ; |Arg1 = UNICODE "wassronledorhad.in"

*/returns the proxy information for a DNS server’s name resolution policy table

Once the connection has been established with the admin server (C2), the payload is served. The payload is picked by the administrator for each campaign and can be any malware type. In this campaign, it happened to be a backdoor.

The URL for the download of the payload was successfully extracted from memory. We will not be analyzing the payload for the purpose of this exercise, but I have included the details at the end of this post.

Conclusion

QuantLoader code has some interesting bits and pieces, like the firewall rules manipulation. It is a fairly straight-forward malware, and does what it has been developed to do. The campaign admins have the ability to change final payloads and run different campaigns using the same downloader.

It has been reported as ransomware, but that seems to be based on a memory-string that has a reference to Locky, which looks like a remnant from an older campaign.

Z:\var\www\4test\files\cryptors\admin\Loc2.exe

Also, it is interesting to see it being served over SMB rather than the traditional HTTP protocol.

Files from the campaign

JS Downloader:

MD5 – 6f2b5a20dba3cdc2b10c6a7c56a7bf35

SHA256 – db078628cdc41e9519e98b7ea56232085e203491bd2d5d8e49ef6708f129e1b8

https://www.virustotal.com/#/file/db078628cdc41e9519e98b7ea56232085e203491bd2d5d8e49ef6708f129e1b8/detection

QuantLoader:

MD5 – 4394536e9a53b94a2634c68043e76ef8

SHA256 – 2b53466eebd2c65f81004c567df9025ce68017241e421abcf33799bd3e827900

https://www.virustotal.com/#/file/2b53466eebd2c65f81004c567df9025ce68017241e421abcf33799bd3e827900/detection

Payload Backdoor:

MD5 – 6c6d772704abf4426c5d7e5a52c847d7

SHA256 – 0d100ff26a764c65f283742b9ec9014f4fd64df4f1e586b57f3cdce6eadeedcd

https://www.virustotal.com/#/file/0d100ff26a764c65f283742b9ec9014f4fd64df4f1e586b57f3cdce6eadeedcd/detection

Vishal Thakur has been working in InfoSec for a number of years, specializing in Incident Response and Malware Analysis. Currently, he’s working for Salesforce in CSIRT (Computer Security Incident Response Team), and before that was part of the CSIRT for Commonwealth Bank of Australia.