{
	"id": "bf27112d-831d-4dd1-bb48-0dfc269793dc",
	"created_at": "2026-04-06T00:10:43.12731Z",
	"updated_at": "2026-04-10T03:37:08.692176Z",
	"deleted_at": null,
	"sha1_hash": "1ce956db1b0ac0abcbd73421018a72eb41055f58",
	"title": "Technical Analysis of SnappyClient | ThreatLabz",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1069807,
	"plain_text": "Technical Analysis of SnappyClient | ThreatLabz\r\nBy Muhammed Irfan V A\r\nPublished: 2026-03-18 · Archived: 2026-04-05 23:37:23 UTC\r\nThe following sections focus on SnappyClient’s attack chain as well as a technical analysis of the core features. \r\nAttack chain\r\nThe figure below shows the SnappyClient attack chain observed by ThreatLabz.\r\nFigure 1: Example attack chain of a campaign delivering SnappyClient.\r\nThe attack started with a website that impersonated a telecommunications company and targeted German-speaking users. The page lists product features and branding details. When a victim visits the page, a\r\nHijackLoader executable file is automatically downloaded on the victim’s system. This HijackLoader sample (if\r\nexecuted by a victim) decrypts and loads SnappyClient. \r\nAdditional attack chains have also been observed for SnappyClient delivery. In early February, ThreatLabz\r\nobserved an X (formerly Twitter) post by @Kostastsale describing a GhostPulse/HijackLoader intrusion via\r\nClickFix, with SnappyClient delivered as the payload.\r\nAMSI bypass\r\nSnappyClient installs a trampoline hook on  LoadLibraryExW and checks whether the process is loading amsi.dll.\r\nIf this condition is met, SnappyClient hooks  AmsiScanBuffer and  AmsiScanString to always\r\nreturn  AMSI_RESULT_CLEAN , effectively bypassing AMSI.\r\nSnappyClient configuration\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 1 of 25\n\nSnappyClient stores the main configuration as a plaintext JSON object embedded in the binary. The table below\r\nshows the configuration keys and their usage.\r\nKey\r\nName\r\nDescription\r\ntag Malware tag; sent in the registration message.\r\nbuildId Malware build ID; sent in the registration message.\r\ndd\r\nDefault directory used by SnappyClient. All folders and files references in the configuration are\r\ncreated under this directory.\r\nef Filename of the encrypted EventsDB (described in the next section).\r\nsf Filename of the encrypted SoftwareDB (described in the next section).\r\nkf Filename of the encrypted keylogger file capturing the victim’s keystrokes.\r\nc\r\nDirectory used to check whether the victim’s device is banned. SnappyClient retrieves the volume\r\nserial number of the root directory and builds a string by concatenating the volume serial number\r\nwith the string  :BANNED , then calculates the SHA-1 digest of the string. SnappyClient then checks\r\nif a filename with this hash value exists in the directory specified by this c variable under the\r\ndefault directory ( dd ). If the file exists, SnappyClient decrypts the contents by performing an\r\nXOR operation with the string  BANNED . If the decrypted content is the string TRUE, the device is\r\ntreated as banned and SnappyClient exits.\r\nab\r\nDirectory used to cache the AES-256 master key for Chromium App-Bound Encryption. The file\r\ncontents are encrypted. The filename is the first 4 bytes of the SHA-256 digest of the\r\napp_bound_encrypted_key value stored in the Local State file.\r\ne If set to True, SnappyClient creates a named shared memory and writes the malware version\r\nnumber at the start of the region. If there is a version already running and the currently running\r\nversion number is less than or equal, the process exits. If the currently running version is greater,\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 2 of 25\n\nKey\r\nName\r\nDescription\r\nthe version number in the named shared memory is updated and the older instance will terminate.\r\nThis ensures the latest version of SnappyClient is always running. The shared memory name is\r\ngenerated by creating a string in the format: {COMPUTERNAME}{USERNAME}. The string is\r\nthen reversed and its FNV-1a 32-bit hash is calculated, which is then XOR’ed with the string\r\nlength. The result is converted to decimal representation and used as the shared memory name.\r\nm Contains the mutex name. If a mutex with this name already exists, SnappyClient exits.\r\ns\r\nIf set to True, the configuration contains additional fields (si, sm, and sn) used for persistence and\r\ninstallation.\r\nsi\r\nSpecifies the installation path where SnappyClient copies itself. After the copy completes,\r\nSnappyClient launches a new process from the copied file and terminates the original process.\r\nsm\r\nIf the value is 1, SnappyClient first attempts to establish persistence using scheduled tasks. If this\r\nfails, SnappyClient attempts to establish persistence using the registry. If the value is anything else,\r\nSnappyClient only attempts to establish persistence using scheduled tasks. For scheduled tasks,\r\nSnappyClient triggers on logon of the current user and the path is set to the current process path.\r\nFor registry persistence, SnappyClient uses the autorun key\r\nSoftware\\Microsoft\\Windows\\CurrentVersion\\Run.\r\nsn Name of the scheduled task and the registry name.\r\nTable 1: Description of SnappyClient’s embedded configuration.\r\nAfter parsing the main configuration, SnappyClient parses another configuration. This configuration is also a\r\nJSON object with a single key:  pairs . The  pairs key contains a list of Class Identifiers (CLSIDs) and\r\nInterface Identifiers (IIDs) used in Chromium App-Bound Encryption. Each list item includes three properties,\r\nand all property values are Base64-encoded. The properties are:\r\nname : Name of the browser.\r\nc : Elevator CLSID.\r\ni : IID of  IElevator interface.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 3 of 25\n\nThere are two configurations that are also retrieved from SnappyClient’s C2 that are named  EventsDB\r\nand  SoftwareDB . These configurations are written to disk encrypted using the ChaCha20 cipher. Each file can\r\ncontain multiple streams of encrypted data with different keys. To separate multiple streams, SnappyClient adds a\r\nheader to the start of each encrypted stream. The structure of the header is shown below.\r\nstruct Header {\r\n DWORD MAGIC1; // Used to find the start of an encrypted stream.\r\n DWORD MAGIC2; // Used to find the start of an encrypted stream.\r\n DWORD MAGIC3; // Used to find the start of an encrypted stream.\r\n BYTE key[0x20]; // Key used in the ChaCha20 cipher.\r\n BYTE nonce[0xC]; // Nonce used in the ChaCha20 cipher.\r\n DWORD crc_value; // CRC value of the header excluding crc_value.\r\n};\r\nThe Python function below demonstrates the use of the MAGIC bytes to identify the start of the encrypted stream.\r\nimport struct\r\nimport binascii\r\nK0 = 0xCEDD9AB7\r\nK1 = 0x7FCBB9E9\r\nHEADER_LEN = 0x3C\r\ndef is_header_valid(header_bytes: bytes, k0: int = K0, k1: int = K1) -\u003e bool:\r\n if len(header_bytes) != HEADER_LEN:\r\n return False\r\n data = struct.unpack(\"15I\", header_bytes) #little-endian\r\n MAGIC1, MAGIC2, MAGIC3, crc_value_stored = data[0], data[1], data[2], data[14]\r\n condition_1 = ((MAGIC2 ^ k0) \u0026 0xFFFFFFFF) == ((~MAGIC1) \u0026 0xFFFFFFFF)\r\n if not condition_1:\r\n return False\r\n condition_2 = ((k1 ^ MAGIC3) \u0026 0xFFFFFFFF) == MAGIC2\r\n if not condition_2:\r\n return False\r\n crc_value = binascii.crc32(header_bytes[:0x38])\r\n return crc_value_stored == crc_value\r\nThe Python script to decrypt these configuration files is available in the ThreatLabz Github repository.\r\nEventsDB\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 4 of 25\n\nThe EventsDB file is a list of events sent by the C2 that perform a particular action when a condition is met. Each\r\nevent is a JSON object. The table below shows the keys in an event and their usage.\r\nKey Name Description\r\nid Event ID; sent by the C2.\r\nhash Event hash; sent by the C2.\r\nf1\r\nBase64-encoded regular expression used to check clipboard content (internal trigger type 4).\r\nIf the pattern matches, SnappyClient executes the configured action.\r\nf2\r\nBase64-encoded, action-dependent value (see the following row): for action 64, it contains\r\nthe replacement clipboard content; for action 8912, it contains the exfiltration URL.\r\naction\r\nAction(s) to perform when the condition is met; multiple values may be combined using\r\nbitwise OR. Supported values include:\r\n64 (0x40): Replace clipboard content (if it matches f1) with f2.\r\n384 (0x180): Take a screenshot of the foreground window, convert it to a JPEG\r\nimage, and send it to the C2.\r\n8912 (0x2000): Exfiltrate clipboard data over HTTP (if it matches f1). In this case, f2\r\ncontains the URL used for exfiltration and may include placeholders that are replaced\r\nwith the appropriate values:  $(name) is replaced with a string concatenating the\r\ncomputer name and username,  $(systemid) is replaced with the victim machine’s\r\nsystem ID (explained in a later section), and  $(clipboard) is replaced with the\r\nclipboard content.\r\ntype\r\nInternal trigger type. There are two supported event trigger types:\r\n3: Check filters against the window title.\r\n4: Check filters against the clipboard’s content.\r\ntarget Target type for the event. The event is only registered if the victim device’s target type\r\nmatches the target filter:\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 5 of 25\n\nKey Name Description\r\n0: Only register the event if the target filter matches {COMPUTERNAME}\r\n{USERNAME}.\r\n1: Only register the event if the target filter matches the computer name.\r\n2: Only register the event if the target filter matches the username.\r\n3: Register the event regardless of the target filter string.\r\nThis value is used to register an event for a particular victim.\r\nfilter Target filter used to check against the target type.\r\ncondition\r\nSpecifies how to match the target filter against the target type. Supported values include:\r\n0: Use regex matching.\r\n1: Use case-insensitive wildcard string matching (supports * and ? only).\r\n2: Use case-insensitive string matching.\r\nwndfilter\r\nBase64-encoded filter evaluated against the window title (internal trigger type 3) if it\r\nmatches, the configured action is executed.\r\nwndcondition\r\nSpecifies how to match wndfilter against the window title. Supported values include:\r\n0: Use case-insensitive wildcard string matching (supports * and ? only).\r\n2: Use regex matching.\r\ntags\r\nList of CRC tag hashes compared to CRC of the tag value in the main configuration. The\r\nevent is registered only if a hash matches, or if tags is 0 (no tag filtering), enabling tag-scoped targeting.\r\nTable 2: Description of the SnappyClient EventsDB configuration file.\r\nSoftwareDB\r\nThe SoftwareDB file is a list of software entries and their properties that the C2 sends to SnappyClient, which the\r\nmalware then uses to steal data. Each software entry is stored as a JSON object. SnappyClient targets software\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 6 of 25\n\napplications for data theft, which are listed in the table below. All values, except  engine and  ids , are Base64-\r\nencoded.\r\nSoftware Type Key Name Description\r\nBrowser\r\nname Name of the browser.\r\ndata_path Base directory where the browser stores per-user data.\r\nengine\r\nBrowser engine type indicating whether the browser is Chromium-based\r\nor Mozilla-based.\r\nprofile_regex Regex used to identify profile subfolders under data_path.\r\nprocess_name Executable filename for the browser.\r\nExtension\r\nname Name of the browser extension.\r\nids ID of the browser extension.\r\nengine Browser engine type.\r\nOther\r\nApplication\r\nname Name of the application.\r\nsearch_path\r\nBase application directory; SnappyClient steals files located in this\r\ndirectory.\r\nTable 3: Description of the SnappyClient SoftwareDB configuration file.\r\nAn example of the decrypted EventsDB and SoftwareDB is available in the ThreatLabz Github repository.\r\nNetwork configuration decryption\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 7 of 25\n\nSnappyClient’s also contains an encrypted network configuration. The network configuration decryption is a\r\nconvoluted process that uses a combination of ChaCha20-Poly1305, SHA1, SHA256, modified RIPEMD-160\r\n(Compared to standard RIPEMD-160, it inserts one additional compression step per block after step 31), Snappy\r\ncompression, and Base58 encoding. The complete network configuration decryption script, including all helper\r\nfunctions, is available in the ThreatLabz GitHub repository. \r\nThe Python function below replicates the algorithm to decrypt the SnappyClient network configuration.\r\ndef decrypt_config(filename: str) -\u003e tuple[bytes, bytes]:\r\n with open(filename, 'rb') as f:\r\n compressed_data = f.read()\r\n try:\r\n uncompressed_data = snappy.uncompress(compressed_data)\r\n except:\r\n print(\"decompression failed\")\r\n exit()\r\n enc_data_size = struct.unpack('H',uncompressed_data[0:2])[0] #little-endian\r\n context_size = struct.unpack('H',uncompressed_data[2:4])[0] #little-endian\r\n enc_content_outputtag = uncompressed_data[4:enc_data_size+4]\r\n enc_content = uncompressed_data[4:enc_data_size+4-16]\r\n output_tag = uncompressed_data[enc_data_size+4-16:enc_data_size+4]\r\n context_content = uncompressed_data[enc_data_size+4:enc_data_size+4+context_size]\r\n keya, noncea = generatekey_nonce(context_content)\r\n noncea = noncea[:0xc]\r\n context_content_compressed = snappy.compress(context_content)\r\n ct_and_tag, ciphertext, tag = ChaCha20Poly1305encrypt(keya, noncea, context_content_compressed)\r\n ct_and_tagb58 = base58.b58encode(ct_and_tag)\r\n ct_and_tagb58_ripemdmodb58 = base58.b58encode(ripemd160_modified(ct_and_tagb58))\r\n tag_ripemdmodb58 = base58.b58encode(ripemd160_modified(tag))\r\n keyb = hashlib.sha256(ct_and_tagb58_ripemdmodb58).digest()\r\n nonceb = hashlib.sha256(tag_ripemdmodb58).digest()\r\n nonceb = nonceb[:0xc]\r\n key_seedcompressed = ChaCha20Poly1305decrypt(keyb, nonceb, enc_content, output_tag)\r\n key_seed = snappy.uncompress(key_seedcompressed)\r\n keyc, noncec = generatekey_nonce(key_seed)\r\n enc_content2 = key_seed[:-16]\r\n tag2 = key_seed[-16:]\r\n k7z_sig = b'\\x37\\x7A\\xBC\\xAF\\x27\\x1C\\x00\\x04'\r\n k7z_data = k7z_sig + context_content[8:]\r\n key_seedripedmdmod_b58 = base58.b58encode(ripemd160_modified(key_seed))\r\n key_seedripedmdmod_b58_keyseed=key_seedripedmdmod_b58+key_seed\r\n archive_path_ip = base58.b58encode(ripemd160_modified(key_seedripedmdmod_b58_keyseed))\r\n keyd_nonced = extract_7z_memory(k7z_data, key_seed, archive_path_ip)\r\n keyd = keyd_nonced[:0x20]\r\n nonced = keyd_nonced[0x20:]\r\n enc_tag = base58.b58decode(key_seed)\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 8 of 25\n\nencd = enc_tag[:0x20]\r\n tagd = enc_tag[0x20:]\r\n ip = ChaCha20Poly1305decrypt(keyd, nonced, encd, tagd)\r\n ip = snappy.uncompress(ip)\r\n iphashobject = hashlib.sha1(ip)\r\n iphash = iphashobject.digest().hex().encode(\"utf-8\")\r\n ipmod = base58.b58encode(ripemd160_modified(ip))\r\n ipmod_keyseed = ipmod+key_seed\r\n archive_path_port = base58.b58encode(ripemd160_modified(ipmod_keyseed))\r\n port_string = extract_7z_memory(k7z_data, key_seed, archive_path_port)\r\n return ip, port_string\r\nThe decrypted network configuration contains one or more C2 IP addresses separated by semi-colons and a JSON\r\nobject with two ports for communication:\r\np: Control port. This port is used for the control session, which is the first session created by SnappyClient.\r\nVictim registration occurs over this session, and SnappyClient receives initial commands through it.\r\ndp: Data port. This port is used for data sessions. For example, when SnappyClient sends a file to the C2, it\r\nestablishes a data session using this port and transfers the data. The C2 can also instruct SnappyClient to\r\ncreate a data session by sending the respective command through the control session.  \r\nSnappyClient has only one control session, but it can create multiple data sessions as required.\r\nNetwork communication protocol\r\nSnappyClient uses a custom network communication protocol over TCP for its control session. SnappyClient first\r\nestablishes a connection to the C2 server and receives a packet from the C2, which contains a ChaCha20 key and\r\nnonce used for encryption. The packet has the following structure:\r\nstruct first_packet {\r\n BYTE key[0x20]; // Key used in ChaCha20-Poly1305 to encrypt messages.\r\n BYTE nonce[0xC]; // Nonce used in ChaCha20-Poly1305.\r\n DWORD controlsession_id; // ID of the control session.\r\n};\r\nSnappyClient replies by encrypting the key using the same key and nonce sent by the C2, and appending the\r\noutput tag after the encrypted bytes. This ensures the C2 can successfully decrypt communications from\r\nSnappyClient.\r\nAll plaintext messages exchanged between the C2 and SnappyClient are JSON objects. Before transmission,\r\nmessages are compressed using Snappy and then encrypted using ChaCha20-Poly1305. Each message is preceded\r\nby a message header with the following structure:\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 9 of 25\n\nstruct message_header {\r\n WORD command_ID; // Command ID of the message.\r\n DWORD message_id; // Unique ID for each message; generated using Mersenne Twister.\r\n DWORD unknown; // Always set to 1 by SnappyClient.\r\n DWORD message_length; // Message length before compression.\r\n BYTE zero_bytes[0x8]; // Set to zero at the end of all message headers.\r\n};\r\nThe message header is also encrypted using ChaCha20-Poly1305. The output tag is appended after the encrypted\r\nmessage header and sent to the C2.\r\nAfter sending the message header, SnappyClient sends the message. Three bytes are added to the start of the\r\nencrypted message:\r\nThe first two bytes represent the message size after compression (plus the output tag size if the third byte is\r\nset to 1).\r\nThe third byte is a flag. If set to 1, the output tag is appended after the encrypted message.  \r\nThe image below shows an example of SnappyClient’s network communication that occurs over the control\r\nsession.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 10 of 25\n\nFigure 2: Example SnappyClient network communication in the control session.\r\nThe data session follows a similar communication protocol to the control session, with two differences:\r\nSnappyClient sends a unique  datasession_id as the first packet for each session to inform the C2 that the\r\nsession corresponds to the specified data session.\r\nThe  controlsession_id sent with the key and nonce is set to zero for the data session since it is not a\r\ncontrol session. \r\nRegistration message\r\nThe first message sent by SnappyClient is a registration message, which contains victim information. The\r\ncommand_ID for the registration message is 0xFFFF. The table below shows the keys used in the registration\r\nmessage JSON object.\r\nKey Name Description\r\ncomputer Victim’s computer name; Base64-encoded.\r\nusername Victim’s username; Base64-encoded.\r\nwindow Foreground window title; Base64-encoded.\r\nwv Operating system Windows version.\r\nrc\r\nSet to 1 if the control session was reset. If the reset count is 0, set rc to 0; otherwise, set it to\r\n1. The reset count is incremented when the TCP session is reset.\r\ntt Time elapsed (in milliseconds) since the system was started.\r\nut Time elapsed (in milliseconds) from the start of SnappyClient’s execution until registration.\r\nit Time elapsed (in milliseconds) since the last input event.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 11 of 25\n\nKey Name Description\r\nram Victim system total physical memory.\r\nuac TokenElevationType of the process.\r\ncpu Victim system processor count.\r\nver Malware version number as a string. The version analyzed was 0.1.11.\r\niver Malware version number in decimal representation.\r\nts Current system time calculated using _Xtime_get_ticks.\r\ncp Control port used by SnappyClient.\r\ndp Data port used by SnappyClient.\r\nsync_events\r\nCombined hash of events in EventsDB. SnappyClient serializes event fields into a single\r\ncontiguous buffer, computes a SHA-1 hash over the buffer, and uses the first 4 bytes of the\r\ndigest as the combined hash. If no events are in EventsDB, the value is 0. This allows the\r\nC2 to quickly identify which events are currently in EventsDB configuration.\r\nsync_software\r\nCombined hash of software applications in SoftwareDB. SnappyClient calculates a hash for\r\neach software type (browser, extension, and other application) using CRC32 over the\r\nrespective fields. These three values are then XOR’ed to produce the combined hash. If no\r\nsoftware is in SoftwareDB, the value is 0. This allows the C2 to quickly identify which\r\nsoftware is currently in the SoftwareDB configuration.\r\nsoftware\r\nBase64-encoded list of installed applications on the system from the SoftwareDB\r\nconfiguration. \r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 12 of 25\n\nKey Name Description\r\nsid\r\nThe system ID of the victim’s machine. This unique ID is generated using the volume serial\r\nnumber of the root directory, the CPU signature (collected using CPUID with EAX set to 1),\r\nthe computer name, and the username. The function to generate the system ID is available in\r\nthe ThreatLabz Github repository.\r\ntag Malware tag label from the main configuration.\r\nbuildId Malware build ID from the main configuration.\r\nav Base64-encoded list of installed antivirus products on the victim’s system.\r\nmonitors\r\nList of display monitors on the system. For each monitor, a JSON object is created with the\r\nfollowing keys:\r\nname: Name of the monitor (Base64-encoded).\r\nwidth: Width of the monitor.\r\nheight: Height of the monitor.\r\nrate: Display refresh rate of the monitor.\r\nTable 4: Data collected by SnappyClient as part of the registration message.\r\nCommand messages\r\nAfter SnappyClient sends the registration message, the C2 will respond with command messages. Command\r\nmessages before encryption are also JSON objects and include a message header. \r\nDepending on the command ID, the following keys may be present in the message:\r\nid: The  controlsession_id of the network communication.\r\nsid: The  datasession_id of the network communication.\r\nframeid: This value is not currently parsed on the client side, but the value is the same across multiple data\r\nsessions.\r\nThe  command_ID in the header determines which commands to process. Each command message may include\r\nadditional arguments. The table below lists the commands supported by SnappyClient and their additional\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 13 of 25\n\narguments. The Type column contains the value of the type key in the command message, which is used as a sub-command ID.\r\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\n0xCCCE\r\n1: Screenshot Grabber\r\nmonitor: Monitor to capture the screenshot from.\r\nquality: Quality of the JPEG image (as an integer).\r\n2: Process Manager\r\naction: Type of action to perform.\r\n0 or 2: Get a list of all processes currently running on\r\nthe system, along with their process IDs (PIDs).\r\n3: Perform a process action on the processes with the\r\nspecified PIDs.\r\n1: Does nothing.\r\n2 or 3: Suspend the process (suspend all\r\nthreads).\r\n4: Resume the process (resume all threads).\r\n5 or 6: Terminate the process.\r\n3: File / Directory Operation subaction: Name of the action to perform. Supported values\r\ninclude:\r\nbrowseabs: Read a directory path from the path key’s\r\nvalue and list files under that directory.\r\narchive: Archive files based on the data key’s value.\r\nThe data key contains:\r\narchivetype (archive format, it uses the 7-Zip\r\nlibrary for compression and supports numerous\r\narchive formats)\r\nbase (base directory prepended to each item’s\r\nname)\r\nitems[] (an array of entries to include in the\r\narchive)\r\nitems[].name (relative path of the files to\r\ncompress in the base directory)\r\noutname (output archive path)\r\np (password string used to encrypt the archive)\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 14 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\nextract: Extract a file based on the data key. The data\r\nkey contains:\r\npath (full path to the archive)\r\np (password)\r\nrecursive: Recursively copy contents under a directory\r\nto a new directory. The source (items[].source) and\r\ndestination (dest) are provided in the data key.\r\nrecursivedel: Recursively delete contents under a\r\ndirectory. The path (items[].path) to delete is provided\r\nin the data key.\r\nrename: Rename a file or folder on disk from\r\nthe target key to the new key.\r\nxs: Validate shortcuts provided in the shortcuts\r\nargument. If a shortcut does not exist, report it back to\r\nthe C2. The shortcut path to check is in\r\nshortcuts[].path.\r\nquick: Execute a file specified in the data.path\r\nargument using ShellExecuteW.\r\nnewfolder: Create a new folder. The folder path is\r\nprovided in the name argument.\r\n4: Exfiltrate Keylogger File Send the keylogger file path and size to the C2.\r\n5: Browser Password Stealer\r\nSend saved browser passwords to the C2. Additional\r\narguments include the keys described in SoftwareDB,\r\nplus logins_file, which contains a regex used to match the\r\nlogin database file (Login Data).\r\n6: Browser Cookies Stealer\r\nSend browser cookies to the C2. Additional arguments include\r\nthe keys described in SoftwareDB, plus cookies_file, which\r\ncontains a regex used to match the cookies database file\r\n(Cookies).\r\n7: Clone Browser Clone browser profile artifacts such as History, Preferences,\r\nBookmarks/Favicons, Top Sites/Visited Links, Web Data, and\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 15 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\nShortcuts. It also clones other data such as Local Storage,\r\nSession Storage, Sessions, Network, Sync Data, Extension\r\nRules, and Local Extension Scripts. Additional arguments\r\ninclude the keys in SoftwareDB, plus cookies_file and\r\nlogins_file.\r\n8: Steal Browser Extension\r\nData\r\nAdditional arguments include the keys described in\r\nSoftwareDB.\r\n9: Steal Other Application\r\nData\r\nAdditional arguments include the keys described in\r\nSoftwareDB, plus include_filter and exclude_filter.\r\ninclude_filter: Regex used to select files\r\nunder search_path for collection.\r\nexclude_filter: Regex used to exclude files\r\nunder search_path from collection.\r\n10: Execute File a: Execution type. Supported values include:\r\n0: Execute the file directly. Additional arguments\r\ninclude path (path of the file to execute).\r\n1: Execute the file as a DLL using rundll32.exe.\r\n2: Extract an archive and execute the file inside it.\r\nAdditional arguments include path (archive\r\npath), arche (name of the executable to run after\r\nextraction), and archp (archive password).\r\nAdditional arguments applicable to all execution types (used\r\nwith CreateProcessW):\r\ncmd: lpCommandLine for the created process. For\r\nDLL execution, cmd includes the path to the DLL.\r\ncd: lpCurrentDirectory for the process.\r\nflags: dwCreationFlags for the process.\r\nd: Desktop name set using\r\nSTARTUPINFOW.lpDesktop.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 16 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\ntaskFlags: If set to 1, bypass UAC using the\r\nCMSTPLUA COM interface with the elevation\r\nmoniker Elevation:Administrator!new:{3E5FC7F9-\r\n9A51-4367-9063-A120244FBEC7} and the ShellExec\r\nfunction.\r\n12: Hidden VNC Browser\r\naction: Type of action to perform.\r\n1: Launch HvncBrowser. Additional arguments include\r\nkeys in SoftwareDB.\r\n0x6E (110): Migrate browser profile data from the\r\nsource key to the dst key.\r\n14: Remote File Browser\r\naction: Type of action to perform.\r\n0: Start a remote file browser for each drive and list the\r\ncontents of each directory.\r\n15: Remote Shell\r\naction: Type of action to perform.\r\n0: Initialize the shell.\r\n2: Execute the command in the data key.\r\n4: Terminate the shell.\r\n0xCCCC 0: Set up a reverse FTP\r\nproxy which forwards\r\nrequests to an internal\r\nhidden FTP server on the\r\nvictim machine controlled\r\nby the malware, allowing the\r\nC2 to exfiltrate files from\r\nthe local filesystem.\r\ncontrolport: Port used to control the proxy.\r\ntunnelport: Port through which proxied data is sent.\r\n1: Set up a reverse VNC\r\nproxy which forwards\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 17 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\nrequests to an internal\r\nhidden VNC server on the\r\nvictim machine controlled\r\nby the malware, providing\r\nthe C2 with graphical\r\nremote control.\r\n2: Set up a reverse RLOGIN\r\nproxy which forwards\r\nrequests to an internal\r\nhidden RLOGIN server on\r\nthe victim machine\r\ncontrolled by the malware,\r\ngranting the C2 command-line access..\r\n4: Set up a reverse SOCKS5\r\nproxy which forwards\r\nrequests to an internal\r\nhidden SOCKS5 server on\r\nthe victim’s machine\r\ncontrolled by the malware,\r\nenabling the C2 to relay\r\ntraffic through the victim\r\nmachine.\r\n0xDDDD N/A\r\nNo type value for this command_ID. Used to start and stop\r\ndata sessions.\r\naction: Type of action to perform.\r\n0: Start a data session. Additional arguments include\r\nsid (datasession_id of the new data session).\r\n1: Stop a data session. Additional arguments include\r\nsid (datasession_id of the session to stop).\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 18 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\n0xDCCA\r\n3: Send Files Or Folders\r\npath: Path of the file/folder to send. If the path is a folder, all\r\nfiles under the folder are sent.\r\nN/A\r\nkl: If the kl key is present in the command message, send the\r\nkeylogger file.\r\n0xDACC The type field can contain\r\nmultiple values combined\r\nusing bitwise OR operations.\r\nN/A\r\n0x200: Multi-Browser\r\nCredential Stealer\r\nContains a list of browsers to steal data from. Additional\r\narguments include the keys described in SoftwareDB.\r\n0x800: Multi-Software\r\nCredential Stealer\r\nContains a list of software to steal data from. Additional\r\narguments include the keys described in SoftwareDB, plus\r\ninclude_filter and exclude_filter.\r\ninclude_filter: Regex used to select files\r\nunder search_path for collection.\r\nexclude_filter: Regex used to exclude files\r\nunder search_path from collection.\r\n0x400: Send Keylogger File No additional arguments.\r\n0x7: Download Job\r\nurl: URL to download the file from.\r\npath: Path to save the file to.\r\nua: User-Agent to use.\r\nhdrs: Additional headers to add when downloading the\r\nfile.\r\n0x1000: File Search Job The data key contains a list of items to search for, with the\r\nfollowing properties:\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 19 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\nfilter: Filter string to search for.\r\ncondition: How to use the filter for searching.\r\n0: Case-insensitive wildcard string match (supports *\r\nand ? only).\r\n2: Regex matching.\r\nIf there is a match, report the file path back to the C2.\r\n0x40: Replace Clipboard\r\nContents\r\nReplace the clipboard content with the value of the data key.\r\n0xDADA N/A\r\nDoes not depend on type. Same as the download job\r\n(command_ID 0xDACC with type 0x7).\r\n0xEECC\r\n0: Sync Software To\r\nSoftwareDB\r\nUpdate the encrypted SoftwareDB on disk based on additional\r\narguments, then process the updated SoftwareDB. Additional\r\narguments include:\r\nhash: Combined hash of the software to check whether\r\nit has already been registered.\r\nsync_software: List of software to sync.\r\n1: Sync Events To EventsDB\r\nUpdate the encrypted EventsDB on disk based on additional\r\narguments, then process the updated EventsDB. Additional\r\narguments include:\r\nhash: Combined hash of the events to check whether\r\nthey have already been registered.\r\nsync_events: List of events to sync.\r\n0xACCC 1: Exit Exit SnappyClient. No additional arguments are required.\r\n3: Ban And Exit Creates a file on disk that marks the SnappyClient infection as\r\nbanned (as described in the main configuration section) and\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 20 of 25\n\nCommand\r\nID\r\nType/Sub-Command ID  Additional Arguments and Description\r\nexits.\r\n0xADBB\r\n1: Create IWebBrowser\r\nWindow Or Create A\r\nMessageBox\r\nThe additional information is provided in the data key. The\r\ndata properties include:\r\ntype: Subtype of the command. Supported values are:\r\n0: Create a MessageBox. Additional arguments\r\ninclude:\r\n0: Message box caption.\r\n1: Message box text.\r\nw: Width of the MessageBox.\r\nh: Height of the MessageBox.\r\n1: Create a window and embed an IWebBrowser\r\ncontrol. Additional arguments include:\r\n0: Window title.\r\n4: Content to render in the window.\r\nw: Width of the window.\r\nh: Height of the window.\r\n2: Update Network\r\nConfiguration\r\nThe updated configuration is provided in the data key. The\r\nproperties inside data are:\r\nh: Updated C2 IP.\r\np: Updated control-session port.\r\ndp: Updated data-session port.\r\nTable 5: Description of SnappyClient commands.\r\nProcess injection\r\nSnappyClient’s process injection technique uses code similar to HijackLoader. To evade user-mode API hooks\r\nwhen invoking certain native APIs, SnappyClient uses Heaven’s Gate to execute x64 direct system calls. For more\r\ndetails, refer to our earlier previous ThreatLabz blogs: HijackLoader Updates and Analyzing New HijackLoader\r\nEvasion Tactics. \r\nTo bypass Chromium’s App-Bound Encryption, the  IElevator COM interface must be instantiated from a\r\ntrusted process. To accomplish this, SnappyClient uses transacted hollowing (with code similar to HijackLoader)\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 21 of 25\n\nto inject a payload, which retrieves Chromium's AES_256 master key. Therefore, SnappyClient can exfiltrate\r\nbrowser data from Chromium-based browsers.\r\nPost-infection activities\r\nTo identify SnappyClient’s goal, ThreatLabz decrypted the malware’s network communications. The activity\r\nindicates a financial motive, with cryptocurrency theft as the primary goal. Below is a list of events and software\r\nthe malware registers.\r\nRegistered events\r\nIf the clipboard content matches the regex  ^0x[a-fA-F0-9]{40}$ (an Ethereum wallet address), perform\r\naction 384 (takes a screenshot and sends it to the C2).\r\nIf the window title matches the regex  (binance|coinbase|exodus \\d{1,2}\\.|atomic wallet) , perform\r\naction 384 (takes a screenshot and sends it to the C2). \r\nRegistered software\r\nBrowsers: 360Browser, Opera, Chrome, CocCoc, Edge, Firefox, Slimjet, Vivaldi, Waterfox, and Brave.\r\nExtensions: Coinbase, Metamask, Phantom, TronLink, and TrustWallet.\r\nOther applications: Atomic, BitcoinCore, Coinomi, Electrum, Exodus, LedgerLive, TrezorSuite, and\r\nWasabi.\r\nPotential ties to HijackLoader\r\nThreatLabz observed potential links between HijackLoader and SnappyClient. HijackLoader is commonly used in\r\neCrime campaigns. The code similarities we identified include the following:\r\nAPI structure layout: SnappyClient’s API structure closely matches HijackLoader’s, with an almost one-to-one mapping. It also includes placeholder (empty) DWORD values for APIs that SnappyClient does not\r\nuse. The figure below shows the API structure layout in IDA for both families. \r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 22 of 25\n\nFigure 3: API structure layout of HijackLoader and SnappyClient.\r\nDirect system calls and 64-bit ntdll mapping: Both HijackLoader and SnappyClient use similar code to\r\npopulate their direct-syscall structures and to map a 64-bit copy of ntdll into memory. The figure below\r\nshows the code used by both to populate the syscall structure.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 23 of 25\n\nFigure 4: Code used by HijackLoader and SnappyClient to populate a syscall structure.\r\nTransacted hollowing: Both families use similar transacted-hollowing code to inject payloads into a\r\nremote process.\r\nIn addition, across all campaigns we have observed to date, HijackLoader has been the exclusive loader used to\r\ndeploy SnappyClient. Based on these overlaps, there may be a connection between the developers of\r\nHijackLoader and SnappyClient.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 24 of 25\n\nSource: https://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient\r\nPage 25 of 25",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.zscaler.com/blogs/security-research/technical-analysis-snappyclient"
	],
	"report_names": [
		"technical-analysis-snappyclient"
	],
	"threat_actors": [
		{
			"id": "2864e40a-f233-4618-ac61-b03760a41cbb",
			"created_at": "2023-12-01T02:02:34.272108Z",
			"updated_at": "2026-04-10T02:00:04.97558Z",
			"deleted_at": null,
			"main_name": "WildCard",
			"aliases": [],
			"source_name": "ETDA:WildCard",
			"tools": [
				"RustDown",
				"SysJoker"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "9f101d9c-05ea-48b9-b6f1-168cd6d06d12",
			"created_at": "2023-01-06T13:46:39.396409Z",
			"updated_at": "2026-04-10T02:00:03.312816Z",
			"deleted_at": null,
			"main_name": "Earth Lusca",
			"aliases": [
				"CHROMIUM",
				"ControlX",
				"TAG-22",
				"BRONZE UNIVERSITY",
				"AQUATIC PANDA",
				"RedHotel",
				"Charcoal Typhoon",
				"Red Scylla",
				"Red Dev 10",
				"BountyGlad"
			],
			"source_name": "MISPGALAXY:Earth Lusca",
			"tools": [
				"RouterGod",
				"SprySOCKS",
				"ShadowPad",
				"POISONPLUG",
				"Barlaiy",
				"Spyder",
				"FunnySwitch"
			],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "8941e146-3e7f-4b4e-9b66-c2da052ee6df",
			"created_at": "2023-01-06T13:46:38.402513Z",
			"updated_at": "2026-04-10T02:00:02.959797Z",
			"deleted_at": null,
			"main_name": "Sandworm",
			"aliases": [
				"IRIDIUM",
				"Blue Echidna",
				"VOODOO BEAR",
				"FROZENBARENTS",
				"UAC-0113",
				"Seashell Blizzard",
				"UAC-0082",
				"APT44",
				"Quedagh",
				"TEMP.Noble",
				"IRON VIKING",
				"G0034",
				"ELECTRUM",
				"TeleBots"
			],
			"source_name": "MISPGALAXY:Sandworm",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "3a0be4ff-9074-4efd-98e4-47c6a62b14ad",
			"created_at": "2022-10-25T16:07:23.590051Z",
			"updated_at": "2026-04-10T02:00:04.679488Z",
			"deleted_at": null,
			"main_name": "Energetic Bear",
			"aliases": [
				"ATK 6",
				"Blue Kraken",
				"Crouching Yeti",
				"Dragonfly",
				"Electrum",
				"Energetic Bear",
				"G0035",
				"Ghost Blizzard",
				"Group 24",
				"ITG15",
				"Iron Liberty",
				"Koala Team",
				"TG-4192"
			],
			"source_name": "ETDA:Energetic Bear",
			"tools": [
				"Backdoor.Oldrea",
				"CRASHOVERRIDE",
				"Commix",
				"CrackMapExec",
				"CrashOverride",
				"Dirsearch",
				"Dorshel",
				"Fertger",
				"Fuerboos",
				"Goodor",
				"Havex",
				"Havex RAT",
				"Hello EK",
				"Heriplor",
				"Impacket",
				"Industroyer",
				"Karagany",
				"Karagny",
				"LightsOut 2.0",
				"LightsOut EK",
				"Listrix",
				"Oldrea",
				"PEACEPIPE",
				"PHPMailer",
				"PsExec",
				"SMBTrap",
				"Subbrute",
				"Sublist3r",
				"Sysmain",
				"Trojan.Karagany",
				"WSO",
				"Webshell by Orb",
				"Win32/Industroyer",
				"Wpscan",
				"nmap",
				"sqlmap",
				"xFrost"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "a66438a8-ebf6-4397-9ad5-ed07f93330aa",
			"created_at": "2022-10-25T16:47:55.919702Z",
			"updated_at": "2026-04-10T02:00:03.618194Z",
			"deleted_at": null,
			"main_name": "IRON VIKING",
			"aliases": [
				"APT44 ",
				"ATK14 ",
				"BlackEnergy Group",
				"Blue Echidna ",
				"CTG-7263 ",
				"ELECTRUM ",
				"FROZENBARENTS ",
				"Hades/OlympicDestroyer ",
				"IRIDIUM ",
				"Qudedagh ",
				"Sandworm Team ",
				"Seashell Blizzard ",
				"TEMP.Noble ",
				"Telebots ",
				"Voodoo Bear "
			],
			"source_name": "Secureworks:IRON VIKING",
			"tools": [
				"BadRabbit",
				"BlackEnergy",
				"GCat",
				"NotPetya",
				"PSCrypt",
				"TeleBot",
				"TeleDoor",
				"xData"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "18a7b52d-a1cd-43a3-8982-7324e3e676b7",
			"created_at": "2025-08-07T02:03:24.688416Z",
			"updated_at": "2026-04-10T02:00:03.734754Z",
			"deleted_at": null,
			"main_name": "BRONZE UNIVERSITY",
			"aliases": [
				"Aquatic Panda",
				"Aquatic Panda ",
				"CHROMIUM",
				"CHROMIUM ",
				"Charcoal Typhoon",
				"Charcoal Typhoon ",
				"Earth Lusca",
				"Earth Lusca ",
				"FISHMONGER ",
				"Red Dev 10",
				"Red Dev 10 ",
				"Red Scylla",
				"Red Scylla ",
				"RedHotel",
				"RedHotel ",
				"Tag-22",
				"Tag-22 "
			],
			"source_name": "Secureworks:BRONZE UNIVERSITY",
			"tools": [
				"Cobalt Strike",
				"Fishmaster",
				"FunnySwitch",
				"Spyder",
				"njRAT"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "256a6a2d-e8a2-4497-b399-628a7fad4b3e",
			"created_at": "2023-11-30T02:00:07.299845Z",
			"updated_at": "2026-04-10T02:00:03.484788Z",
			"deleted_at": null,
			"main_name": "WildCard",
			"aliases": [],
			"source_name": "MISPGALAXY:WildCard",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "6abcc917-035c-4e9b-a53f-eaee636749c3",
			"created_at": "2022-10-25T16:07:23.565337Z",
			"updated_at": "2026-04-10T02:00:04.668393Z",
			"deleted_at": null,
			"main_name": "Earth Lusca",
			"aliases": [
				"Bronze University",
				"Charcoal Typhoon",
				"Chromium",
				"G1006",
				"Red Dev 10",
				"Red Scylla"
			],
			"source_name": "ETDA:Earth Lusca",
			"tools": [
				"Agentemis",
				"AntSword",
				"BIOPASS",
				"BIOPASS RAT",
				"BadPotato",
				"Behinder",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"Doraemon",
				"FRP",
				"Fast Reverse Proxy",
				"FunnySwitch",
				"HUC Port Banner Scanner",
				"KTLVdoor",
				"Mimikatz",
				"NBTscan",
				"POISONPLUG.SHADOW",
				"PipeMon",
				"RbDoor",
				"RibDoor",
				"RouterGod",
				"SAMRID",
				"ShadowPad Winnti",
				"SprySOCKS",
				"WinRAR",
				"Winnti",
				"XShellGhost",
				"cobeacon",
				"fscan",
				"lcx",
				"nbtscan"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "d53593c3-2819-4af3-bf16-0c39edc64920",
			"created_at": "2022-10-27T08:27:13.212301Z",
			"updated_at": "2026-04-10T02:00:05.272802Z",
			"deleted_at": null,
			"main_name": "Earth Lusca",
			"aliases": [
				"Earth Lusca",
				"TAG-22",
				"Charcoal Typhoon",
				"CHROMIUM",
				"ControlX"
			],
			"source_name": "MITRE:Earth Lusca",
			"tools": [
				"Mimikatz",
				"PowerSploit",
				"Tasklist",
				"certutil",
				"Cobalt Strike",
				"Winnti for Linux",
				"Nltest",
				"NBTscan",
				"ShadowPad"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "b3e954e8-8bbb-46f3-84de-d6f12dc7e1a6",
			"created_at": "2022-10-25T15:50:23.339976Z",
			"updated_at": "2026-04-10T02:00:05.27483Z",
			"deleted_at": null,
			"main_name": "Sandworm Team",
			"aliases": [
				"Sandworm Team",
				"ELECTRUM",
				"Telebots",
				"IRON VIKING",
				"BlackEnergy (Group)",
				"Quedagh",
				"Voodoo Bear",
				"IRIDIUM",
				"Seashell Blizzard",
				"FROZENBARENTS",
				"APT44"
			],
			"source_name": "MITRE:Sandworm Team",
			"tools": [
				"Bad Rabbit",
				"Mimikatz",
				"Exaramel for Linux",
				"Exaramel for Windows",
				"GreyEnergy",
				"PsExec",
				"Prestige",
				"P.A.S. Webshell",
				"AcidPour",
				"VPNFilter",
				"Neo-reGeorg",
				"Cyclops Blink",
				"SDelete",
				"Kapeka",
				"AcidRain",
				"Industroyer",
				"Industroyer2",
				"BlackEnergy",
				"Cobalt Strike",
				"NotPetya",
				"KillDisk",
				"PoshC2",
				"Impacket",
				"Invoke-PSImage",
				"Olympic Destroyer"
			],
			"source_id": "MITRE",
			"reports": null
		}
	],
	"ts_created_at": 1775434243,
	"ts_updated_at": 1775792228,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/1ce956db1b0ac0abcbd73421018a72eb41055f58.pdf",
		"text": "https://archive.orkl.eu/1ce956db1b0ac0abcbd73421018a72eb41055f58.txt",
		"img": "https://archive.orkl.eu/1ce956db1b0ac0abcbd73421018a72eb41055f58.jpg"
	}
}