{
	"id": "171d82a4-c079-4846-bd4b-de8c5c538553",
	"created_at": "2026-04-10T03:21:19.386106Z",
	"updated_at": "2026-04-10T03:22:17.143414Z",
	"deleted_at": null,
	"sha1_hash": "b3b03a38967b4084a44f96e956d8c67612dd468a",
	"title": "Technical Analysis of TransferLoader | ThreatLabz",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 758474,
	"plain_text": "Technical Analysis of TransferLoader | ThreatLabz\r\nBy ThreatLabz\r\nPublished: 2025-05-14 · Archived: 2026-04-10 02:31:57 UTC\r\nThis section describes TransferLoader’s functionality as well as payloads embedded in the malware. These\r\npayloads are likely developed by the same threat actor and share code similarities. ThreatLabz has identified\r\nTransferLoader samples with the following embedded payloads:\r\nDownloader - Downloads and executes a payload from a command-and-control (C2) server.\r\nBackdoor loader - Executes the backdoor module and manages its configuration data.\r\nBackdoor - executes commands specified by the C2.\r\nAnother embedded payload discovered in TransferLoader was the ransomware family known as Morpheus. Even\r\nthough the ransomware’s code shares a few similarities with the aforementioned payloads (e.g. string decryption\r\nroutines), the link between them is less clear. Therefore, an analysis of Morpheus has been omitted in this section. \r\nAnti-analysis\r\n \r\nAnti-VM/anti-debug\r\nTransferLoader and its payloads contain the following anti-analysis methods:\r\nThe loader retrieves its own filename and checks if it includes a specified-hardcoded substring (for\r\nexample the substring  ess ) followed by the character _ .\r\nCertain variants of the loader require two or more command-line parameters to proceed with the execution.\r\nAll components leverage the  BeingDebugged  field in the Process Environment Block (PEB)  to detect a\r\ndebugging session.\r\nWindows APIs are dynamically resolved using a hashing algorithm.\r\nThere are junk code blocks in certain functions of the loader (see figure below). The inserted code block\r\nnever executes, but this addition is enough to break the decompilation process of IDA.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 1 of 13\n\nFigure 1: Example of TransferLoader junk code block.\r\nString encryption\r\nEach TransferLoader component decrypts strings at runtime using a bitwise-XOR operation. Every string is paired\r\nwith a unique 8-byte key for decryption. The encrypted string data is pushed on the stack and decrypted, as shown\r\nin the figure below.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 2 of 13\n\nFigure 2: TransferLoader runtime string decryption example.\r\nCode obfuscation\r\nAll the components we analyzed employed code obfuscation using two different methods.\r\nObfuscation method 1\r\nThe first method reads the current address of the block and subtracts a hardcoded offset from this address. The\r\nresult is the next block address to jump/execute as shown in the figure below.\r\nFigure 3: TransferLoader obfuscated control flow.\r\nWe observed this first method only in TransferLoader (not its embedded components) and the control flow can be\r\nrecovered using the IDAPython script available in our GitHub repository.\r\nObfuscation method 2\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 3 of 13\n\nThe second method uses a more advanced approach and the developers make use of this method in the embedded\r\npayloads within TransferLoader (unlike the first method which is only used in TransferLoader itself).\r\nThe obfuscation process starts by saving all registers (including the RFLAGS and SIMD registers) and then\r\nallocating stack space for further operations/storage. Then, any local function variables and parameters are stored\r\nin the SIMD registers and the obfuscated instructions are executed. Each obfuscated instruction has its own\r\nhandler. For example, the code block in the figure below shows how the obfuscator obtains the constant value\r\n0x1000 and then stores the value in the SIMD register XMM4. The stored value is used at a later stage of the\r\nexecution as a function parameter. Furthermore, the instructions for inserting a value into a SIMD register contain\r\njunk instructions to hinder analysis. In addition, it is important to highlight that before executing a handler, the\r\nobfuscator saves the offset of the next block address to execute as soon as the handler has executed. \r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 4 of 13\n\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 5 of 13\n\nFigure 4: TransferLoader obfuscation handler.\r\nAfter executing the obfuscated handlers, the obfuscator resets the state of the registers. At this stage, any pushed\r\nvalues from the obfuscated handlers are placed into the registers.\r\nDespite the fact that the obfuscator handles a small set of instructions (e.g., the instruction  XOR REG , REG is\r\nnot handled), the threat actors can obscure the modification or assignment of local variables and code structures.\r\nANALYST NOTE: Even though the second obfuscation approach might appear to be a Virtual Machine (VM)\r\nprotection, the obfuscator does not use any kind of virtualization and/or bytecodes.\r\nTransferLoader\r\nTransferLoader is a malware loader that appears to be the first part in the execution chain of subsequent modules\r\nand components. ThreatLabz has observed different variants of TransferLoader with minor functionality\r\ndifferences.\r\nTransferLoader decrypts and executes an embedded payload with a straightforward decryption process, which is\r\nsummarized as follows:\r\n1. TransferLoader retrieves the data from the PE section with the encrypted payload. The name of this section\r\nmight vary from sample to sample. For example, we observed the section names .dbg and .secenc.\r\n2. Next, TransferLoader decrypts two strings: a 16-byte AES key and a custom Base32 charset.\r\n3. TransferLoader calculates a 1-byte key from the custom Base32 charset and adds the calculated key to each\r\nencrypted byte of the target section.\r\nout = bytearray()\r\nkey = 0xFFFF\r\nfor i in range(len(base32_charset)):\r\n if input[i]\r\n4. The resulting output from the previous operation is a Base32 string, which is then decoded using Base32 with\r\nthe custom Base32 charset.\r\n5. As a final step, TransferLoader performs a bitwise-XOR operation with the 16-byte AES key and the decoded\r\ndata. The result is then decrypted using the AES-CBC decryption algorithm (with the aforementioned AES key).\r\nDespite the simplicity of the decryption process described above, the developers have modified the AES algorithm\r\nto make the automation of the decryption process more tedious. Specifically, during the AES key expansion, the\r\ndevelopers chose to use only the first element of the round constant array ( r_con ).\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 6 of 13\n\nNote that older variants of TransferLoader use either Base32 decoding or AES decryption for the final payload,\r\nbut not both of them at the same time.\r\nDownloader\r\nThe downloader component is the most common embedded payload of TransferLoader that ThreatLabz has\r\nobserved so far. The primary goal of the downloader is to download an additional payload from a C2 server and\r\nexecute a decoy file.\r\nThe downloader sends an HTTPS GET request to the server to retrieve the payload. The HTTP request uses the\r\nfollowing HTTP headers:\r\nUser-Agent: Microsoft Edge/1.0 (not used across all samples identified by ThreatLabz).\r\nX-Custom-Header: xxx\r\nThe downloader decrypts the received payload using a bitwise-XOR operation with the hardcoded 4-byte key\r\n0xFFFFFFFF. The Python code below replicates the decryption routine.\r\nout = bytearray()\r\npayload_data = b\"\"\r\nkey = 0xffffffff\r\nfor b in payload_data:\r\n out.append((b ^ key) \u0026 0xff)\r\n key -= 1\r\nAfter executing the decrypted payload, the downloader attempts to resolve an export from the downloaded\r\npayload using its custom hashing algorithm. The DLL export should match the hash value\r\n0xF78B59DF7AED203F. If successful, the downloader will execute the resolved export function.\r\nAfter completing the operation above, the downloader opens a PDF decoy file. If the export function of the\r\ndownloaded file was executed successfully, but did not return any data, then the downloader restarts the current\r\nWindows Explorer (explorer.exe) instance. The decoy document is either embedded in the binary or the\r\ndownloader will create an invalid PDF file consisting of 8-bytes of junk data. \r\nBackdoor loader\r\nThe backdoor module has its own dedicated loader, which handles any requests for reading or modifying\r\nconfiguration data.\r\nThe backdoor loader expects to reside either in the memory space of an Explorer instance (explorer.exe) or\r\nWordPad (wordpad.exe). In the second case (WordPad), the backdoor loader performs additional actions including\r\nthe following:\r\nHooks the Windows API  ShowWindow . The hooked code enters an infinite sleep loop.\r\nDeletes the CSLID registry key SOFTWARE\\Classes\\CLSID\\{F5078F32-C551-11D3-89B9-\r\n0000F81FE221}, which is the XML DOM Document 3.0 object.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 7 of 13\n\nUses the registry key SOFTWARE\\Classes\\CLSID\\{1BAC8681-2965-4FFC-92D1-170CA4099E01},\r\nwhich represents the object  Windows.System.User.ProxyStubFactory , to add persistence on the\r\ncompromised host. This method is also known as Component Object Model (COM) hijacking.\r\nANALYST NOTE: The backdoor loader attempts to create the file  defender.user.tmp under the Windows\r\ntemporary directory. If the backdoor loader fails to do this, the execution stops. We were not able to confirm the\r\npurpose of this operation.\r\nThe backdoor loader is also used for retrieving and updating the configuration of the backdoor. The table below\r\ndescribes the configuration elements that are stored in the registry key SOFTWARE\\Microsoft\\Phone\\Config\\.\r\nName Description\r\nrmi C2 server (up to 64 bytes in length).\r\nto Timeout/sleep value.\r\nid Network encryption key (16-bytes).\r\nTable 1: TransferLoader backdoor configuration fields.\r\nFor the backdoor module to update or retrieve the configuration information, the backdoor loader creates a named\r\npipe using a hardcoded name (in our samples, the name is set to  nice ). The supported pipe commands (see the\r\ntable below) and the packet structures for incoming/outgoing packets are described below.\r\nCommand ID Description\r\n2 Retrieves the network key from the configuration.\r\n3 Retrieves the C2 server from the configuration.\r\n4 Retrieves sleep/timeout value from the configuration.\r\n5 Updates sleep/timeout value.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 8 of 13\n\nCommand ID Description\r\n6 Updates the C2 server.\r\n7 Stores a PE file in the registry under the name md and then executes it in memory.\r\n8 Self-remove. Removes any files/registry keys associated with the infection and exits.\r\nTable 2: TransferLoader pipe commands.\r\n#pragma pack(1)\r\nstruct get_data_pipe_packet\r\n{\r\nuint8_t result; // Set either to 1 (true) or 0 (false) to indicate if the requested operation was successful.\r\nvoid* command_parameter_pointer;\r\n};\r\n#pragma pack(1)\r\nstruct update_data_pipe_packet\r\n{\r\nuint8_t cmd_id;\r\nvoid* command_parameter_pointer;\r\nDWORD sizeof_command_parameter; // Used only for command ID 7.\r\n};\r\nThe pipe communication is encrypted using a bitwise-XOR operation with a 4-byte hardcoded XOR key (e.g.,\r\n0x534110E6) as shown below:\r\ninitial_key = 0x534110E6\r\nout = bytearray()\r\nfor c in data:\r\n key = initial_key + len(data)\r\n out.append((c ^ key) \u0026 0xff)\r\nAs a final step, the backdoor loader executes the backdoor from the registry key\r\nSOFTWARE\\Microsoft\\Phone\\Config\\md after decrypting it using the same XOR decryption algorithm provided\r\nabove.\r\nBackdoor\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 9 of 13\n\nThe backdoor module is the core orchestrator for receiving and executing commands from the C2 server as well as\r\nfor updating its own configuration data.\r\nDuring the initialization phase, the backdoor requests the configuration data from its loader. If the configuration\r\ndata is absent, the execution does not proceed and the malware exits. After reading and setting the configuration\r\ninformation, the backdoor attempts to start the initial communication with the malicious C2 server.\r\nInterestingly, the backdoor uses the InterPlanetary File System (IPFS) decentralized network if it is unable to\r\nestablish a successful connection with the C2 server. Specifically, the backdoor sends a request to the embedded\r\nIPFS URL and expects a new C2 server in return. Using this feature, the threat actors can redirect compromised\r\nhosts to a new C2 server in case of a server takedown.\r\nThe backdoor module supports both HTTPS and raw TCP communication methods. TCP is only used when the\r\nbackdoor cannot initialize an HTTP session with the server. As for the HTTP request headers, the backdoor uses\r\nthe user agent  My Agent/1.0 along with the custom header  X-Edge-key: none .\r\nFurthermore, the backdoor stores both the session’s information and configuration data in the following structure:\r\n#pragma pack(1)\r\nstruct info\r\n{\r\n uint8_t connection_mode;\r\n uint8_t network_key[16];\r\n uint8_t cnc[64];\r\n uint16_t port;\r\n uintptr_t connection_handle; // Socket or HTTP request handle\r\n HINTERNET session_handle;\r\n HINTERNET request_handle;\r\n};\r\nAs soon as a connection with the server is established, the backdoor requests a command. Each packet received\r\nhas its own header with a size of 43-bytes followed by command parameters with a size up to 4,058 bytes. For\r\nclarity, we provided the C structures below, which represent the network packet.\r\n#pragma pack(1)\r\nstruct packet_header\r\n{\r\n uint32_t encoded_length;\r\n uint32_t unknown;\r\n uint8_t cmd_id; // 2 for TCP, 3 for HTTP\r\n uint8_t connection_mode;\r\n uint32_t random_number;\r\n uint32_t header_checksum;\r\n uint32_t unknown_2; // Set when receiving a HTTP response\r\n uint8_t output_network_key[16];\r\n};\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 10 of 13\n\n#pragma pack(1)\r\nstruct command_network_packet\r\n{\r\n packet_header header;\r\n uint8_t command_data[4058];\r\n};\r\nBefore parsing the incoming network packet, the backdoor verifies the integrity of the packet by calculating the\r\nchecksum value of the header. The Python code below replicates the checksum algorithm.\r\nchecksum = 0xFFFFFFFF\r\nfor c in header_data:\r\n checksum ^= ( (checksum * c) ^ len(header_data)) \u0026 0xffffffff\r\nIf the packet received passes the checksum validation, then the backdoor decrypts it using a custom stream-cipher,\r\nwhich has been replicated in Python below.\r\ndef buggy_blocks_swap(data: bytes) -\u003e bytearray:\r\n data = bytearray(data)\r\n div_size = len(data) // 2\r\n for iterator in range(div_size):\r\n read_char = data[iterator]\r\n data[iterator] = data[div_size - iterator - 1]\r\n data[div_size - iterator - 1] = read_char\r\n return data\r\ndef encrypt(key: bytes, input_data: bytes) -\u003e bytearray:\r\n out = bytearray()\r\n for i in range(len(input_data)):\r\n out.append( input_data[i] ^ key[i % 16])\r\n out = buggy_blocks_swap(data=out)\r\n div_s = len(out) // 2\r\n for i in range(div_s):\r\n out[i + div_s] ^= out[i]\r\n return out\r\n \r\n \r\ndef decrypt(key: bytes, data: bytes) -\u003e bytearray:\r\n data = bytearray(data)\r\n div_s = len(data) // 2\r\n for i in range(div_s):\r\n data[i + div_s] ^= data[i]\r\n reversed_data = buggy_blocks_swap(data=data)\r\n for i in range(len(reversed_data )):\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 11 of 13\n\nreversed_data[i] = data[i] ^ key[i % 16]\r\n return reversed_data\r\nANALYST NOTE: Part of the cipher process is to swap the input data blocks. However, the implementation\r\ncontains a bug in the loop’s control variable and the data remains intact.\r\nWhen the backdoor sends a request to the server, it first encrypts the header of the packet (excluding the network\r\nkey at the end) and then the command’s parameter data (if no data is present, then the encryption is applied to null\r\nbytes). Overall, the backdoor supports the network commands described in the table below.\r\nCommand\r\nID\r\nDescription\r\n0 Do nothing.\r\n1 Executes a remote shell command and sends the output to the C2 server.\r\n2 Reads a file from the compromised host.\r\n3 Writes a file to the compromised host.\r\n4 Executes a command/file without storing any output of the operation.\r\n5 Updates the C2 server.\r\n6 Updates the timeout/sleep value of the configuration.\r\n7 Updates the network encryption key.\r\n8 Collects information about the compromised host. This includes the username, hostname,\r\nNETBIOS name, Windows version and the access rights of the current user. The\r\nrepresentative structure is:\r\nstruct host_info\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 12 of 13\n\nCommand\r\nID\r\nDescription\r\n{\r\nuint8_t username[100];\r\nuint8_t hostname[100];\r\nuint8_t netbios_name[100];\r\nuint8_t windows_version[16];\r\nuint8_t user_rights;\r\n};\r\n9 Stops execution and starts the self-remove process from the backdoor loader side.\r\nTable 3: TransferLoader backdoor network commands.\r\nAs shown in the table above, the backdoor reports any command’s output. The outgoing network packet follows\r\nthe same structure as the one provided above. The key difference is the handling of any error outputs. Specifically,\r\nthe backdoor uses the following offsets in the network packet for logging error codes and messages.\r\n0x2b - Includes an error string with a maximum size of 256 bytes.\r\n0x27 - A DWORD value, which has the error code obtained from the Windows API function GetLastError.\r\nMoreover, before sending the report packet, the backdoor applies an extra encryption layer by encrypting the\r\nentire packet again.\r\nSource: https://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-transferloader\r\nPage 13 of 13",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.zscaler.com/blogs/security-research/technical-analysis-transferloader"
	],
	"report_names": [
		"technical-analysis-transferloader"
	],
	"threat_actors": [],
	"ts_created_at": 1775791279,
	"ts_updated_at": 1775791337,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/b3b03a38967b4084a44f96e956d8c67612dd468a.pdf",
		"text": "https://archive.orkl.eu/b3b03a38967b4084a44f96e956d8c67612dd468a.txt",
		"img": "https://archive.orkl.eu/b3b03a38967b4084a44f96e956d8c67612dd468a.jpg"
	}
}