{
	"id": "d75cdbcf-3577-4173-901b-663c4981a7be",
	"created_at": "2026-04-06T01:32:21.631683Z",
	"updated_at": "2026-04-10T03:29:57.992212Z",
	"deleted_at": null,
	"sha1_hash": "c2d126d84639e3321771b89aa487a49259193212",
	"title": "Return of the Higaisa APT | Zscaler Blog",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 5148332,
	"plain_text": "Return of the Higaisa APT | Zscaler Blog\r\nBy Sudeep Singh, Atinderpal Singh\r\nPublished: 2020-06-11 · Archived: 2026-04-06 01:06:41 UTC\r\nCybercriminals will often use LNK files attached in an email to launch an attack on unsuspecting victims. And we\r\nrecently noticed another campaign using this technique.\r\nIn May 2020, we observed several LNK files in the wild, which we attribute to the same threat actor based on the\r\ncode overlap, similar tactics, techniques and procedures (TTPs) and similar backdoor. For those who are\r\nunfamiliar, an LNK file is a shortcut or \"link\" used by Windows as a reference to an original file, folder, or\r\napplication similar to an alias on the Macintosh platform.\r\nThe final backdoor, to the best of our knowledge, has not been documented before in the public domain. Recently,\r\nMalwarebytes published a blog about this attack, but the details of the backdoor were not mentioned in that blog.\r\nThis backdoor uses sophisticated and deceptive techniques, such as FakeTLS-based network communication over\r\na duplicated socket handle and a complex cryptographic key derivation routine.\r\nWe attribute this attack (with a moderate confidence level) to the South Korean advanced persistent threat (APT)\r\nactor Higaisa. The decoy files used in the two instances of the LNK attack targeted users of Chinese origin.\r\nThe infection chain used by the LNK files is very similar to the instance observed in March 2020 by Anomali. The\r\nC\u0026C network infrastructure was correlated to Higaisa APT.\r\nIn this blog, we provide a detailed description of the distribution strategy, threat attribution, shellcode, anti-analysis techniques and the final backdoor of this campaign.\r\nDistribution strategy\r\nThe LNK files used by this threat actor contain decoy files that are displayed to the user while the malicious\r\nactivities are carried out in the background. The decoy content could be an internet shortcut file (.url file\r\nextension) or a PDF file. In this section, we will describe the various themes used in this campaign.\r\nOn May 12, 2020, we discovered two LNK files that used the Zeplin platform (zeplin.io) as the decoy theme.\r\nZeplin is a collaboration platform used by developers and designers in the enterprise industry. The details of the\r\nLNK files include:\r\nMD5 hash: 45278d4ad4e0f4a891ec99283df153c3\r\nFilename: Conversations - iOS - Swipe Icons - Zeplin.lnk\r\nMD5 hash: c657e04141252e39b9fa75489f6320f5\r\nFilename: Tokbox icon - Odds and Ends - iOS - Zeplin.lnk\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 1 of 30\n\nThese LNK files contain internet shortcut files that will be opened by the web browser installed on the system.\r\nThe URLs correspond to a project as shown below:\r\nProject URL for file with MD5 hash: 45278d4ad4e0f4a891ec99283df153c3\r\nhttps://app.zeplin.io/project/5b5741802f3131c3a63057a4/screen/5b589f697e44cee37e0e61df\r\nProject URL for file with MD5 hash: c657e04141252e39b9fa75489f6320f5\r\nhttps://app.zeplin.io/project/5b5741802f3131c3a63057a4/screen/5b589f697e44cee37e0e61df\r\nIf the user is not logged into the site, apps.zeplin.io, then it will redirect the user to the login page as shown in\r\nFigure 1.\r\nFigure 1: The login page displayed by Zeplin.\r\nThe previously mentioned LNK files were present inside a RAR archive file format with the following\r\ninformation:\r\nMD5 hash of RAR archive: 2ffb817ff7ddcfa216da31f50e199df1\r\nFilename: Project link and New copyright policy.rar\r\nThe contents of the RAR archive are shown below:\r\n├── Project link and New copyright policy\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 2 of 30\n\n│   ├── All tort's projects - Web lnks\r\n│   │   ├── Conversations - iOS - Swipe Icons - Zeplin.lnk\r\n│   │   └── Tokbox icon - Odds and Ends - iOS - Zeplin.lnk\r\n│   └── Zeplin Copyright Policy.pdf\r\nThe contents of the decoy PDF are related to Zeplin’s copyright policy as shown in Figure 2.\r\nFigure 2: The decoy PDF displaying Zeplin’s copyright policy notice.\r\nOn May 30, 2020, we discovered two more LNK files, which we attribute to the same threat actor as\r\ndescribed below.\r\nMD5 hash: 4a4a223893c67b9d34392670002d58d7\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 3 of 30\n\nFilename:\r\nCurriculum Vitae_WANG LEI_Hong Kong Polytechnic University.pdf.lnk\r\nThis LNK file drops a PDF file at runtime and opens it with the default PDF viewer on the system.\r\nMD5 hash of the dropped PDF file: 4dcd2e0287e0292a1ad71cbfdf99726e\r\nFilename of decoy PDF: Curriculum Vitae_WANG LEI_Hong Kong Polytechnic University.pdf\r\nThe contents of this PDF file are shown in Figure 3.\r\nFigure 3: The decoy PDF displaying the CV of a student from Hong Kong Polytechnic University\r\nThe contents of the PDF correspond to the CV (curriculum vitae) of a student from Hong Kong Polytechnic\r\nUniversity include:\r\nMD5 hash of the dropped PDF file: 28bfed8776c0787e9da3a2004c12b09a\r\nFilename of decoy PDF: International English Language Testing System certificate.pdf\r\nThe second LNK file we observed on May 30, 2020 contained a PDF corresponding to the International English\r\nLanguage Testing System (IELTS) results of a student.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 4 of 30\n\nFigure 4: A student's IELTS examination results.\r\nLNK metadata analysis\r\nThe LNK file format contains a wealth of metadata information that can be used for attribution and correlating the\r\nfiles to a particular threat actor. While most of the metadata from the LNK files in this attack was erased, we\r\nfound the Security Identifier (SID) value preserved in the LNK files.\r\nUsing the LECmd tool, we extracted the SID value from the LNK files which are detailed in the table below:\r\nLNK file MD5 hash SID value\r\n997ab0b59d865c4bd63cc55b5e9c8b48 S-1-5-21-1624688396-48173410-756317185-1001\r\nc657e04141252e39b9fa75489f6320f5 S-1-5-21-1624688396-48173410-756317185-1001\r\n4a4a223893c67b9d34392670002d58d7 S-1-5-21-1624688396-48173410-756317185-1001\r\n45278d4ad4e0f4a891ec99283df153c3 S-1-5-21-1624688396-48173410-756317185-1001\r\nWe wrote a YARA hunting rule to discover other LNK files in the wild with the same SID value as shown below:\r\nrule ZS_LNK_SID\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 5 of 30\n\n{\r\n            strings:\r\n                                    $a = \"S-1-5-21-1624688396-48173410-756317185-1001\" wide\r\n            condition:\r\n                                    $a\r\n}\r\nThe only instances we found were the above four LNK files. So, in addition to other indicators shared between\r\nthese four LNK files, the common SID values helped us to further attribute them to the same threat actor.\r\nTechnical analysis\r\nFor the purpose of technical analysis, we will use the LNK file with MD5 hash:\r\n45278d4ad4e0f4a891ec99283df153c3.\r\nIf the Chrome browser is already installed on the machine, then the icon of the LNK file will appear to be the\r\nsame as the Chrome browser icon. This is because the IconFileName property in the LNK file is set to the path of\r\nthe Chrome browser as shown below:\r\nIconFileName - C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe\r\nThe target property of the LNK file specifies the command that will be executed at runtime as shown in Figure 5.\r\nFigure 5: The LNK command target.\r\nThis command starts the infection chain and involves multiple stages as detailed below :\r\nCopies the original LNK file to the temporary directory in the location: %temp%\\g4ZokyumB2DC.tmp\r\nIterates over the files in the C:\\Windows\\System32 directory to search for certutil.exe\r\nCopies certutil.exe to %temp%\\gosia.exe\r\nUses findstr.exe to search for the marker “TVNDRgA” inside the original LNK file.\r\nUsing the market, a base64 encoded blob is extracted to the temporary file: %temp%\\cSi1rouy.tmp\r\nUses certutil.exe to decode the base64 encoded blob to the file: %temp%\\o423DFDS.tmp\r\nThe resulting decoded file has the CAB file format.\r\nUses expand.exe to extract the contents of the CAB file to the %temp% directory.\r\nThe components of the cab file are shown in Figure 6.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 6 of 30\n\nFigure 6: The CAB file contents.\r\nHere is a brief description of each component of the CAB file. They are described in more details later in the blog.\r\n3t54dE3r.tmp – Contains the shellcode that will be loaded and executed at runtime.\r\n34fDFkfSD32.js – The JavaScript that is used to initiate the infection chain after extraction of CAB file contents.\r\nConversations - iOS - Swipe Icons – Zeplin.url – This is the internet shortcut file that will be used to open the\r\nURL: https://app.zeplin.io/project/5b5741802f3131c3a63057a4/screen/5b589f697e44cee37e0e61df with Chrome\r\nbrowser on the machine.\r\nSvchast.exe – This is the shellcode loader binary that spoofs the name of a legitimate Windows binary called\r\nsvchost.exe. Other details include:\r\nThe LNK file will open the internet shortcut file (which opens by default with the web browser and loads\r\nthe URL).\r\nIt copies the CAB file component, 3t54dE3r.tmp to the location: C:\\Users\\Public\\Downloads\\3t54dE3r.tmp\r\nIt uses wscript.exe to execute the JavaScript file: 34fDFkfSD32.js\r\nJavaScript file analysis\r\nMD5 hash of the JavaScript file: a140420e12b68c872fe687967ac5ddbe\r\nThe contents of the JavaScript are shown in Figure 7.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 7 of 30\n\nFigure 7: The JavaScript file contents\r\nBelow are the main operations performed by this JavaScript file.\r\nIt runs the ipconfig command to gather information about the machine's network adapter configuration. It\r\nthen redirects the results of this command to the file: C:\\\\Users\\\\Public\\\\Downloads\\\\d3reEW.txt\r\nIt copies svchast.exe to the Startup directory in the location: %AppData%\\\\Microsoft\\\\Windows\\\\Start\r\nMenu\\\\Programs\\\\Startup\\\\officeupdate.exe for persistence:\r\nIt copies svchast.exe to the location: C:\\\\Users\\\\Public\\\\Downloads\\\\officeupdate.exe\r\nIt uses schtasks.exe to create a scheduled task with the name: “Driver Bootser Update” which will be used\r\nto execute the officeupdate.exe binary\r\nIt executes svchast.exe binary.\r\nIt sends an HTTP POST request to the URL: hxxp://zeplin.atwebpages.com/inter.php and exfiltrates the\r\nipconfig output gathered from the machine.\r\nShellcode loader analysis\r\nMD5 hash: a29408dbedf1e5071993dca4a9266f5c\r\nFilename: svchast.exe\r\nThe file svchast.exe is used to load the shellcode stored in the file 66DF3DFG.tmp in the path:\r\nC:\\Users\\Public\\Downloads\\66DF3DFG.tmp\r\nThis path is hardcoded in the loader.\r\nThe shellcode is loaded using the following steps:\r\n1. It reads the contents of the file, “C:\\Users\\Public\\Downloads\\66DF3DFG.tmp” into a newly allocated\r\nmemory region marked with PAGE_EXECUTE_READWRITE permission.\r\n2. It transfers the control to this memory region to start the execution of the shellcode.\r\nShellcode analysis\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 8 of 30\n\nIn this section, we have detailed the interesting code sections of the shellcode.\r\nAnti-debugging technique\r\nThe shellcode uses an anti-debugging technique to calculate a 32-bit hash of the code section. This is done to\r\ndetect the presence of any software breakpoints or tampering of code done for the purpose of reverse engineering.\r\nWhen a software breakpoint is added in the debugger, a byte with the value 0xCC is added by the debugger in\r\nplace of the original operation code (opcode). As a result of this, the hash calculation is corrupted.\r\nSuch anti-debugging techniques can be easily bypassed by using hardware breakpoints instead of software\r\nbreakpoints.\r\nAs an example, let us set a software breakpoint at the comparison instruction right after hash calculation and\r\ncheck the resulting hash calculated (shown in Figure 8).\r\nFigure 8: The software breakpoint detection by anti-debugging techniques in the shellcode.\r\nAs can be seen in Figure 8, due to the software breakpoint, the computed hash was corrupted. Because of this, the\r\ncode can detect the presence of a debugger. The shellcode will exit the execution if it detects a debugger.\r\nHowever, if we set a hardware breakpoint, the computed hash will be correct as shown in Figure 9.\r\nFigure 9: The hardware breakpoint bypasses the anti-debugging technique in the shellcode.\r\nWe re-wrote the algorithm used by the shellcode to calculate the hash of the code section in Python and it can be\r\nfound in Appendix I.\r\nDecryption of data in the buffer\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 9 of 30\n\nThe shellcode uses a 16-byte XOR key for decrypting the data as shown in Figure 10.\r\nFigure 10: Decryption of the data in the buffer. XOR decryption used to decrypt the strings.\r\nThe 16-byte XOR key used for decryption is:\r\nkey = [0xE4, 0xFD, 0x23, 0x99, 0xA3, 0xE1, 0xD3, 0x58, 0xA6, 0xCC, 0xDB, 0xE8, 0xF2, 0x91, 0xD2, 0xF8]\r\nWe re-wrote the decryption code in Python and can been seen in Appendix II.\r\nSince we believe this to be a new backdoor, we have shared the complete list of decrypted strings in Appendix IV\r\nfor reference.\r\nKey generation routine\r\nIn the first thread created by the shellcode, it generates a cryptographic session key that will be transmitted later to\r\nthe C\u0026C server to protect the communication channel between the bot and the server.\r\nIn this section, we detail the key generation routine.\r\nThere are multiple parts that are concatenated together to form the final key.\r\nPart 1:\r\nIt calls UUIDCreate() API to generate a UUID.\r\nIt uses the format string: “%08X....-%04X...-%0llX” to format the UUID using sprintf().\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 10 of 30\n\nExample UUID: DB7C6235-FD1A-45B6-224F868\r\nPart 2:\r\nIt calls UUIDCreate() to generate a 16-byte UUID.\r\nThe last byte of the UUID is used to generate a byte that will be used to perform the ROR operation later.\r\nIt uses an ROR and ADD instruction-based algorithm to compute a 32-bit hash that will be appended to\r\nfirst two steps (listed above). The algorithm used to compute the 32-bit hash in this case is similar to the\r\none used in the anti-debugging section. This algorithm has been re-written in Python and can be found in\r\nAppendix I.\r\nFormat:\r\nuuid2 = [] [ROR byte 0x00 0x00 0x00] [32-bit hash]\r\nIt uses CryptBinaryToStringA() to generate Base64 encoded data using UUID2.\r\nPart 3:\r\nIt uses Windows Crypto APIs to generate an MD5 hash using UUID1 (from Part 1). Before the hash is\r\ncalculated, the length of the UUID is extended to 0x48 bytes by padding with null bytes. This can be re-written in Python as:\r\ndata = uuid1 + “\\x00” * (0x48 - len(uuid1))\r\nmd5 = hashlib.md5()\r\nmd5.update(data)\r\nhash1 = md5.hexdigest()\r\nIt calculates an MD5 hash of the above-generated hash once again.\r\nhash2 = md5(hash1)\r\nIt uses CryptDeriveKey() to derive a 128-bit AES key.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 11 of 30\n\nFigure 11: The cryptographic session key derivation routine.\r\nIt appends hash2 with null bytes to extend the length to 0x48 bytes and then encrypts it using the AES-128\r\nbit key derived in step 3 above. The encrypted hash is used to derive the AES key for encryption.\r\nAll these parts are concatenated together before transmitting to the C\u0026C server for registering the AES key for\r\nencrypted communication.\r\nInitialization of a TLS session\r\nAfter decrypting the C\u0026C server address, the shellcode proceeds to send an HTTP GET request to fetch the\r\nresource: “msdn.cpp” on the server.\r\nWinHTTPSetOption() is used to set the WINHTTP_OPTION_SECURITY_FLAGS value to 0x3300, which\r\nallows it to ignore any certificate errors that might occur at the time of the request.\r\nFigure 12 shows that the content-length request header field in the HTTP GET request is set to: 0xffffffff manually\r\nat the time of invoking the WinHTTPSendRequest.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 12 of 30\n\nFigure 12: The initial request sent to the C\u0026C server for deception purposes to make it look like a TLS session\r\nThe HTTP GET request looks like:\r\nGET hxxps://45.76.6[.]149/msdn.cpp HTTP/1.1\r\nConnection: Keep-Alive\r\nUser-Agent: WinHTTP/1.1\r\nContent-Length: 4294967295 Host: 45.76.6[.]149\r\nThis HTTP GET request was sent for deception purposes to make it look like a valid TLS session. As we will see\r\nlater, a FakeTLS session is used by the shellcode to perform C\u0026C communication with the server.\r\n \r\nDuplication of socket - ShadowMove similarity\r\nWe discovered an interesting code section in this shellcode which creates a duplicate socket to connect to the C2\r\nserver. The method is very similar to the ShadowMove lateral movement technique which was presented in\r\nUsenix 2020.\r\nAt first glance, due to the high level of code overlap in this shellcode with the above technique, we believed it to\r\nbe using the ShadowMove lateral movement technique. However on further inspection, we concluded that this\r\ntechnique was used to create a duplicate socket that will be used for FakeTLS communication as described in the\r\nnext section.\r\nBelow are the details of the steps used by the shellcode to create a duplicate socket used for communication with\r\nthe C2 server:\r\nIt calls the NtQuerySystemInformation() native API with the InfoClass parameter set to:\r\nSystemExtendedHandleInformation (0x40). This fetches detailed information for all the handles and their\r\ncorresponding object names.\r\nThe information is returned in the form of a SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX structure.\r\nIt uses a GetCurrentProcessID to find the process ID of the current process.\r\nIt compares the UniqueProcessID member of the SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX\r\nstructure with the current process ID. If they are equal, then it proceeds to the next step.\r\nIt compares the HandleValue member of the SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX structure\r\nwith the socket handle. If they are equal, then it proceeds to the next step.\r\nIt creates a new thread that calls the native API, NtQueryObject() to retrieve information about the object.\r\nThe information is returned in the structure: __PUBLIC_OBJECT_TYPE_INFORMATION.\r\nIf the TypeName member of the structure __PUBLIC_OBJECT_TYPE_INFORMATION is equal to\r\n“\\Device\\Afd”, then it proceeds to the next step. It is important to note that Windows sockets have the\r\nobject type “\\Device\\Afd”.\r\nIt calls getpeername() to get the IP address and port number corresponding to the above socket.\r\nIt compares the IP address and port number with the expected values corresponding to the C\u0026C server.\r\nIf the correct socket is found, then it calls DuplicateHandle() to duplicate this socket.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 13 of 30\n\nFigure 13 shows the code section that locates the socket handle.\r\nFigure 13: The subroutine that is used to iterate over system handles.\r\nFigure 14 shows the code section that checks if the socket handle corresponds to the socket used to communicate\r\nwith the C\u0026C server.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 14 of 30\n\nFigure 14: The subroutine that used to locate the target socket handle used to communicate with the C\u0026C server.\r\nFakeTLS\r\nWe observed interesting use of the FakeTLS method in this shellcode. It creates a FakeTLS header using the byte\r\nsequence: [0x17 0x03 0x01] as shown in Figure 15.\r\nFigure 15: The subroutine used to craft the FakeTLS header.\r\nIt is important to note that this FakeTLS method has been used in the past by APT groups, such as Lazarus.\r\nThe reason for using this technique is to confuse network monitoring security systems that do not perform proper\r\nSSL inspection and, as a result, allow the traffic to pass through.\r\nAlso, we noticed two requests sent by the bot using the FakeTLS header in the initialization phase.\r\nRequest 1 [Fake session key]\r\nIn the first request, the routine:\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 15 of 30\n\nUses time() to get the current time.\r\nUses srand() to seed the pseudo-random number generator using the value obtained in step 1.\r\nUses rand() to generate a random number.\r\nGenerates a total of 0xC3 random bytes using the above method.\r\nAppends a total of 0x3C bytes with the value 0xAD to the data generated in step 4.\r\nSo a total of 0xFF bytes are generated in the format: [0xC3 bytes of random data][0x3C bytes with value 0xAD].\r\nThis data is appended to the FakeTLS header and sent using ws2_32.send() to the C\u0026C server as shown in Figure\r\n16.\r\nFigure 16: The FakeTLS packet appended with random data.\r\nIt is important to note that this memory chunk is freed using VirtualFree() after sending it in a request to the C\u0026C\r\nserver. So we do not believe this was used as a session key because, in that case, the bot would have to preserve\r\nthe key somewhere.\r\nRequest 2 [Real session key]\r\nIn the second instance of the request sent to the C\u0026C server, we noticed the FakeTLS header appended with the\r\ncryptographic session key generated earlier as shown in Figure 17.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 16 of 30\n\nFigure 17: FakeTLS header appended with cryptographic session key.\r\nThe data appended to the FakeTLS header has the following format:\r\n[command padded to 4 bytes][size padded to 4 bytes][base64-encoded data from Part2][Hash2 - padded to 0x48\r\nbytes][AES-128 bit Encrypted Key].\r\nBelow is an example of a packet with the FakeTLS Header and the data appended after it. The structure of the\r\npacket is detailed in Figure 18.\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 17 of 30\n\nFigure 18: The packet structure containing the FakeTLS header and custom format used for C\u0026C communication.\r\nOther messages contain encrypted data right after the TLS header.\r\nC\u0026C communication\r\nThe shellcode creates two more threads that work together to handle the commands exchanged between the\r\nbackdoor and the C\u0026C server.\r\nBelow are the main steps used by the C\u0026C command handler:\r\nIT creates a dispatch thread that will handle the commands posted to it by the worker thread.\r\nThe dispatch thread creates a message queue using the PeekMessageW() API.\r\nThe worker thread sends the message ID along with the command buffer to the message queue using\r\nPostThreadMessageW() API.\r\nOnce a message is posted to the dispatch thread by the worker thread, it is retrieved using the\r\nGetMessageW() API. This message will be dispatched to the appropriate command handler based on the\r\nID of the message as detailed below.\r\nThere are two sets of command IDs. One of them corresponds to commands from client to server and the other set\r\ncorresponds to commands from server to client. Corresponding to each command, there is a size of the command.\r\nAs an example,\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 18 of 30\n\nClient to server: The command ID 0x65 corresponds to the backdoor registering the system ID (calculated using\r\nUUID) with the C\u0026C server and the cryptographic session key as shown in Figure 18 above.\r\nServer to client: The command ID 0x64 is used to receive the encryption key that will be used by the client to\r\nencrypt the data sent to the server.\r\nAt the time of analysis, since the C2 server was not responding, we cannot conclusively determine the commands\r\nthat were supported by this backdoor.\r\nZscaler Cloud Sandbox detection\r\nFigure 19 shows the Zscaler Cloud Sandbox successfully detecting this LNK-based threat.\r\nFigure 19: The Zscaler Cloud Sandbox detection.\r\nIn addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators at\r\nvarious levels:\r\nLNK.Dropper.Higaisa\r\nConclusion\r\nThis new instance of attack from the Higaisa APT group shows that they are actively updating their tactics,\r\ntechniques and procedures (TTPs) and incorporating new backdoors with evasion techniques. The network\r\ncommunication protocol between the backdoor and the C\u0026C server is deceptive and complex, which was\r\ndesigned to evade network security solutions.\r\nUsers are advised to take extra precaution while opening LNK files sent inside email attachments. LNK files can\r\nhave the file icon of legitimate applications, such as Web browsers or PDF reader applications, so the source of the\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 19 of 30\n\nfiles should be verified before opening them.\r\nThe Zscaler ThreatLabZ team will continue to monitor this campaign, as well as others, to help keep our\r\ncustomers safe.\r\nMITRE ATT\u0026CK TTP Mapping\r\nTactic Technique\r\nT1193 - Spearphishing Attachment\r\nLNK files delivered inside RAR archives as an email\r\nattachment\r\nT1059 - Command-Line Interface Commands run using cmd.exe to extract and run payload\r\nT1204 - User Execution LNK file is executed by user double click\r\nT1064 - Scripting Use of Visual Basic scripts\r\nT1060 - Registry Run Keys / Startup\r\nFolder\r\nCopies executable to the startup folder for persistence\r\nT1053 - Scheduled Task\r\nCreates scheduled task named “Driver Bootser Update” for\r\npersistence\r\nT1027 - Obfuscated Files or Information\r\nParts of shellcode and its configuration is encrypted using\r\nXOR encryption algorithm\r\nT1140 - Deobfuscate/Decode Files or\r\nInformation\r\nDecodes configuration at runtime\r\nT1036 - Masquerading\r\nMasquerades as legitimate documents, has embedded decoy\r\ndocuments\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 20 of 30\n\nT1033 - System Owner/User Discovery Discovers username using GetUserNameA\r\nT1016 - System Network Configuration\r\nDiscovery\r\nDiscovers network configuration using GetAdaptersInfoA\r\nT1082 - System Information Discovery\r\nDiscovers various information about system i.e. username,\r\ncomputername, os version, etc\r\nT1094 - Custom Command and Control\r\nProtocol\r\nUses custom protocol mimicking TLS communication\r\nT1043 - Commonly Used Port Uses port 443\r\nT1090 - Connection Proxy Discovers system proxy settings and uses if available\r\nT1008 - Fallback Channels Has code to communicate over UDP in addition to TCP\r\nT1132 - Data Encoding Uses base64 for encoding UUID\r\nT1032 - Standard Cryptographic Protocol Uses AES-128 to encrypt network communications\r\nT1095 - Standard Non-Application Layer\r\nProtocol\r\nCommunicates over TCP\r\nT1002 - Data Compressed Can use LZNT1 compression\r\nT1022 - Data Encrypted Uses AES-128 for data encryption\r\nT1020 - Automated Exfiltration\r\nAutomatically sends system information to  CnC based on\r\nconfiguration and CnC commands\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 21 of 30\n\nT1041 - Exfiltration Over Command and\r\nControl Channel\r\nSends data over its CnC channel\r\nIndicators of Compromise (IOCs)\r\nLNK file MD5 hashes\r\n21a51a834372ab11fba72fb865d6830e\r\naa67b7141327c0fad9881597c76282c0\r\nc657e04141252e39b9fa75489f6320f5\r\n45278d4ad4e0f4a891ec99283df153c3\r\n997ab0b59d865c4bd63cc55b5e9c8b48\r\n4a4a223893c67b9d34392670002d58d7\r\nLNK file names\r\nInternational English Language Testing System certificate.pdf.lnk\r\nTokbox icon - Odds and Ends - iOS - Zeplin.lnk\r\n20200308-sitrep-48-covid-19.pdf.lnk\r\nCurriculum Vitae_WANG LEI_Hong Kong Polytechnic University.pdf.lnk\r\nConversations - iOS - Swipe Icons - Zeplin.lnk\r\nHTTP POST requests to register the bot\r\nhxxp://sixindent[.]epizy[.]com/inter.php\r\nhxxp://goodhk[.]azurewebsites[.]net/inter.php\r\nhxxp://zeplin[.]atwebpages[.]com/inter.php\r\nHTTP GET request to C\u0026C server\r\nhxxps://comcleanner[.]info/msdn.cpp\r\nhxxps://45[.]76[.]6[.]149/msdn.cpp\r\nAppendix I\r\nAnti-debugging hash computation\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 22 of 30\n\n# Hash of code section before decryption should be equal to 0x733C7595\r\n# Hash of code section after decryption should be equal to 0x6621A914\r\n# read the shellcode contents\r\ncontents = open(“shellcode.bin”, “rb”).read()\r\n# x86 ROR instruction re-written in Python\r\nror = lambda val, r_bits, max_bits: \\\r\n            ((val \u0026 (2**max_bits-1)) \u003e\u003e r_bits%max_bits) | \\\r\n            (val\r\n# x86 movsx instruction re-written in Python\r\ndef SIGNEXT(x, b):\r\n            m = 1\r\n            x = x \u0026 ((1\r\n            return (x ^ m) - m\r\n# limit = length of code section used for hash calculation\r\n# First 0xcb06 bytes are used to calculate the hash\r\nfor i in range(0xcb06):\r\n            result = ror(result, 0xa, 32)\r\n            t = SIGNEXT(ord(contents[i]), 8) \u0026 0xffffffff\r\n            result += t\r\n            result = result \u0026 0xffffffff\r\nprint “final hash is: %x” %(result)\r\nAppendix II\r\nXOR decryption code to extract plaintext strings and C\u0026C server address\r\nimport binascii, struct, sys\r\n# read the contents of shellcode\r\ncontents = open(sys.argv[1], \"rb\").read()\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 23 of 30\n\n# XOR decrypt the strings\r\ndef decrypt_data(encrypted, key):\r\ndecrypt = \"\"\r\nfor i in range(len(encrypted)):\r\ndb = encrypted[i]\r\nkb = key[i % len(key)]\r\nif(type(kb) == type(\"\")):\r\nkb = ord(kb)\r\nif(type(db) == type(\"\")):\r\ndb = ord(db)\r\ndecrypt += chr(db ^ kb)\r\nreturn decrypt\r\ndef extract_c2(contents):\r\nkey = contents[0xcb0e:0xcb1e]\r\nencrypted = contents[0xcb1e:]\r\ndecrypt = \"\"\r\ndecrypt = decrypt_data(encrypted, key)\r\nreturn \"{}:{}\".format(decrypt[432:].split(\"\\x00\")[0],struct.unpack(\"\r\nprint(\"==C2 Server==\\n{}\\n\".format(extract_c2(contents)))\r\n# Encrypted data is present at offset, 0xacc0 and has a total length of 0x12b0\r\nencrypted = contents[0xacc0:0xacc0+0x12b0]\r\n#16-byte XOR key\r\nkey = [0xE4, 0xFD, 0x23, 0x99, 0xA3, 0xE1, 0xD3, 0x58, 0xA6, 0xCC, 0xDB, 0xE8, 0xF2, 0x91, 0xD2, 0xF8]\r\nprint(\"==Strings==\")\r\nfor item in decrypt_data(encrypted, key).split(\"\\x00\"):\r\nif item:\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 24 of 30\n\nprint(item)\r\nAppendix III\r\nScript to generate AES key message\r\nfrom wincrypto import CryptCreateHash, CryptHashData, CryptDeriveKey, CryptEncrypt, CryptImportKey,\r\nCryptExportKey,  CryptGetHashParam, CryptDecrypt\r\nfrom wincrypto.constants import CALG_SHA1, CALG_AES_256, bType_SIMPLEBLOB, CALG_AES_128,\r\nCALG_MD5\r\nimport binascii, base64, struct, uuid\r\n###  Hash functions ###\r\nror = lambda val, r_bits, max_bits: \\\r\n   ((val \u0026 (2**max_bits-1)) \u003e\u003e r_bits%max_bits) | \\\r\n   (val\r\n# x86 movsx instruction re-written in Python\r\ndef SIGNEXT(x, b):\r\n   m = 1\r\n   x = x \u0026 ((1\r\n   return (x ^ m) - m\r\ndef get_hash(uuid1):\r\n   result = 0\r\n   for i in range(len(uuid1)):\r\n       result = ror(result, 0xa, 32)\r\n       t = SIGNEXT(uuid1[i], 8) \u0026 0xffffffff\r\n       result += t\r\n       result = result \u0026 0xffffffff\r\n   return result\r\n### UUID convert from bytes to base64 ###\r\nuuid0 = uuid.uuid4().bytes\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 25 of 30\n\nuuid0_wh = uuid0 + b\"\\x00\\x00\\x00\"  + struct.pack(\"\r\nuuid0_enc = base64.b64encode(uuid0_wh) + b\"\\x0d\\x0a\" #append \"\\r\\n\" added by windows API\r\n### Derive key from UUID ####\r\n#Generate uuid\r\nuuid1 = str(uuid.uuid4())\r\n#Append NULL bytes to make length equal to 0x48\r\ndata = uuid1 + (b\"\\x00\" * (0x48 - len(uuid1)))\r\n#Generate MD5 hash\r\nhasher = CryptCreateHash(CALG_MD5)\r\nCryptHashData(hasher, data)\r\nuuid1_md5 = CryptGetHashParam(hasher,0x2)\r\n#Append NULL bytes to md5 and again generate md5 hash to make length equal to 0x48\r\nuuid1_md5_md5 = uuid1_md5 + (b\"\\x00\" * (0x48 - len(uuid1_md5)))\r\nhasher = CryptCreateHash(CALG_MD5)\r\nCryptHashData(hasher, uuid1_md5_md5)\r\n#Derive AES key\r\naes_key = CryptDeriveKey(hasher, CALG_AES_128)\r\n#Encrypt Send MD5 hash using AES\r\nencrypted_hash = CryptEncrypt(aes_key, uuid1_md5_md5)\r\n#append more NULL bytes to Encrypted hash to make length 0x90\r\nencrypted_hash_padded = encrypted_hash + (b\"\\x00\" * (0x90 - len(encrypted_hash)))\r\n#Again use encrypted hash to calculate its md5 and derive new AES key\r\nhasher = CryptCreateHash(CALG_MD5)\r\nCryptHashData(hasher, encrypted_hash_padded)\r\naes_key = CryptDeriveKey(hasher, CALG_AES_128)\r\n#generate message buffer to send to server to register key\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 26 of 30\n\nfake_tls_header =  b\"\\x17\\x03\\x01\"\r\nclient_key_message_header = b\"\\x65\\x00\\x00\\x00\\xd8\\x00\\x00\\x00\"\r\nbuffer = client_key_message_header +  uuid0_enc + b\"\\x00\\x00\" + uuid1_md5_md5 + encrypted_hash_padded\r\nbuffer = fake_tls_header + struct.pack(\"\u003eh\", len(buffer))  + buffer\r\nbinascii.hexlify(buffer)\r\nlen(buffer)\r\nAppendix IV\r\nDecrypted strings from the shellcode\r\nhttps://www.google.com\r\nWinHTTP /1.1\r\nGET /msdn.cpp\r\n\\Device\\Afd\r\nhttps://msdn.microsoft.com\r\nhttps://github.com\r\nhttps://www.google.com\r\nhttps://\r\njsproxy.dll\r\nInternetInitializeAutoProxyDll\r\nInternetDeInitializeAutoProxyDllInternetGetProxyInfo\r\nDIRECT\r\nszFmt:%dszS:%s\r\nszWS:%ws\r\nszD:%d\r\nszP:%p\r\nszX:%x\r\nszN:%d\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 27 of 30\n\nInit Error:%d\r\nconnect\r\n_CbConnect Over\r\nikcp_udp\r\nrecv in\r\nUninstall module:%d\r\nInitModule:%d\r\nContentLength :%d\r\nszHttpRecv :%d\r\n10.0.0.49\r\nszTunnel\r\nProxip:%s\r\nProxport:%d\r\nCurProxIp:%s\r\nCurProxPort:%d\r\nIeProxy ip:%s\r\nport:%d\r\ntype:%d\r\nProxyNumber:%d\r\nGET\r\nPOST\r\nhttp://%s/../...\r\n%s..%d\r\n200 OK\r\nHost:\r\nContent-Length:\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 28 of 30\n\nConnection: Keep-Alive\r\nHTTP/1.0\r\nHTTP/1.1Authorization: Basic\r\nDELETE\r\nnews\r\nQUERY\r\nSUBMIT\r\nen-us/msdn\r\nlibrary\r\n?hl=en-US\r\n?wd=http\r\n?lan=ja-jp\r\n10.0.0.208\r\ncbreover\r\ndispatch\r\nAppendix V\r\nStructure of packet containing AES key\r\nstruct Packet {\r\n            struct FakeTls {\r\n            struct AppDataHeader{\r\n                                     byte tls_header_app_data_constant;\r\n             byte tls_version_major;\r\n                                     byte tls_version_minor;\r\n            } tls_app_data_header  ;\r\n                        ushort  PacketSize;\r\n            } FakeTlsHeader ;\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 29 of 30\n\nstruct PacketData {\r\n                        int  Command ; //(0x65 Client to Server 0x64 Server to Client) AES key\r\n                        int  DataSize ;\r\n                        char SystemId[0x22];\r\n                        char Padding[2];\r\n                        byte data[DataSize] ;\r\n            } command ;\r\n} packet;\r\nSource: https://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nhttps://www.zscaler.com/blogs/security-research/return-higaisa-apt\r\nPage 30 of 30",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://www.zscaler.com/blogs/security-research/return-higaisa-apt"
	],
	"report_names": [
		"return-higaisa-apt"
	],
	"threat_actors": [
		{
			"id": "873919c0-bc6a-4c19-b18d-c107e4aa3d20",
			"created_at": "2023-01-06T13:46:39.138138Z",
			"updated_at": "2026-04-10T02:00:03.227223Z",
			"deleted_at": null,
			"main_name": "Higaisa",
			"aliases": [],
			"source_name": "MISPGALAXY:Higaisa",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "30c9c492-afc6-4aa1-8fe6-cecffed946e0",
			"created_at": "2022-10-25T15:50:23.400822Z",
			"updated_at": "2026-04-10T02:00:05.350302Z",
			"deleted_at": null,
			"main_name": "Higaisa",
			"aliases": [
				"Higaisa"
			],
			"source_name": "MITRE:Higaisa",
			"tools": [
				"PlugX",
				"certutil",
				"gh0st RAT"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "c0cedde3-5a9b-430f-9b77-e6568307205e",
			"created_at": "2022-10-25T16:07:23.528994Z",
			"updated_at": "2026-04-10T02:00:04.642473Z",
			"deleted_at": null,
			"main_name": "DarkHotel",
			"aliases": [
				"APT-C-06",
				"ATK 52",
				"CTG-1948",
				"Dubnium",
				"Fallout Team",
				"G0012",
				"G0126",
				"Higaisa",
				"Luder",
				"Operation DarkHotel",
				"Operation Daybreak",
				"Operation Inexsmar",
				"Operation PowerFall",
				"Operation The Gh0st Remains the Same",
				"Purple Pygmy",
				"SIG25",
				"Shadow Crane",
				"T-APT-02",
				"TieOnJoe",
				"Tungsten Bridge",
				"Zigzag Hail"
			],
			"source_name": "ETDA:DarkHotel",
			"tools": [
				"Asruex",
				"DarkHotel",
				"DmaUp3.exe",
				"GreezeBackdoor",
				"Karba",
				"Nemain",
				"Nemim",
				"Ramsay",
				"Retro",
				"Tapaoux",
				"Trojan.Win32.Karba.e",
				"Virus.Win32.Pioneer.dx",
				"igfxext.exe",
				"msieckc.exe"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775439141,
	"ts_updated_at": 1775791797,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/c2d126d84639e3321771b89aa487a49259193212.pdf",
		"text": "https://archive.orkl.eu/c2d126d84639e3321771b89aa487a49259193212.txt",
		"img": "https://archive.orkl.eu/c2d126d84639e3321771b89aa487a49259193212.jpg"
	}
}