CryptBot v3 (Final) - Malware Analysis

Featured image
CryptBot series · 3 parts
  1. CryptBot Malware Analysis
  2. CryptBot v2.0 - Malware Analysis
  3. CryptBot v3 (Final) - Malware Analysis

Overview

CryptBot is a persistent infostealer malware targeting Windows systems, first identified in 2019. Its primary goal is to steal sensitive data, including browser credentials, cryptocurrency wallets, cookies, credit card details, and system screenshots. Distributed via phishing emails, cracked software sites, or malicious LNK files, CryptBot has evolved significantly. This analysis examines version 3 (CryptBot v3.0), which builds on the techniques observed in v2 (see our previous analysis), introducing a new compiler, enhanced obfuscation, and refined infection chains.

Key updates in v3 include a switch to the GNU linker ld (GNU Binutils 2.40), potentially to evade signature-based detection, a new RC4 key (LkgwUi), and the use of advanced obfuscation techniques, possibly leveraging the gimple obfuscator. The malware retains its core functionality, including RC4 encryption for data exfiltration, but introduces new C2 domains and modified persistence mechanisms. Variants continue to show code reuse with other stealers, often referred to as “Yet Another Silly Stealer (YASS).”

What is RC4 Encryption?

As discussed in our CryptBot v2 analysis, RC4 (Rivest Cipher 4) is a stream cipher used by CryptBot to encrypt stolen data before exfiltration to command-and-control (C2) servers. Its simplicity and speed make it ideal for malware, though its cryptographic weaknesses remain a concern. In v3, the RC4 implementation remains consistent, but the key has changed to LkgwUi.

Analysis

Activities

Programming Language

The malware is written in C/C++, as indicated by low-level Windows API calls and memory management patterns observed during analysis with IDA Pro. Unlike v2, which used Microsoft’s linker, v3 is compiled with GNU linker ld (GNU Binutils 2.40), likely to disrupt signature-based tracking. Dynamic imports from libraries like kernel32.dll, wininet.dll, and crypt32.dll are resolved at runtime, consistent with v2.

Linker: GNU linker ld (GNU Binutils)
Description: Linker -  GNU linker ld (GNU Binutils)(2.40)[GUI32]
Info: GUI32
Version: 2.40

Static Analysis

String Extraction

Extracting wide strings from the binary reveals stealer-related indicators, many of which overlap with v2, such as:

UID:
UserName:
ComputerName:
DateTime:
UserAgent:
Keyboard Languages:
Display Resolution:
CPU:
RAM:
GPU:
Installed Apps:

Additional strings include build artifacts exposing the development environment:

/home/anal/bot/zip_include/zip.c
/home/anal/bot/zip_include/miniz.h

These paths suggest a Linux-based build environment, a shift from v2, potentially to obscure the malware’s origin.

Main Function

The main function in v3 is heavily obfuscated, with IDA Pro reporting “sorry, this node is too big to display” due to extensive data initialization. The function moves data dynamically into the .data section, as shown below:

alt text

This extensive data initialization, stored in the .data section (e.g., dword_807140 to dword_80718C)

text:005AF200                 push    ebp
.text:005AF201                 mov     ebp, esp
.text:005AF203                 push    edi
.text:005AF204                 push    esi
.text:005AF205                 push    ebx
.text:005AF206                 and     esp, 0FFFFFFF0h
.text:005AF209                 sub     esp, 0B0h
.text:005AF20F                 mov     ebx, [ebp+argc]
.text:005AF212                 mov     edi, [ebp+argv]
.text:005AF215                 mov     esi, [ebp+envp]
.text:005AF218                 call    sub_401780
.text:005AF21D                 mov     edx, 0FFFFCE8Ch
.text:005AF222                 mov     ecx, 4
.text:005AF227                 mov     dword_80A0A0, 2F5Eh
.text:005AF231                 mov     eax, ds:pbBuffer
.text:005AF236                 mov     [esp+0BCh+var_10], eax
.text:005AF23D                 xor     eax, eax
.text:005AF23F                 mov     eax, 29B9h
.text:005AF244                 mov     dword_807120, 17AEh
.text:005AF24E                 mov     dword_807140, 0AB0E1B75h
.text:005AF258                 mov     dword_807144, 3686Ch
.text:005AF262                 mov     dword_807148, 65CC42F2h
.text:005AF26C                 mov     dword_80714C, 8C014825h
.text:005AF276                 mov     dword_807150, 0BF4C4495h
.text:005AF280                 mov     dword_807154, 8AFBAE0Eh
.text:005AF28A                 mov     dword_807158, 0C97E436Eh
.text:005AF294                 mov     dword_80715C, 1E64401Ah
.text:005AF29E                 mov     dword_807160, 0CEBE992Dh
.text:005AF2A8                 mov     dword_807164, 9289F207h
.text:005AF2B2                 mov     dword_807168, 0E79964D6h
.text:005AF2BC                 mov     dword_80716C, 91D43309h
.text:005AF2C6                 mov     dword_807170, 7670A9C1h
.text:005AF2D0                 mov     dword_807174, 6ED804Dh
.text:005AF2DA                 mov     dword_807178, 299A7B86h
.text:005AF2E4                 mov     dword_80717C, 57DDDD62h
.text:005AF2EE                 mov     dword_807180, 0D8D70F48h
.text:005AF2F8                 mov     dword_807184, 0A1105B5Ah
.text:005AF302                 mov     dword_807188, 2F458880h
.text:005AF30C                 mov     dword_80718C, 3F031802h

Deeply analsysis suggests the use of an obfuscator, potentially gimple (see hellscape).

.text:006C392D                 vzeroupper
.text:006C3930                 call    sub_425100
.text:006C3935                 mov     eax, [esp+0BCh+var_10]

The function ends with a call to sub_425100, renamed mw_main_1 for clarity:

.text:006C3930                 call    mw_main_1

Many calls and insane thing happen on this binary so let’s move to dynamic analysis …

Dynamic Analysis

Dynamic analysis was performed using x32dbg, setting breakpoints on key functions:

Breakpoints were set as follows:

alt text

Type=Software
Address=000714A0
Module/Label/Exception=<crypt.exe.OptionalHeader.AddressOfEntryPoint>
State=One-time
Disassembly=mov dword ptr ds:[9B3070],1
Summary=entry breakpoint
...
Address=0021F200
Module/Label/Exception=<crypt.exe.main>
State=Enabled
Disassembly=push ebp
...
Address=777796A0
Module/Label/Exception=<msvcrt.dll.strcmp>
State=Enabled
Disassembly=mov edx,dword ptr ss:[esp+4]
...

The entry point (0x00070000) was aligned with IDA Pro for consistency.

alt text

Running until the strlen breakpoint revealed the RC4 key LkgwUi at 011FF5C0.

alt text

The encrypted blob at 0x00475960 was extracted and decrypted, confirming the configuration’s similarity to v2. alt text

RC4 Implementation

The RC4 encryption routine remains consistent with v2, using a new key (LkgwUi). The implementation is similar to the mw_rc4 function described in our previous analysis. The key is identified during dynamic analysis at address 011FF570:

"LkgwUi"

Manual Decryption

To decrypt the configuration, extract the encrypted blob at address 0x00475960 using x32dbg or IDA Pro:

# In x32dbg, navigate to 0x00475960 and extract bytes

The extracted hex blob (partial example):

FE 93 1F C5 6E 11 10 78 AE 72 C8 0B 72 3C 18 FC E7 18 10 CA 6D E1 C5 F3 69 0A 02 99 64 0C 60 79 2A CC DD C4 07 A1 82 E0 D5 0D A9 82 02 52 EE F0 B2 F5 4A 13 76 F6 D6 65 B3 3F F1 5D 4F 81 F5 3B 46 7F 8E B9 7A 68 73 FD D9 ED 6B 59 38 7B 03 7B 40 8A 22 53 52 BD 6E AC 2C 94 B0 F1 F0 05 70 75 E9 F3 A9 EF F1 08 29 5D 6D A1 54 2A 25 97 E0 97 27 80 B0 9C 0B 9F 32 9A 3D 95 78 23 AF A1 C5 C4 A3 06 47 89 E7 8C 00 D6 37 E0 E9 F3 1D 5B FA 82 63 DD E2 B1 DF 9B 1A 01 D6 A0 79 43 1D 13 60 34 C1 5B 1B 9B 57 8A 5E 82 82 62 BC 96 00 CB E9 68 37 76 E7 77 01 F5 7E E1 E7 C6 29 4D AF BB 88 A0 63 47 8F C7 3C 61 3C A6 41 37 15 C9 94 5F 9A C7

In CyberChef:

  1. Input the extracted hex content.
  2. Apply the RC4 recipe with the passphrase LkgwUi, setting Input to Hex and Output to Latin1.

alt text

Decrypted Config:

tventyvx20pn.top
\nuSONyiIRP
LkgwUi
\ServiceData
\ServiceData\Clip.au3
\ServiceData\Clip.exe
/c schtasks /create /tn \Service\Data /tr """"%wS""" """%wS"""" /st 00:01 /du 9800:59 /sc once /ri 1 /f
GET
POST
/index.php
/gate.php
/zip.php
/v1/upload.php
curl/8.0.1
NULL
NULL
NULL
Content-Length: %lu
HTTP
HTTPS
"encrypted_key":"
DPAPI
DISPLAY
$CREEN.JPEG
ScreenShot.jpeg
Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
Apps
Browsers
Files
Wallets
UserID.txt
Debug.txt
End.txt
log.txt
User's Computer Information.txt
Desktop
Others
NULL
An error occurred while starting the application (0xc000007b). To exit the application, click OK.
System Error
NULL
ComSpec
LocalAppData
AppData
Temp
UserProfile
NULL
NULL
analforeverlovyu.top
NULL
kernel32.dll
ntdll.dll
user32.dll
shlwapi.dll
msvcrt.dll
shell32.dll
wininet.dll
winhttp.dll
ws2_32.dll
urlmon.dll
crypt32.dll
gdi32.dll
gdiplus.dll
ole32.dll
cabinet.dll
advpack.dll
advapi32.dll
rstrtmgr.dll
winsqlite3.dll
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
GetModuleHandleA
GetModuleHandleW
GetModuleHandleExA
GetModuleHandleExW
LoadLibraryA
LoadLibraryW
LoadLibraryExA
LoadLibraryExW
GetProcAddress
FreeLibrary
NULL
MessageBoxA
MessageBoxW
NULL
CreateThread
CreateRemoteThread
CreateRemoteThreadEx
OpenThread
OpenProcess
GetThreadId
GetProcessId
CreateMutexA
CreateMutexW
ReleaseMutex
WaitForSingleObject
CreateProcessA
CreateProcessW
ShellExecuteA
ShellExecuteW
WinExec
NULL
HeapCreate
GetProcessHeap
HeapAlloc
HeapReAlloc
HeapSize
HeapFree
NULL
VirtualAlloc
VirtualAllocEx
VirtualFree
VirtualFreeEx
VirtualProtect
VirtualProtectEx
NULL
LocalAlloc
LocalFree
NULL
calloc
malloc
realloc
free
NULL
CreateFileA
CreateFileW
ReadFile
WriteFile
SetFilePointer
SetFilePointerEx
GetFileAttributesA
GetFileAttributesW
GetFileAttributesExA
GetFileAttributesExW
GetFileSize
GetFileSizeEx
CreateFileMappingA
CreateFileMappingW
MapViewOfFile
UnmapViewOfFile
CloseHandle
NULL
SHGetFolderPathA
SHGetFolderPathW
GetEnvironmentVariableA
GetEnvironmentVariableW
ExpandEnvironmentStringsA
ExpandEnvironmentStringsW
GetModuleFileNameA
GetModuleFileNameW
GetModuleFileNameExA
GetModuleFileNameExW
GetCurrentDirectoryA
GetCurrentDirectoryW
GetSystemDirectoryA
GetSystemDirectoryW
GetSystemWow64DirectoryA
GetSystemWow64DirectoryW
GetTempPathA
GetTempPathW
GetTempFileNameA
GetTempFileNameW
NULL
URLDownloadToFileA
URLDownloadToFileW
URLOpenBlockingStreamA
URLOpenBlockingStreamW
CoInitialize
CoUninitialize
NULL
WinHttpCrackUrl
WinHttpOpen
WinHttpConnect
WinHttpOpenRequest
WinHttpAddRequestHeaders
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpReadData
WinHttpReadDataEx
WinHttpQueryHeaders
WinHttpQueryOption
WinHttpCloseHandle
NULL
InternetCrackUrlA
InternetOpenUrlA
InternetOpenA
InternetConnectA
HttpOpenRequestA
HttpSendRequestA
HttpQueryInfoA
InternetReadFile
InternetReadFileExA
InternetCloseHandle
NULL
InternetCrackUrlW
InternetOpenUrlW
InternetOpenW
InternetConnectW
HttpOpenRequestW
HttpSendRequestW
HttpQueryInfoW
InternetReadFile
InternetReadFileExW
InternetCloseHandle
NULL
WSAStartup
socket
htons
inet_addr
bind
listen
accept
recv
recvfrom
send
closesocket
WSAGetLastError
WSACleanup
NULL
FindFirstFileNameA
FindFirstFileNameW
FindNextFileNameA
FindNextFileNameW
FindFirstFileA
FindFirstFileW
FindFirstFileExA
FindFirstFileExW
FindNextFileA
FindNextFileW
FindClose
NULL
RegOpenKeyExA
RegOpenKeyExW
RegQueryInfoKeyA
RegQueryInfoKeyW
RegEnumKeyExA
RegEnumKeyExW
RegQueryValueExA
RegQueryValueExW
RegCloseKey
NULL
wnsprintfA
wnsprintfW
StrStrIA
StrStrIW
PathIsDirectoryA
PathIsDirectoryW
PathFileExistsA
PathFileExistsW
SHAnsiToUnicode
SHUnicodeToAnsi
NULL
wsprintfA
wsprintfW
_snprintf
_snwprintf
swprintf
sprintf
_swprintf
sprintf_s
swprintf_s
_snwprintf_s
_vscprintf
vsnprintf
_vscwprintf
vswprintf
NULL
WideCharToMultiByte
MultiByteToWideChar
GetComputerNameA
GetComputerNameW
GetUserNameA
GetUserNameW
CopyFileA
CopyFileW
CopyFileExA
CopyFileExW
DeleteFileA
DeleteFileW
MoveFileA
MoveFileW
MoveFileExA
MoveFileExW
CreateDirectoryA
CreateDirectoryW
RemoveDirectoryA
RemoveDirectoryW
NULL
EnumDisplaySettingsA
EnumDisplaySettingsW
CreateDCA
CreateDCW
CreateCompatibleDC
CreateCompatibleBitmap
SelectObject
BitBlt
GetDeviceCaps
StretchBlt
GetObjectA
GetObjectW
GetDIBits
ReleaseDC
DeleteDC
NULL
GdiplusStartup
GdipGetImageEncoders
GdipGetImageEncodersSize
GdipLoadImageFromFile
GdipCreateBitmapFromHBITMAP
GdipSaveImageToFile
GdipSaveImageToStream
GetBitmapBits
DeleteObject
GdiplusShutdown
NULL
SHCreateMemStream
CreateStreamOnHGlobal
SaveImageToStream
IStream_Size
IStream_Reset
IStream_Read
NULL
ExtractFilesA
ExtractFilesW
Extract
FCICreate
FCIAddFile
FCIFlushFolder
FCIFlushCabinet
FCIDestroy
NULL
CryptUnprotectData
GetTickCount
GetTickCount64
QueryPerformanceCounter
CreateToolhelp32Snapshot
Process32FirstA
Process32FirstW
Process32NextA
Process32NextW
GetLocaleInfoA
GetLocaleInfoW
GetLogicalDriveStringsA
GetLogicalDriveStringsW
GetDriveTypeA
GetDriveTypeW
GetVolumeInformationA
GetVolumeInformationW
GetDiskFreeSpaceExA
GetDiskFreeSpaceExW
ReadConsoleA
ReadConsoleW
WriteConsoleA
WriteConsoleW
GetCommandLineA
GetCommandLineW
GetConsoleMode
printf
wprintf
atoi
_wtoi
FileTimeToSystemTime
GetFileInformationByHandle
IsBadReadPtr
SystemTimeToFileTime
GetTimeZoneInformation
GetLocalTime
GlobalMemoryStatusEx
DuplicateHandle
GetCurrentProcess
GetCurrentThread
GetUserDefaultLocaleName
GetSystemMetrics
GetSystemInfo
GetNativeSystemInfo
IsWow64Process
IsWow64Process2
GetKeyboardLayoutList
RtlGetVersion
GetLastError
SetErrorMode
abs
clock
OpenProcess
TerminateProcess
RmStartSession
RmRegisterResources
RmGetList
RmEndSession
strtod
isspace
Sleep
SleepEx
GetExitCodeThread
ExitThread
ExitProcess
FileTimeToDosDateTime
WinHttpSetOption
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL

This configuration closely resembles v2’s, with updated C2 domains (e.g., tventyvx20pn.top, analforeverlovyu.top) and a new file extension (Clip.au3).

Malware Configuration Extractor

The Python script for extracting and decrypting the configuration is adapted from v2, updated for the new RC4 key (LkgwUi) and blob address (0x00475960). The script assumes the encrypted blob is extracted as hex.

import argparse
import re

def rc4(key, data):
    """
    RC4 decryption implementation based on CryptBot's mw_rc4 function.
    Key: The encryption key (e.g., 'LkgwUi').
    Data: The encrypted blob as bytes.
    Returns: Decrypted data as a bytearray.
    """
    S = list(range(256))
    j = 0
    out = bytearray()

    # KSA (Key Scheduling Algorithm)
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    # PRGA (Pseudo-Random Generation Algorithm)
    i = j = 0
    for byte in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        out.append(byte ^ k)

    return out

def extract_config(binary_path, key_str='LkgwUi', blob_size=6500):
    """
    Extracts and decrypts the configuration from a CryptBot v3 binary.
    binary_path: Path to the binary file.
    key_str: RC4 key (default: 'LkgwUi').
    blob_size: Size of the encrypted blob (default: 6500).
    Returns: Decrypted configuration as a string or error message.
    """
    key = bytearray(key_str.encode('utf-8'))
    
    try:
        with open(binary_path, 'rb') as f:
            binary_data = f.read()
    except Exception as e:
        return f"Error reading binary file: {e}"

    # Search for the RC4 key in the binary
    key_pattern = re.escape(key_str).encode('utf-8')
    key_match = re.search(key_pattern, binary_data)
    
    if not key_match:
        return f"Error: RC4 key '{key_str}' not found in binary."

    # Locate the encrypted blob (0x00475960)
    blob_start = None
    try:
        base_address = 0x400000  # Typical PE base address
        target_offset = 0x00475960 - base_address
        if target_offset + blob_size <= len(binary_data):
            blob_start = target_offset
        else:
            blob_start = key_match.end() + 10  # Heuristic offset
    except:
        return "Error: Unable to determine blob offset."

    if blob_start + blob_size > len(binary_data):
        return "Error: Blob size exceeds binary length."

    # Extract and decrypt the blob
    encrypted_blob = binary_data[blob_start:blob_start + blob_size]
    decrypted = rc4(key, encrypted_blob)
    
    try:
        config = decrypted.decode('latin1').rstrip('\x00')
        config_lines = [line for line in config.split('\x00') if line]
        return '\n'.join(config_lines)
    except UnicodeDecodeError:
        return decrypted.hex()

def main():
    parser = argparse.ArgumentParser(description="CryptBot v3 Configuration Extractor")
    parser.add_argument("binary", help="Path to the CryptBot binary file")
    parser.add_argument("--key", default="LkgwUi", help="RC4 key (default: LkgwUi)")
    parser.add_argument("--size", type=int, default=6500, help="Size of encrypted blob (default: 6500)")
    args = parser.parse_args()

    config = extract_config(args.binary, args.key, args.size)
    print("Decrypted Configuration:")
    print(config)

if __name__ == "__main__":
    main()

Explanation of the Extractor

Yara Rule

The Yara rule for v3 is adapted from v2 to account for the new RC4 key and C2 domains, maintaining strict conditions to reduce false positives.

rule CryptBot_v3 {
    meta:
        author = "0xw43l"
        description = "Detects CryptBot v3 infostealer based on RC4 key, C2 strings, and configuration artifacts"
        reference = "https://0xw43l.com/posts/CryptBot-0x03/"
        date = "2025-08-30"

    strings:
        // RC4 Key
        $key = "LkgwUi" ascii fullword
        
        // C2 Domains and Paths
        $c2_1 = "tventyvx20pn.top" ascii
        $c2_2 = "analforeverlovyu.top" ascii
        $c2_3 = "/index.php" ascii
        $c2_4 = "/gate.php" ascii
        $c2_5 = "/zip.php" ascii
        $c2_6 = "/v1/upload.php" ascii
        $path_1 = "\\nuSONyiIRP" ascii
        $path_2 = "\\ServiceData" ascii
        $path_3 = "\\ServiceData\\Clip.au3" ascii
        $path_4 = "\\ServiceData\\Clip.exe" ascii
        
        // Persistence Command
        $persist = "/c schtasks /create /tn \\Service\\Data /tr" ascii
        
        // Stealer Strings
        $s1 = "UID:" ascii
        $s2 = "UserName:" ascii
        $s3 = "ComputerName:" ascii
        $s4 = "DateTime:" ascii
        $s5 = "UserAgent:" ascii
        $s6 = "Keyboard Languages:" ascii
        $s7 = "Display Resolution:" ascii
        $s8 = "CPU:" ascii
        $s9 = "RAM:" ascii
        $s10 = "GPU:" ascii
        $s11 = "Installed Apps:" ascii

    condition:
        $key and (2 of ($c2_*)) and (2 of ($path_*)) and $persist and 6 of ($s*)
}

Yara Rule Enhancements

Samples (SHA256 Hashes)

MITRE ATT&CK Mapping

Tactic Technique ID Technique Name
Initial Access T1204.002 User Execution: Malicious File
Execution T1059.001 Command and Scripting Interpreter: PowerShell
Execution T1059.007 Command and Scripting Interpreter: JavaScript/JScript
Persistence T1547.001 Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder
Defense Evasion T1140 Deobfuscate/Decode Files or Information
Defense Evasion T1027 Obfuscated Files or Information
Defense Evasion T1497 Virtualization/Sandbox Evasion
Credential Access T1555.003 Credentials from Password Stores: Credentials from Web Browsers
Collection T1005 Data from Local System
Collection T1119 Automated Collection
Collection T1113 Screen Capture
Command and Control T1071.001 Application Layer Protocol: Web Protocols
Command and Control T1573.001 Encrypted Channel: Symmetric Cryptography
Exfiltration T1041 Exfiltration Over C2 Channel

Indicators of Compromise (IOCs)

Type Indicator Description
Domain tventyvx20pn.top C2 Domain
Domain analforeverlovyu.top C2 Domain
Path \nuSONyiIRP Persistence Path
Path \ServiceData Persistence Path
Path \ServiceData\Clip.au3 Dropped File
Path \ServiceData\Clip.exe Dropped File
Hash (SHA256) ff10143803f39c6c08b2fbe846d990b92c6d1b71e27f89bca69ab9331945b14a Core Malware

References