15 November 2023  — 
MSP Threats Security Team

Ransomware diaries, part 1: QazLocker -Cyb3rK1dZ has more time than skills

Authors:

Ilia Dafchev, Security Researcher

Albert Zsigovits, Senior Security Researcher

Robert Neumann, Head, Acronis Cyber Protection Operations Center

Introduction

Unless you’ve been hibernating for the past decade, ransomware needs no further introduction. Hundreds of different ransomware families have emerged over the years. Initially, attackers aimed for mass impact via email as the main infection vector. Later, ransomware became more sophisticated and targeted, introducing and utilizing the RaaS model and adding double extortion to the mix, while still being true to its roots.

This article is the first in the ransomware diaries series. We will provide an in-depth look at some active ransomware families, such as Mallox or encryptors from the Epsilon Gang, revealing the unique picture they occasionally paint, along with commonalities they maintain along the way. Every family will represent a different piece of the current ransomware landscape to showcase how diversified it has become over the years.

Crypto done (almost) right

In this article, we delve into the inner workings of a recently identified ransomware family: QazLocker. It will serve as a reminder that the ransomware landscape — like the malware landscape in general — is diverse, encompassing both high-level threats and those with weaknesses that offer a chance for restoration. In each case, we will explore the attacker’s modus operandi, the tools leveraged by the threat actors and the encryption method they implemented.

Most ransomware families encrypt files in a way that makes is impossible to restore without the encryption key due to known limitations with breaking cryptological algorithms. Occasionally, however, new families will appear with a far-from-perfect implementation.

In the QazLocker case we have been investigating, threat actors mostly used readily available, off-the-shelf hacking tools for password dumping and port scanning. There were four days of dwell time between the first indications of compromise and several attempts at launching their “almost done right” ransomware payload.

Technical analysis

Tactics, techniques and procedures

Initially, the perpetrators showcased a mix of tools aimed at escalating privileges and spreading across the local network. Notably, several well-known hacking tools and so-called freeware admin utilities — misused for reconnaissance and lateral movement — were employed, including:

·       Mimikatz

·       Advanced Port Scanner

·       Network Share Scanner (NS.exe) tool

·       NirSoft MessenPass (mspass.exe)

·       NirSoft Protected Storage Pass View (pspv.exe)

·       NirSoft Password Sniffer (SniffPass.exe)

·       NirSoft Wireless Key View

These tools were predominantly located either in the “C:\Users\<user>\Desktop\Loki[tools]\” directory or in the Downloads folder. Also present is a batch script called “!start.cmd,” which executes the following Mimikatz command to dump token and credentials:

Acronis

Additional observations included attempts to install the Traffmonetizer PUA application and launch a suspicious “svchost.exe” binary from the desktop.

On day four of the attack, the threat actors introduced the ransomware payload “C:\PROGRA~2\COMMON~1\anti.exe” as a scheduled task.

Note that while these malicious programs were thwarted by Acronis Cyber Protect, the attackers persisted in their endeavors, and in the following weeks, tried to re-run them several times. After a couple of weeks of unsuccessful encryption attempts, the threat actors tried to deploy the RDP brute-force tool NLBrute, probably in an attempt to spread internally or use the compromised host to target other publicly accessible RDP services. This action also points to the possibility of and RDP-based initial attack vector. To see what damage it might pose, we took a copy of the payload, executed it without any protection in place and conducted an in-depth analysis.

Acronis
Figure 1. Attack stages

Analysis of the ransomware binary

The main ransomware payload is a compiled AutoIt script with the UPX packer on top, which is a commonly used tool for compressing executables, especially in case of AutoIt. Upon execution of the payload, it carries out a set of commands, the majority being typical of ransomware threats. All the activities, including the encryption (more on that later), are implemented purely in the scripting capabilities of AutoIt. It will check the network configuration, create a new scheduled task, delete shadow copies, disable Windows automatic repair and set the “BootStatusPolicy” to “IgnoreAllFailures”. Each command window will set its title to a random number. Next, the encryptor replicates itself to “C:\Program Files (x86)\Common Files” and saves the log of encrypted files as “log.txt” in the same folder.

Acronis
Figure 2. Process tree during execution of the ransomware payload

Once the encryption process is completed, the ransomware displays a ransom note in Notepad, along with a GUI window showing the list of encrypted files. The presence of an input field called “Key” suggests the potential of reversibility, as it would need to check the validity of it. Encrypted files retain their original extensions with an “.encrypted” string inserted before it, such as “test.encrypted.exe” for a file called “test.exe”.

Acronis
Figure 3. Ransomware GUI showing the log of encrypted files along with input filed for a decryption key

Ransom note

The ransom note displayed in Notepad is rather simplistic — there are multiple email addresses provided for communication, along with the LOCK-ID specific to the given PC. There is no link to darknet sites or any sign of a wall of shame, no crypto coin wallet and no indication of the ransom the attackers would demand.

Acronis
Figure 4. Ransom note

Dissecting AutoIt

AutoIt is a highly popular automation tool used by many IT professionals due to its flexible scripting capabilities, including compiling and embedding scripts into an executable for easy portability. There are various options for reversing and decompiling AutoIt binaries; however, depending on the skill set of the authors, we might face additional layers of obfuscation. After the initial decompilation, we were presented with a script using hex-encoded AutoIt code, as shown in the figure below.

Acronis
Figure 5. Obfuscated AutoIt script

Once we’ve deobfuscated, we will have a much more interpretable script for output. The core functionality of the script leverages Windows API function calls to achieve its goals. Both for basic file operations and encryption, it uses functions from system libraries such as kernel32.dll and advapi32.dll.

Acronis
Figure 6. Deobfuscated AutoIt script

Network artifacts

Despite the attackers leaving no signs of their demands in the ransom note, we observed that the executable was trying to connect to “blockchain.info” to check the current bitcoin (BTC) value of $1,000 or $200, depending on whether the host is domain joined or not. If it succeeds, then the returned value is written as text to a file under “C:\Program Files (x86)\Common Files”. This file and the BTC value are not used elsewhere. This might indicate that the ransomware is based on existing code that was modified and certain functionality left unused instead of being removed.

Key generation flaws

Modern ransomware is known for having "safe enough” implementation of the cryptographic algorithms used for encryption. Cybercriminals often combine a fast, symmetric algorithm (i.e., AES, ChaCha20) and a considerably slower but significantly more secure asymmetric one (i.e., RSA, ECDSA) that protects the key generated by the former. How secure the final result will be depends not only on the algorithms chosen, but also on the actual implementation — even the most secure lock in the world is worthless if one leaves the key under the doormat.

While analyzing the deobfuscated AutoIt script in detail, we concluded that implementation might also be flawed. We discovered that the “LOCK-ID” in the ransom note was used as a seed for the key generation algorithm. The “LOCK-ID” is produced by getting the first (top) MAC address returned in the output from the “ipconfig /all” command, removing the delimiters and appending the current month as a hex digit to it.

Acronis
Figure 7. LOCK-ID generation algorithm

The last five bytes from the LOCK-ID are used in the key derivation; those will be encrypted with RC4. The passphrase set for RC4 is the MD5 hash of the string “Kernel32.dll” — the output cipher text is also five bytes long. An MD5 hash of these five bytes is calculated, and the final result is used in the Windows API “CryptDeriveKey” to derive an AES 256bit key. The generated key is used with the Windows function “CryptEncrypt” to encrypt the files using AES cipher in CBC mode and the initialization vector (IV) set to zeroes.

Consequently, to derive the decryption key, either the LOCK-ID is needed or, alternatively, the month when encryption took place and the MAC address of the PC.

Acronis
Figure 8. The encryption algorithm

Building a decryptor

As showcased above, the encryption scheme in our case can be easily reversed due to a weak implementation of the otherwise secure encryption algorithms. To restore the encrypted files, we have created a proof-of-concept decryptor script in Python, which you can find in the appendix.

Acronis
Figure 9. Decryption of file kernel32.encrypted.dll
Acronis
Figure 10. Encrypted and decrypted versions of kernel32.dll

Conclusion

In this first article in the ransomware diaries, we have aimed to showcase a ransomware family that represents the lower ranks of the cybercriminal hierarchy. Our analysis reveals what happens when cybercriminals have the right (bad) intentions, but lack the skills required to build a sophisticated ransomware threat or penetrate the defenses of valuable targets. Despite the unsophisticated nature of QazLocker ransomware, it is still capable of encrypting critical information and causing significant damage for victims, especially those who lack adequate data backups and comprehensive endpoint protection.

Given the notoriety of major ransomware groups and the frequent media coverage of ransomware attacks, companies and individuals might assume that the possibility of decrypting their files is futile. While most of the time the above statement is correct, our analysis illustrates that less sophisticated threats still lurk in the wild and, encouragingly, file restoration might be achieved without succumbing to ransom demands. Unfortunately, the ransomware landscape is diverse and full of threat actors capable of causing damage of higher magnitudes. Stay tuned for part two of the ransomware diaries.

Protection statement

Acronis Active Protection, an advanced ransomware protection technology, protects against QazLocker ransomware. Victims without Active Protection may try to restore encrypted files via our decryptor script.

Acronis
Figure 11. - Active Protection detects FiilekyCrypt and restores affected files
Acronis

Ransom note emails:

·       fiileky2023@onionmail.com

·       fiileky2023@yahooweb.co

Ransom note filename:

·       Readme.txt

Extension:

·       <filename>.encrypted.<original_extension>

Mutex:

·       “qazwsxedcrfvtgybhnujmiko”

Packer and obfuscation:

·       UPX 3.91

·       AutoIT 3.3.16.1

Executed commands:

·       cmd.exe /c ipconfig /all

·       cmd.exe /c schtasks /create /sc onlogon /tn 2953439145 /rl highest /tr C:\PROGRA~2\COMMON~1\CE43E9~2.EXE

·       cmd.exe /C title 2676167|vssadmin.exe Delete Shadows /All /Quiet

·       cmd.exe /C title 8612145|bcdedit /set {default} recoveryenabled No

·       cmd.exe /C title 6039549|bcdedit /set {default} bootstatuspolicy ignoreallfailures

·       notepad.exe

Web requests:

·       https://blockchain[.]info/tobtc?currency=USD&value=200

·       https://blockchain[.]info/tobtc?currency=USD&value=1000

Appendix:

Python decryptor:

from Crypto.Hash import MD5

from Crypto.Cipher import ARC4

from Crypto.Cipher import AES

from Crypto.Util.Padding import unpad

from ctypes import *

from ctypes.wintypes import DWORD, LPVOID , UINT, BOOL, BYTE, LPCSTR

import argparse

import sys, os

import binascii

CryptAcquireContextA = windll.advapi32.CryptAcquireContextA

CryptAcquireContextA.restype = BOOL

CryptCreateHash = windll.advapi32.CryptCreateHash

CryptCreateHash.restype = BOOL

CryptHashData = windll.advapi32.CryptHashData

CryptHashData.restype = BOOL

CryptEncrypt = windll.advapi32.CryptEncrypt

CryptEncrypt.restype = BOOL

CryptDeriveKey = windll.advapi32.CryptDeriveKey

CryptDeriveKey.restype = BOOL

CryptExportKey = windll.advapi32.CryptExportKey

CryptExportKey.restype = BOOL

parser = argparse.ArgumentParser(description="Decryptor")

parser.add_argument("-f", "--file", help="Target file to decrypt", type=str, metavar='FILE')

parser.add_argument("-l", "--lockid", help="The LOCK-ID from the ransom note", type=str, metavar='LOCKID')

parser.add_argument("-m", "--month", help="The month when the encryption happened (1-12)", type=int, metavar='MONTH')

parser.add_argument("-a", "--mac_address", help="The top(first) MAC address in the output from 'ipconfig /all' command", type=str, metavar='MAC')

args = parser.parse_args()

if args.lockid:

    if args.month or args.mac_address:

        print("ERROR: Either only LOCKID or both MONTH and MAC should be used!")

        sys.exit()

    if len(args.lockid) != 13:

        print("ERROR: invalid LOCK-ID")

        sys.exit()

    for c in args.lockid.upper():

        if c not in "0123456789ABCDEF":

            print("ERROR: invalid LOCK-ID")

            sys.exit()

    lock_id = args.lockid

    key_seed = args.lockid[-5:].upper()

else:

    if not (args.month and args.mac_address):

        print("ERROR: Either only LOCKID or both MONTH and MAC should be used!")

        sys.exit()

    if len(args.mac_address) != 17:

        print("ERROR: invalid MAC address")

        sys.exit()

    for c in args.mac_address.upper():

        if c not in "0123456789ABCDEF-:":

            print("ERROR: invalid MAC address")

            sys.exit()

    if args.month < 1 or args.month > 12:

            print("ERROR: invalid month")

            sys.exit()

    hex_month = hex(args.month)[2]

    normalized_mac = args.mac_address.replace(':','').replace('-','')

    lock_id = (normalized_mac + hex_month).upper()

    key_seed = lock_id[-5:].upper()

RC4_PASSPHRASE = b"Kernel32.dll"

print(f"LOCK-ID: {lock_id}")

print(f"KEY SEED: {key_seed}")

print(f"RC4 PASSPHRASE: {RC4_PASSPHRASE.decode('utf8')}")

passphrase_md5 = MD5.new()

passphrase_md5.update(RC4_PASSPHRASE)

print(f"MD5(RC4_PASSPHRASE): {passphrase_md5.hexdigest()}")

cipherRC4 = ARC4.new(passphrase_md5.digest())

aes_key_seed = cipherRC4.encrypt(key_seed.encode('utf8'))

print(f"AES KEY SEED: {binascii.hexlify(aes_key_seed).decode('utf8')}")

hProv = c_void_p()

dwProvType = 0x0018

dwFlags = 0xF0000000

CryptAcquireContextA(byref(hProv), None, None, dwProvType, dwFlags)

error = GetLastError()

if error:

    print("CryptAcquireContextA")

    print(error)

    print(WinError(error))

hHash = c_void_p()

CALG_MD5 = 0x00008003

CryptCreateHash(hProv, CALG_MD5, 0, 0, byref(hHash))

error = GetLastError()

if error:

    print("CryptCreateHash")

    print(error)

    print(WinError(error))

CryptHashData(hHash, aes_key_seed, 5, 1)

error = GetLastError()

if error:

    print("CryptHashData")

    print(error)

    print(WinError(error))

CALG_AES256 = 0x6610

hKey = c_void_p()

CryptDeriveKey(hProv, CALG_AES256, hHash, 1, byref(hKey))

error = GetLastError()

if error:

    print("CryptDeriveKey")

    print(error)

    print(WinError(error))

PLAINTEXTKEYBLOB = 0x8

ptKey = create_string_buffer(64) 

keyLen = DWORD(64)

CryptExportKey(hKey, 0, PLAINTEXTKEYBLOB, 0, byref(ptKey), byref(keyLen))

error = GetLastError()

if error:

    print("CryptExportKey")

    print(error)

    print(WinError(error))

aes_key = ptKey.raw[keyLen.value-32:keyLen.value]

print(f"AES KEY: {binascii.hexlify(aes_key).decode('utf8')}")

if args.file:

    if os.path.isfile(args.file):

        output_filename = args.file.replace('.encrypted','')

        with open(args.file, 'rb') as f_in, open(output_filename, 'wb') as f_out:

            data = f_in.read()

            IV = b"\x00"*16

            print("IV: " + "00"*16)

            print("Mode: CBC")

            print(f"Decrypting file '{args.file}'..")

            cipherAES = AES.new(aes_key, AES.MODE_CBC, iv=IV)

            plaintext = cipherAES.decrypt(data)

            f_out.write(unpad(plaintext, 16))

            print(f"Decrypted successfuly. Output file: '{output_filename}'")

    else:

        print(f"ERROR: invalid file '{args.file}'")



Ilia Dafchev is a Security Researcher at Acronis, specializing in threat research while his skills also include malware analysis, developing anti-malware signatures and automation tools. Ilia's prior experience involved serving as a security analyst, where he responded to security incidents and performed digital forensic investigations.

Albert Zsigovits is a Senior Security Researcher at Acronis, specializing in threat hunting, memory forensics and signature development. He enjoys the challenge of connecting the dots between cybercrime and criminal rings leveraging threat intelligence and open source intelligence techniques. Albert is also a former conference speaker at AVAR, BSidesBUD, BSidesVienna, DisobeyFi, Hacktivity, SEC-T and VirusBulletin.

Robert Neumann is the Head of the Cyber Protection Operations Center at Acronis. Besides managing teams to counterbalance the fight against cybercriminals, he is focusing on various short- and long-term research projects, ranging from small-scale malicious campaigns through niche malware and file formats to in-depth investigations and threat actor attribution.