Lazarus Group targets Aerospace and Defense with new Comebacker variant

www.enki.co.kr · ENKI · 5 months ago · news
quality 7/10 · good
0 net
Lazarus Group targets Aerospace and Defense with new Comebacker variant | Enki White Hat Select Language EN EN Contact Company Service Product Media Center Contact Company Service Product Media Center Select Language EN EN Select Language EN EN Contact Company Service Product Media Center 오늘 닫기 "> Go to Top Go to Top Threat Intelligence Threat Intelligence Threat Intelligence L a z a r u s G r o u p t a r g e t s A e r o s p a c e a n d D e f e n s e w i t h n e w C o m e b a c k e r v a r i a n t L a z a r u s G r o u p t a r g e t s A e r o s p a c e a n d D e f e n s e w i t h n e w C o m e b a c k e r v a r i a n t L a z a r u s G r o u p t a r g e t s A e r o s p a c e a n d D e f e n s e w i t h n e w C o m e b a c k e r v a r i a n t 엔키화이트햇 엔키화이트햇 Nov 7, 2025 Nov 7, 2025 Nov 7, 2025 Content Content Content Executive Summary ENKI identified a new variant of Comebacker, initially identified following public reporting of a malicious domain. The malware is delivered via lure documents themed around prominent aerospace and defense organizations, indicating a targeted espionage campaign against this sector. Pivoting from the initial C&C infrastructure, we uncovered an additional C&C domain and a related Comebacker sample, suggesting the campaign has been active since at least March 2025. 1. Overview In 2025-06, ENKI initiated an investigation based on ThreatBookLabs ' reporting of a malicious domain, office-theme[.]com , attributed to Lazarus Group. Analysis of .docx files hosted on this domain revealed a multi-stage malware infection chain deploying a new variant of the Comebacker backdoor. By pivoting on the malware's C&C infrastructure, we identified an additional C&C domain and a related Comebacker sample that suggests the campaign has been active since at least March 2025. This report provides an analysis of this new Comebacker variant, details the associated infrastructure, and tracks the malware's evolution over time. 1.1. Comebacker Comebacker was first reported by Google Threat Analysis Group in a 2021 report on a campaign targeting security researchers. Functioning as a downloader and backdoor, it is designed to retrieve and execute DLLs payloads from a C&C server. Microsoft later named the malware "Comebacker" , and it has since been attributed to Lazarus group. Since its initial discovery in 2021, Comebacker has been observed in multiple campaigns. In 2024, variants were found embedded in malicious PyPI packages, demonstrating the threat actor’s continued activity. 2. Malware Analysis 2.1. C&C Server Open Directory We identified staging activity on the open directory at an open directory on office-theme[.]com . This document initiates a multi-stage execution flow, ultimately leading to the in-memory execution of the final COMEBACKER payload. The full infection chain is detailed in the following subsections. caption - Open directory listing on office-theme[.]com While multiple files were present in the directory, only four files with .bin extensions were downloadable at the time of analysis. These files were identified as Microsoft Word documents, each containing a malicious VBA macro. Although the lure content varied, all four droppers deploy the same malicious payload. 2.2. Comebacker Dropper When a victim opens one of the malicious .docx files and enables macros, the embedded VBA code executes. We extracted this macro code for analysis using the olevba tool. caption - VBA macro code extracted via olevba The macro decrypts and deploys two embedded components that are stored as large hexadecimal strings: a loader DLL and a decoy document. The decryption process involves a custom algorithm using XOR and bit-swapping operations. A Python script to replicate this decryption is available in Appendix C, under "Comebacker Dropper Decryption Script" . The decrypted files are written to the following paths on the victim system: Loader: C:\ProgramData\WPSOffice\wpsoffice_aam.ocx Decoy document: C:\ProgramData\Document\EDGE_Group_Interview_NDA.docx The macro then executes the loader by calling the `LoadLibraryA` API function and opens the decoy document. We identified four distinct decoy documents leveraging themes related to the aerospace and defense sectors, including lures impersonating Edge Group, Indian Institute of Technology Kanpur (IIT Kanpur), and Airbus. This specific targeting strongly indicates the campaign's objective is espionage. caption - Decoy document impersonating IIT Kanpur: Guest_Lecture_Invitation_Format_IITK.docx caption - Decoy document related to Airbus: Airbus_C295_Integration_Document_for_TASL.docx 2.3. Comebacker Stage 1 Loader – wpsoffice_aam.cox The wpsoffice_aam.ocx file is the second-stage loader, employed to decrypt, persist, and execute the third stage of the infection chain. The loader first decrypts an embedded payload using the ChaCha20 stream cipher. The static configuration for this decryption is as follows: key: ad9c5aca9977d04c73be579199a827049b6dd9840091ffe8e23acc05e1d4a657 iv: edc9ce049daeba35b8687740 counter: 1 A Python script to replicate this decryption is available in Appendix C, under "Comebacker Stage 1 Loader Decryption Script". Following decryption, the loader decompresses the resulting data using the zlib library. The final payload is written to C:\ProgramData\USOShared\USOPrivate.dll . caption - ChaCha20 decryption and writing of USOPrivate.dll To establish persistence, the loader creates a shortcut ( .lnk ) to USOPrivate.dll in the user's Startup folder. cmd.exe /C powershell -Command "$s = (New-Object -COMWScript.Shell).CreateShortcut('C:\\ProgramData\\USOShared\\Micro.lnk'); $s.TargetPath = 'C:\\Windows\\System32\\rundll32.exe'; $s.Arguments = '\"[USOPrivate.dll path]\" LoadMimi \"C:\\Windows\\System32\\cmd.exe\"'; $s.Save()" After creating the shortcut, the loader calls the LoadMimi funciton USOPrivate.dll using rundll32.exe. 2.4. Comebacker Stage 2 Loader – USOPrivate.dll USOPrivate.dll is the final loader in the infection chain. It decrypts the embedded Comebacker and executes it directly from memory. The DLL employs the same ChaCha20 decryption code seen in the previous stage, reusing the identical key, iv, and initial counter values. key: ad9c5aca9977d04c73be579199a827049b6dd9840091ffe8e23acc05e1d4a657 iv: edc9ce049daeba35b8687740 initial counter value: 1 caption - Reuse of the ChaCha20 decryption code in USOPrivate.dll After decryption and decompression, the loader loads the final Comebacker payload into memory. It then transfers execution to the payload by calling its GetWindowSizedW export with the string argument "1282" . caption - Calling the GetWindowSizedW export of the Comebacker payload 2.5. Comebacker Once executed, the Comebacker payload's main GetWindowSizedW export begins by generating a unique victim identifier. This ID is constructed by concatenating a randomly generated 10-character alphanumeric string, the argument value passed during execution ( "1282" ), and the static string "64" . caption - Victim ID generation The malware then prepares to beacon out to its hardcoded C&C server: hxxps://hiremployee[.]com 2.5.1. C&C Server Communication All C&C communications occur over HTTPS. The outbound data is first encrypted with AES-128-CBC and then Base64-encoded. The malware uses the same value for both the encryption key and the IV. encryption key and IV: x!P<&}mjH2YHRQ', caption - AES encryption code Data received from the C&C server is similarly Base64-decoded and then decrypted using the same AES-128-CBC key and IV. A Python script to decrypt this C&C traffic is available in Appendix C, under “Comebacker C&C Data Decryption Script” . 2.5.2. Initial Connection The malware's initial beacon is encoded in the query string of the URL. The query string structure is as follows: [random 2 lowercase letters]=[random 10 lowercase letters]& [random 5 lowercase letters]=[base64-encoded ID value]& [random 4 lowercase letters]=& [random 6 lowercase letters]=0& [random 6 lowercase letters]=[base64-encoded length of the current time]& [random 6 lowercase letters]=[base64-encoded current time]& [random letters up to 10]=[random letters up to 20] The C&C server's response follows the following format. [4 hexadecimal digits] [1 hexadecimal digit] [base64-encoded message length] [base64-encoded message] The malware parses the hex digits and the decoded message to determine its next action. The primary behaviors are outlined below. | C & C server response | action | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | First value is 13 | Terminates process . | | Decoded message is "0" | Enter a sleep - retry loop . Beacons again after 60 seconds . After 20 consecutive "0" responses , the sleep interval increases to 20 minutes . | | Decoded message is "1" | Beacon again after a short sleep of 10 seconds | C & C server response | action | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | First value is 13 | Terminates process . | | Decoded message is "0" | Enter a sleep - retry loop . Beacons again after 60 seconds . After 20 consecutive "0" responses , the sleep interval increases to 20 minutes . | | Decoded message is "1" | Beacon again after a short sleep of 10 seconds | C & C server response | action | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | First value is 13 | Terminates process . | | Decoded message is "0" | Enter a sleep - retry loop . Beacons again after 60 seconds . After 20 consecutive "0" responses , the sleep interval increases to 20 minutes . | | Decoded message is "1" | Beacon again after a short sleep of 10 seconds caption - C&C response info If the server's response does not match any of the control commands, the malware downloads and executes a payload from the message with the following structure: [command code]|[encrypted file size]|[export name]|[argument]|[MD5 hash of the encrypted file] If a message that satisfies the above condition is received, it downloads the encrypted file from the C&C server and executes it. 2.5.3. File Download and Execution Upon receiving a download and execute command, the malware requests the payload from the C&C server. After downloading, it first calculates the MD5 hash of the received encrypted file and compares it against the hash from the command. If the hashes do not match, the download is considered corrupt, and the malware re-enters its sleep-retry loop. caption - MD5 hash comparison If the hashes match, the malware decrypts the payload using the same ChaCha20 implementation seen in the loader stages, with identical static key, nonce, and counter values. key: ad9c5aca9977d04c73be579199a827049b6dd9840091ffe8e23acc05e1d4a657 iv: edc9ce049daeba35b8687740 initial counter value: 1 caption - ChaCha20 decryption code Finally, the decrypted payload is loaded into memory. The malware calls the exported function specified in the command, passing the provided argument. After execution completes, it sends the result back to the C&C server and resumes beaconing. During our analysis, the C&C server did not respond with download and execute command, so we were unable to retrieve or analyze any next-stage payloads. 3. Additional Malware Collection and Analysis To expand our visibility into the threat actor's infrastructure, we pivoted on known indicators. Using VirusTotal's Relations feature, we searched for other domains serving identical HTTP responses to the C&C server hiremployee[.]com . This analysis identified a second C&C domain: birancearea[.]com . caption - VirusTotal Relations tab showing infrastructure overlap between C&C domains birancearea[.]com was scanned by VirusTotal in March 2025. We found an associated Comebacker sample that communicates with this domain, with the following hash. f2b3867aa06fb38d1505b3c2b9e523d83f906995dcdd1bb384a1087b385bfc50 3.1. Comebacker Stage 1 Loader This loader is a DLL file that functions as the first stage in this alternate infection chain. It was first uploaded to VirusTotal in March 2025, and is similar to the Comebacker loader that was embedded in PyPI packages and distributed in 2024, including the HC256 implementation and usage, as well as the code that executes the decrypted payload. Upon execution, the loader checks if the command line includes the specific argument "9Ez6THDirL6Zye4" . If this argument is not present, the process terminates. This check indicates the loader is designed to be executed by a preceding dropper or script, which we were unable to obtain. caption - Command-line argument check If the argument is present, the loader decrypts an embedded payload using the HC256 stream cipher. The decryption algorithm is HC256, and the hardcoded key/IV are identical to those used in the Comebacker loaders distributed via malicious PyPI packages in 2024. key, iv: LH*x239udC<*sd_Sej%lOa0$&ujHl(.R caption - HC256 code in the March 2025 Comebacker loader caption - HC256 code in a 2024 PyPI-distributed sample A Python script to decrypt the payload is available in Appendix C, under “HC256 Decryption Script” . After decryption and decompression, the loader writes the next stage to C:\ProgramData\USOShared\USOInfo.dat and executes it using rundll32.exe . It calls the GetSysStartTime export with two arguments: "dfgdfg" and "G3z!X97k7QrwG" . caption - Execution of USOInfo.dat via rundll32.exe 3.2. Comebacker Stage 2 Loader - USOInfo.dat USOInfo.dat is the in-memory loader for this variant, analogous to USOPrivate.dll from the first infection chain. It begins by validating its command-line arguments, checking for "G3z!X97k7QrwG" . If the argument check succeeds, it decrypts its embedded payload. This stage again uses the HC256 stream cipher but with a different, unique key and IV pair. key, iv: 6w6ZT9|a-0}s$@;(@&#jPVC4o+V?1IU% caption - Argument check and HC256 decryption in USOInfo.dat Following decryption and decompression, the loader loads the final payload into memory. It then calls the payload's GetWindowSizedW export with the argument "3718" . The final payload Comebacker, with identical functionality to the Comebacker detailed in Section 2.5. 4. Attack Evolution 4.1. Decryption Process The Comebacker variant described in the 2021 Google Threat Analysis Group report decrypted its payload using either RC4 or HC256 with the same key and IV. caption - Decryption code in the Google, Microsoft report’s a75886b016d84c3eaacaf01a3c61e04953a7a3adf38acf77a4a2e3a8f544f855 The variant distributed in 2024 via PyPI packages and the variant discovered in March 2025 consistently used HC256 with identical keys and IVs. caption - Decryption code in JPCERT's report on Comebacker distributed as pycryptoenv The newly identified variant deviates from this by introducing a custom XOR/bit-swap algorithm for the initial dropper stage and adopting ChaCha20 for subsequent loader stages. caption - ChaCha20 decryption code in the new variant 4.2. Communications Encrytion Past Comebacker variants communicated with their C2 servers in plaintext, including the samples from the 2021 security researcher campaign and the 2024 PyPI campaign. caption - Communication code in JPCERT report's report on Comebacker distributed as pycryptoenv The variants observed since March 2025 introduce encrypted C2 communications, using AES-128-CBC to encrypt C&C traffic. caption - AES-128-CBC code in newer variants 4.3. Distribution Process Comebacker, first reported by Google's Threat Analysis Group, was employed in a campaign targeting security researchers with themes of vulnerability research collaboration. The attacker used Visual Studio projects that contained malicious Visual Studio Build Events. In addition, they carried out attacks using an Internet Explorer 0-day. At the time, we published analysis of the exploit on our blog . We have since translated the post to English . caption - Attacker activity in the 2021 campaign targeting security researchers (Source: Google TAG) In 2024, the actor published malicious packages to PyPI, using typosquatting tactics to target developers. caption - pycryptoconf package used to distribute Comebacker in 2024 (Source: JPCERT/CC) While we could not determine the distribution vector for the March 2025 sample, the lure documents from the most recent activity provide strong clues. The documents impersonate specific organizations in the aerospace and defense sector (Edge Group, IIT Kanpur, Airbus) and contain tailored content. This deliberate crafting of decoys for specific targets is a hallmark of spear phishing campaigns aimed at a small set of victims. 5. Conclusion This report details a recent espionage campaign conducted by the DPRK-nexus threat actor Lazarus Group against the aerospace and defense sectors. The campaign leverages a new variant of the Comebacker backdoor, demonstrating the actor's continued refinement of its malware arsenal. The actor's use of highly specific lure documents indicates that this is a targeted spear phishing campaign. Although there are no reports of victims so far, the C2 infrastructure remains active at the time of this publication. Organizations in the aerospace, defense, and research sectors should remain vigilant for phishing attempts and ensure they have robust defenses against macro-based threats. 6. Appendix Appendix A. MITRE ATT&CK | Tactics | Techniques | | --- | --- | | Resource Development | T1583 . 001 : Acquire Infrastructure : Domains | | Initial Access | T1566 . 001 : Phishing : Spearphighing Attachment | | Execution | T1204 . 002 : User Execution : Malicious File - nT1204 . 005 : User Execution : Malicious Library - nT1059 . 001 : Command and Scripting Interpreter : PowerShell - nT1059 . 003 : Command and Scripting Interpreter : Windows Command Shell - nT1059 . 005 : Command and Scripting Interpreter : Visual Basic | | Persistence | T1547 . 001 : Boot or Logon Autostart Execution : Registry Run Keys / Startup Folder - nT1547 . 000 : Boot or Logon Autostart Execution : Shortcut Modification | | Defense Evasion | T1140 : Deobfuscate / Decode Files or Information - nT1027 . 013 : Obfuscated Files or Information : Encrypted / Encoded File - nT1027 . 015 : Obfuscated Files or Information : Compression - nT1218 . 011 : System Binary Proxy Execution : Rundll32 - nT1620 : Reflective Code Loading | | Command and Control | T1132 . 001 : DataEncoding : Standard Encoding - nT1573 . 001 : Encrypted Channel : Symmetric Cryptography - nT1071 . 001 : Application Layer Protocol : Web Protocols - nT1102 : Web Service | Tactics | Techniques | | --- | --- | | Resource Development | T1583 . 001 : Acquire Infrastructure : Domains | | Initial Access | T1566 . 001 : Phishing : Spearphighing Attachment | | Execution | T1204 . 002 : User Execution : Malicious File - nT1204 . 005 : User Execution : Malicious Library - nT1059 . 001 : Command and Scripting Interpreter : PowerShell - nT1059 . 003 : Command and Scripting Interpreter : Windows Command Shell - nT1059 . 005 : Command and Scripting Interpreter : Visual Basic | | Persistence | T1547 . 001 : Boot or Logon Autostart Execution : Registry Run Keys / Startup Folder - nT1547 . 000 : Boot or Logon Autostart Execution : Shortcut Modification | | Defense Evasion | T1140 : Deobfuscate / Decode Files or Information - nT1027 . 013 : Obfuscated Files or Information : Encrypted / Encoded File - nT1027 . 015 : Obfuscated Files or Information : Compression - nT1218 . 011 : System Binary Proxy Execution : Rundll32 - nT1620 : Reflective Code Loading | | Command and Control | T1132 . 001 : DataEncoding : Standard Encoding - nT1573 . 001 : Encrypted Channel : Symmetric Cryptography - nT1071 . 001 : Application Layer Protocol : Web Protocols - nT1102 : Web Service | Tactics | Techniques | | --- | --- | | Resource Development | T1583 . 001 : Acquire Infrastructure : Domains | | Initial Access | T1566 . 001 : Phishing : Spearphighing Attachment | | Execution | T1204 . 002 : User Execution : Malicious File - nT1204 . 005 : User Execution : Malicious Library - nT1059 . 001 : Command and Scripting Interpreter : PowerShell - nT1059 . 003 : Command and Scripting Interpreter : Windows Command Shell - nT1059 . 005 : Command and Scripting Interpreter : Visual Basic | | Persistence | T1547 . 001 : Boot or Logon Autostart Execution : Registry Run Keys / Startup Folder - nT1547 . 000 : Boot or Logon Autostart Execution : Shortcut Modification | | Defense Evasion | T1140 : Deobfuscate / Decode Files or Information - nT1027 . 013 : Obfuscated Files or Information : Encrypted / Encoded File - nT1027 . 015 : Obfuscated Files or Information : Compression - nT1218 . 011 : System Binary Proxy Execution : Rundll32 - nT1620 : Reflective Code Loading | | Command and Control | T1132 . 001 : DataEncoding : Standard Encoding - nT1573 . 001 : Encrypted Channel : Symmetric Cryptography - nT1071 . 001 : Application Layer Protocol : Web Protocols - nT1102 : Web Service caption - MITRE ATT&CK Appendix B. IOCs sha256 b7d625679fbcc86510119920ffdd6d21005427bf49c015697c69ae1ee27e6bab - docx file 046caa2db6cd14509741890e971ddc8c64ef4cc0e369bd5ba039c40c907d1a1f - docx file 14213c013d79ea4bc8309f730e26d52ff23c10654197b8d2d10c82bbbcd88382 - docx file b357b3882cf8107b1cb59015c4be3e0b8b4de80fd7b80ce3cd05081cd3f6a8ff - docx file 7e61c884ce5207839e0df7a22f08f0ab7d483bfa1828090aa260a2f14a0c942c - wpsoffice_aam.cox c4a5179a42d9ff2774f7f1f937086c88c4bc7c098963b82cc28a2d41c4449f9e - USOPrivate.dll f2b3867aa06fb38d1505b3c2b9e523d83f906995dcdd1bb384a1087b385bfc50 - Comebacker Loader 96b973e577458e5b912715171070c0a0171a3e02154eff487a2dcea4da9fb149 - USOInfo.dat C&C hxxps://birancearea[.]com/adminv2 hxxps://hiremployee[.]com Open Directory C&C office-theme[.]com aes key x!P<&}mjH2YHRQ', chacha20 key ad9c5aca9977d04c73be579199a827049b6dd9840091ffe8e23acc05e1d4a657 HC256 key LH*x239udC<*sd_Sej%lOa0$&ujHl(.R 6w6ZT9|a-0}s$@;(@&#jPVC4o+V?1IU% Appendix C. Decryption Scripts Comebacker Dropper Decryption Script def decrypt ( enc ) : dec = '' for i in enc : dec += bin ( i ^ 25 ) [ 2 : ] . rjust ( 8 , '0' ) enc = dec [ : ] dec = '' for i in range ( 0 , len ( enc ) , 4 ) : dec += ( enc [ i + 2 ] + enc [ i + 3 ] + enc [ i ] + enc [ i + 1 ] ) enc = dec [ : ] dec = '' for i in enc [ ::- 1 ] : dec += '1' if i == '0' else '0' final = b'' for i in range ( 0 , len ( dec ) , 8 ) : final += bytes ( [ int ( dec [ i : i + 8 ] , 2 ) ] ) return final enc = bytes . fromhex ( open ( 'loader_enc' , 'r' ) . read ( ) ) loader = decrypt ( enc ) open ( 'wpoffice_aam.ocx' , 'wb' ) . write ( loader ) enc = bytes . fromhex ( open ( 'lurefile_enc' , 'r' ) . read ( ) ) lurefile = decrypt ( enc ) open ( 'EDGE_Group_Interview_NDA.docx' , 'wb' ) . write ( lurefile ) def decrypt ( enc ) : dec = '' for i in enc : dec += bin ( i ^ 25 ) [ 2 : ] . rjust ( 8 , '0' ) enc = dec [ : ] dec = '' for i in range ( 0 , len ( enc ) , 4 ) : dec += ( enc [ i + 2 ] + enc [ i + 3 ] + enc [ i ] + enc [ i + 1 ] ) enc = dec [ : ] dec = '' for i in enc [ ::- 1 ] : dec += '1' if i == '0' else '0' final = b'' for i in range ( 0 , len ( dec ) , 8 ) : final += bytes ( [ int ( dec [ i : i + 8 ] , 2 ) ] ) return final enc = bytes . fromhex ( open ( 'loader_enc' , 'r' ) . read ( ) ) loader = decrypt ( enc ) open ( 'wpoffice_aam.ocx' , 'wb' ) . write ( loader ) enc = bytes . fromhex ( open ( 'lurefile_enc' , 'r' ) . read ( ) ) lurefile = decrypt ( enc ) open ( 'EDGE_Group_Interview_NDA.docx' , 'wb' ) . write ( lurefile ) def decrypt ( enc ) : dec = '' for i in enc : dec += bin ( i ^ 25 ) [ 2 : ] . rjust ( 8 , '0' ) enc = dec [ : ] dec = '' for i in range ( 0 , len ( enc ) , 4 ) : dec += ( enc [ i + 2 ] + enc [ i + 3 ] + enc [ i ] + enc [ i + 1 ] ) enc = dec [ : ] dec = '' for i in enc [ ::- 1 ] : dec += '1' if i == '0' else '0' final = b'' for i in range ( 0 , len ( dec ) , 8 ) : final += bytes ( [ int ( dec [ i : i + 8 ] , 2 ) ] ) return final enc = bytes . fromhex ( open ( 'loader_enc' , 'r' ) . read ( ) ) loader = decrypt ( enc ) open ( 'wpoffice_aam.ocx' , 'wb' ) . write ( loader ) enc = bytes . fromhex ( open ( 'lurefile_enc' , 'r' ) . read ( ) ) lurefile = decrypt ( enc ) open ( 'EDGE_Group_Interview_NDA.docx' , 'wb' ) . write ( lurefile ) Comebacker Stage 1 Loader Decryption Script import struct def rotl32 ( x , n ) : return ( ( x << n ) & 0xffffffff ) | ( x >> ( 32 - n ) ) def pack4 ( b ) : return struct . unpack ( '> 32 ) & 0xffffffff ) def _quarterround ( self , x , a , b , c , d ) : x [ a ] = ( x [ a ] + x [ b ] ) & 0xffffffff x [ d ] = rotl32 ( x [ d ] ^ x [ a ] , 16 ) x [ c ] = ( x [ c ] + x [ d ] ) & 0xffffffff x [ b ] = rotl32 ( x [ b ] ^ x [ c ] , 12 ) x [ a ] = ( x [ a ] + x [ b ] ) & 0xffffffff x [ d ] = rotl32 ( x [ d ] ^ x [ a ] , 8 ) x [ c ] = ( x [ c ] + x [ d ] ) & 0xffffffff x [ b ] = rotl32 ( x [ b ] ^ x [ c ] , 7 ) def _block_next ( self ) : x = self . state [ : ] for _ in range ( 10 ) : self . _quarterround ( x , 0 , 4 , 8 , 12 ) self . _quarterround ( x , 1 , 5 , 9 , 13 ) self . _quarterround ( x , 2 , 6 , 10 , 14 ) self . _quarterround ( x , 3 , 7 , 11 , 15 ) self . _quarterround ( x , 0 , 5 , 10 , 15 ) self . _quarterround ( x , 1 , 6 , 11 , 12 ) self . _quarterround ( x , 2 , 7 , 8 , 13 ) self . _quarterround ( x , 3 , 4 , 9 , 14 ) for i in range ( 16 ) : self . keystream32 [ i ] = ( x [ i ] + self . state [ i ] ) & 0xffffffff self . state [ 12 ] = ( self . state [ 12 ] + 1 ) & 0xffffffff if self . state [ 12 ] == 0 : self . state [ 13 ] = ( self . state [ 13 ] + 1 ) & 0xffffffff assert self . state [ 13 ] != 0 def xor ( self , data : bytes ) -> bytes : output = bytearray ( ) keystream = bytearray ( ) for word in self . keystream32 : keystream . extend ( struct . pack ( '= 64 : self . _block_next ( ) keystream = bytearray ( ) for word in self . keystream32 : keystream . extend ( struct . pack ( '> ( 32 - n ) ) def pack4 ( b ) : return struct . unpack ( '> 32 ) & 0xffffffff ) def _quarterround ( self , x , a , b , c , d ) : x [ a ] = ( x [ a ] + x [ b ] ) & 0xffffffff x [ d ] = rotl32 ( x [ d ] ^ x [ a ] , 16 ) x [ c ] = ( x [ c ] + x [ d ] ) & 0xffffffff x [ b ] = rotl32 ( x [ b ] ^ x [ c ] , 12 ) x [ a ] = ( x [ a ] + x [ b ] ) & 0xffffffff x [ d ] = rotl32 ( x [ d ] ^ x [ a ] , 8 ) x [ c ] = ( x [ c ] + x [ d ] ) & 0xffffffff x [ b ] = rotl32 ( x [ b ] ^ x [ c ] , 7 ) def _block_next ( self ) : x = self . state [ : ] for _ in range ( 10 ) : self . _quarterround ( x , 0 , 4 , 8 , 12 ) self . _quarterround ( x , 1 , 5 , 9 , 13 ) self . _quarterround ( x , 2 , 6 , 10 , 14 ) self . _quarterround ( x , 3 , 7 , 11 , 15 ) self . _quarterround ( x , 0 , 5 , 10 , 15 ) self . _quarterround ( x , 1 , 6 , 11 , 12 ) self . _quarterround ( x , 2 , 7 , 8 , 13 ) self . _quarterround ( x , 3 , 4 , 9 , 14 ) for i in range ( 16 ) : self . keystream32 [ i ] = ( x [ i ] + self . state [ i ] ) & 0xffffffff self . state [ 12 ] = ( self . state [ 12 ] + 1 ) & 0xffffffff if self . state [ 12 ] == 0 : self . state [ 13 ] = ( self . state [ 13 ] + 1 ) & 0xffffffff assert self . state [ 13 ] != 0 def xor ( self , data : bytes ) -> bytes : output = bytearray ( ) keystream = bytearray ( ) for word in self . keystream32 : keystream . extend ( struct . pack ( '= 64 : self . _block_next ( ) keystream = bytearray ( ) for word in self . keystream32 : keystream . extend ( struct . pack ( '> ( 32 - n ) ) def pack4 ( b ) : return struct . unpack ( '> 32 ) & 0xffffffff ) def _quarterround ( self , x , a , b , c , d ) : x [ a ] = ( x [ a ] + x [ b ] ) & 0xffffffff x [ d ] = rotl32 ( x [ d ] ^ x [ a ] , 16 ) x [ c ] = ( x [ c ] + x [ d ] ) & 0xffffffff x [ b ] = rotl32 ( x [ b ] ^ x [ c ] , 12 ) x [ a ] = ( x [ a ] + x [ b ] ) & 0xffffffff x [ d ] = rotl32 ( x [ d ] ^ x [ a ] , 8 ) x [ c ] = ( x [ c ] + x [ d ] ) & 0xffffffff x [ b ] = rotl32 ( x [ b ] ^ x [ c ] , 7 ) def _block_next ( self ) : x = self . state [ : ] for _ in range ( 10 ) : self . _quarterround ( x , 0 , 4 , 8 , 12 ) self . _quarterround ( x , 1 , 5 , 9 , 13 ) self . _quarterround ( x , 2 , 6 , 10 , 14 ) self . _quarterround ( x , 3 , 7 , 11 , 15 ) self . _quarterround ( x , 0 , 5 , 10 , 15 ) self . _quarterround ( x , 1 , 6 , 11 , 12 ) self . _quarterround ( x , 2 , 7 , 8 , 13 ) self . _quarterround ( x , 3 , 4 , 9 , 14 ) for i in range ( 16 ) : self . keystream32 [ i ] = ( x [ i ] + self . state [ i ] ) & 0xffffffff self . state [ 12 ] = ( self . state [ 12 ] + 1 ) & 0xffffffff if self . state [ 12 ] == 0 : self . state [ 13 ] = ( self . state [ 13 ] + 1 ) & 0xffffffff assert self . state [ 13 ] != 0 def xor ( self , data : bytes ) -> bytes : output = bytearray ( ) keystream = bytearray ( ) for word in self . keystream32 : keystream . extend ( struct . pack ( '= 64 : self . _block_next ( ) keystream = bytearray ( ) for word in self . keystream32 : keystream . extend ( struct . pack ( ' int : return len ( n ) * 8 def rotr32 ( x : int , n : int ) -> int : return ( x >> n ) + ( ( x << ( 32 - n ) ) ) & 0xffffffff def rotl32 ( x : int , n : int ) -> int : return ( x << n ) + ( ( x >> ( 32 - n ) ) ) & 0xffffffff class HC256 ( ) : def __init__ ( self , key : bytes , iv : bytes , log_level : Optional [ int ] = logging . WARN ) : logging . basicConfig ( ) self . logger = logging . getLogger ( __name__ ) self . logger . setLevel ( log_level ) if bit_check ( key ) != 256 : self . logger . warning ( "keylen != 256 bits, null-padding" ) if bit_check ( iv ) != 256 : self . logger . warning ( "ivlen != 256 bits, null-padding" ) key = key + b"\x00" * ( 32 - len ( key ) ) iv = iv + b"\x00" * ( 32 - len ( iv ) ) # Convert to list of DWORD self . key = [ int . from_bytes ( key [ i : i + 4 ] , byteorder = "little" ) for i in range ( len ( key ) ) [ :: 4 ] ] self . iv = [ int . from_bytes ( iv [ i : i + 4 ] , byteorder = "little" ) for i in range ( len ( iv ) ) [ :: 4 ] ] self . logger . debug ( f"key: { self . key } " ) self . logger . debug ( f"iv: { self . iv } " ) self . __init_cipher ( ) self . ctr = 0 def __init_cipher ( self ) : def f1 ( x ) : return ( rotr32 ( x , 7 ) ^ rotr32 ( x , 18 ) ^ ( x >> 3 ) ) & 0xffffffff def f2 ( x ) : return ( rotr32 ( x , 17 ) ^ rotr32 ( x , 19 ) ^ ( x >> 10 ) ) & 0xffffffff if len ( self . key ) != 8 : raise ValueError ( "Invalid key len!" ) if len ( self . iv ) != 8 : raise ValueError ( "Invalid IV len!" ) self . logger . debug ( "Setting key/IV" ) self . ctr = 0 w = [ 0 ] * 2560 self . P = None self . Q = None for i in range ( len ( self . key ) ) : w [ i ] = self . key [ i ] w [ i + 8 ] = self . iv [ i ] for i in range ( 16 , 2560 ) : w [ i ] = ( ( ( ( f2 ( w [ i - 2 ] ) + w [ i - 7 ] ) & 0xffffffff ) + ( f1 ( w [ i - 15 ] ) + w [ i - 16 ] ) & 0xffffffffff ) + i ) & 0xffffffff self . P = w [ 512 : 1536 ] self . Q = w [ 1536 : ] if len ( self . P ) != 1024 or len ( self . Q ) != 1024 : raise ValueError ( f"P/Q invalid len: P: { len ( self . P ) } , Q: { len ( self . Q ) } " ) # Run 4096 iters. self . logger . debug ( "Running keystream iterations before cipher" ) for i in range ( 4096 ) : self . keystream ( ) def keystream ( self ) -> int : r = None def g1 ( x : int , y : int ) -> int : return ( ( rotr32 ( x , 10 ) ^ rotr32 ( y , 23 ) ) + self . Q [ ( x ^ y ) % 1024 ] ) & 0xffffffff def g2 ( x : int , y : int ) -> int : return ( ( rotr32 ( x , 10 ) ^ rotr32 ( y , 23 ) ) + self . P [ ( x ^ y ) % 1024 ] ) & 0xffffffff def h1 ( x : int ) -> int : r = self . Q [ x & 0xff ] r = ( r + self . Q [ 256 + ( ( x >> 8 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . Q [ 512 + ( ( x >> 16 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . Q [ 768 + ( ( x >> 24 ) & 0xff ) ] ) & 0xffffffff return r & 0xffffffff def h2 ( x : int ) -> int : r = self . P [ x & 0xff ] r = ( r + self . P [ 256 + ( ( x >> 8 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . P [ 512 + ( ( x >> 16 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . P [ 768 + ( ( x >> 24 ) & 0xff ) ] ) & 0xffffffff return r & 0xffffffff j = self . ctr % 1024 j3 = ( j - 3 ) % 1024 j10 = ( j - 10 ) % 1024 j12 = ( j - 12 ) % 1024 j1023 = ( j - 1023 ) % 1024 if self . ctr < 1024 : self . P [ j ] = ( self . P [ j ] + self . P [ j10 ] + g1 ( self . P [ j3 ] , self . P [ j1023 ] ) ) & 0xffffffff r = h1 ( self . P [ j12 ] ) r = ( r ^ self . P [ j ] ) & 0xffffffff else : self . Q [ j ] = ( self . Q [ j ] + self . Q [ j10 ] + g2 ( self . Q [ j3 ] , self . Q [ j1023 ] ) ) & 0xffffffff r = h2 ( self . Q [ j12 ] ) r = ( r ^ self . Q [ j ] ) & 0xffffffff self . ctr = ( self . ctr + 1 ) & 0x7ff return r def crypt ( self , cipher : bytes ) -> bytes : uncipher = list ( cipher ) i = 0 while i < len ( cipher ) : k = self . keystream ( ) self . logger . debug ( f"keystream[ { i } ]: { k } " ) j = 0 while ( j < 4 and i < len ( cipher ) ) : uncipher [ i ] ^= ( k & 0xff ) i += 1 j += 1 k = ( k >> 8 ) return bytes ( uncipher ) key = b'LH*x239udC<*sd_Sej%lOa0$&ujHl(.R' encdat = open ( 'enc_dat' , 'rb' ) . read ( ) cipher = HC256 ( key , key ) decdat = cipher . crypt ( encdat ) open ( 'PK_dat' , 'wb' ) . write ( decdat ) zipfile . ZipFile ( 'PK_dat' , 'r' ) . extractall ( '.' ) from typing import Optional import logging import zipfile def bit_check ( n : bytes ) -> int : return len ( n ) * 8 def rotr32 ( x : int , n : int ) -> int : return ( x >> n ) + ( ( x << ( 32 - n ) ) ) & 0xffffffff def rotl32 ( x : int , n : int ) -> int : return ( x << n ) + ( ( x >> ( 32 - n ) ) ) & 0xffffffff class HC256 ( ) : def __init__ ( self , key : bytes , iv : bytes , log_level : Optional [ int ] = logging . WARN ) : logging . basicConfig ( ) self . logger = logging . getLogger ( __name__ ) self . logger . setLevel ( log_level ) if bit_check ( key ) != 256 : self . logger . warning ( "keylen != 256 bits, null-padding" ) if bit_check ( iv ) != 256 : self . logger . warning ( "ivlen != 256 bits, null-padding" ) key = key + b"\x00" * ( 32 - len ( key ) ) iv = iv + b"\x00" * ( 32 - len ( iv ) ) # Convert to list of DWORD self . key = [ int . from_bytes ( key [ i : i + 4 ] , byteorder = "little" ) for i in range ( len ( key ) ) [ :: 4 ] ] self . iv = [ int . from_bytes ( iv [ i : i + 4 ] , byteorder = "little" ) for i in range ( len ( iv ) ) [ :: 4 ] ] self . logger . debug ( f"key: { self . key } " ) self . logger . debug ( f"iv: { self . iv } " ) self . __init_cipher ( ) self . ctr = 0 def __init_cipher ( self ) : def f1 ( x ) : return ( rotr32 ( x , 7 ) ^ rotr32 ( x , 18 ) ^ ( x >> 3 ) ) & 0xffffffff def f2 ( x ) : return ( rotr32 ( x , 17 ) ^ rotr32 ( x , 19 ) ^ ( x >> 10 ) ) & 0xffffffff if len ( self . key ) != 8 : raise ValueError ( "Invalid key len!" ) if len ( self . iv ) != 8 : raise ValueError ( "Invalid IV len!" ) self . logger . debug ( "Setting key/IV" ) self . ctr = 0 w = [ 0 ] * 2560 self . P = None self . Q = None for i in range ( len ( self . key ) ) : w [ i ] = self . key [ i ] w [ i + 8 ] = self . iv [ i ] for i in range ( 16 , 2560 ) : w [ i ] = ( ( ( ( f2 ( w [ i - 2 ] ) + w [ i - 7 ] ) & 0xffffffff ) + ( f1 ( w [ i - 15 ] ) + w [ i - 16 ] ) & 0xffffffffff ) + i ) & 0xffffffff self . P = w [ 512 : 1536 ] self . Q = w [ 1536 : ] if len ( self . P ) != 1024 or len ( self . Q ) != 1024 : raise ValueError ( f"P/Q invalid len: P: { len ( self . P ) } , Q: { len ( self . Q ) } " ) # Run 4096 iters. self . logger . debug ( "Running keystream iterations before cipher" ) for i in range ( 4096 ) : self . keystream ( ) def keystream ( self ) -> int : r = None def g1 ( x : int , y : int ) -> int : return ( ( rotr32 ( x , 10 ) ^ rotr32 ( y , 23 ) ) + self . Q [ ( x ^ y ) % 1024 ] ) & 0xffffffff def g2 ( x : int , y : int ) -> int : return ( ( rotr32 ( x , 10 ) ^ rotr32 ( y , 23 ) ) + self . P [ ( x ^ y ) % 1024 ] ) & 0xffffffff def h1 ( x : int ) -> int : r = self . Q [ x & 0xff ] r = ( r + self . Q [ 256 + ( ( x >> 8 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . Q [ 512 + ( ( x >> 16 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . Q [ 768 + ( ( x >> 24 ) & 0xff ) ] ) & 0xffffffff return r & 0xffffffff def h2 ( x : int ) -> int : r = self . P [ x & 0xff ] r = ( r + self . P [ 256 + ( ( x >> 8 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . P [ 512 + ( ( x >> 16 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . P [ 768 + ( ( x >> 24 ) & 0xff ) ] ) & 0xffffffff return r & 0xffffffff j = self . ctr % 1024 j3 = ( j - 3 ) % 1024 j10 = ( j - 10 ) % 1024 j12 = ( j - 12 ) % 1024 j1023 = ( j - 1023 ) % 1024 if self . ctr < 1024 : self . P [ j ] = ( self . P [ j ] + self . P [ j10 ] + g1 ( self . P [ j3 ] , self . P [ j1023 ] ) ) & 0xffffffff r = h1 ( self . P [ j12 ] ) r = ( r ^ self . P [ j ] ) & 0xffffffff else : self . Q [ j ] = ( self . Q [ j ] + self . Q [ j10 ] + g2 ( self . Q [ j3 ] , self . Q [ j1023 ] ) ) & 0xffffffff r = h2 ( self . Q [ j12 ] ) r = ( r ^ self . Q [ j ] ) & 0xffffffff self . ctr = ( self . ctr + 1 ) & 0x7ff return r def crypt ( self , cipher : bytes ) -> bytes : uncipher = list ( cipher ) i = 0 while i < len ( cipher ) : k = self . keystream ( ) self . logger . debug ( f"keystream[ { i } ]: { k } " ) j = 0 while ( j < 4 and i < len ( cipher ) ) : uncipher [ i ] ^= ( k & 0xff ) i += 1 j += 1 k = ( k >> 8 ) return bytes ( uncipher ) key = b'LH*x239udC<*sd_Sej%lOa0$&ujHl(.R' encdat = open ( 'enc_dat' , 'rb' ) . read ( ) cipher = HC256 ( key , key ) decdat = cipher . crypt ( encdat ) open ( 'PK_dat' , 'wb' ) . write ( decdat ) zipfile . ZipFile ( 'PK_dat' , 'r' ) . extractall ( '.' ) from typing import Optional import logging import zipfile def bit_check ( n : bytes ) -> int : return len ( n ) * 8 def rotr32 ( x : int , n : int ) -> int : return ( x >> n ) + ( ( x << ( 32 - n ) ) ) & 0xffffffff def rotl32 ( x : int , n : int ) -> int : return ( x << n ) + ( ( x >> ( 32 - n ) ) ) & 0xffffffff class HC256 ( ) : def __init__ ( self , key : bytes , iv : bytes , log_level : Optional [ int ] = logging . WARN ) : logging . basicConfig ( ) self . logger = logging . getLogger ( __name__ ) self . logger . setLevel ( log_level ) if bit_check ( key ) != 256 : self . logger . warning ( "keylen != 256 bits, null-padding" ) if bit_check ( iv ) != 256 : self . logger . warning ( "ivlen != 256 bits, null-padding" ) key = key + b"\x00" * ( 32 - len ( key ) ) iv = iv + b"\x00" * ( 32 - len ( iv ) ) # Convert to list of DWORD self . key = [ int . from_bytes ( key [ i : i + 4 ] , byteorder = "little" ) for i in range ( len ( key ) ) [ :: 4 ] ] self . iv = [ int . from_bytes ( iv [ i : i + 4 ] , byteorder = "little" ) for i in range ( len ( iv ) ) [ :: 4 ] ] self . logger . debug ( f"key: { self . key } " ) self . logger . debug ( f"iv: { self . iv } " ) self . __init_cipher ( ) self . ctr = 0 def __init_cipher ( self ) : def f1 ( x ) : return ( rotr32 ( x , 7 ) ^ rotr32 ( x , 18 ) ^ ( x >> 3 ) ) & 0xffffffff def f2 ( x ) : return ( rotr32 ( x , 17 ) ^ rotr32 ( x , 19 ) ^ ( x >> 10 ) ) & 0xffffffff if len ( self . key ) != 8 : raise ValueError ( "Invalid key len!" ) if len ( self . iv ) != 8 : raise ValueError ( "Invalid IV len!" ) self . logger . debug ( "Setting key/IV" ) self . ctr = 0 w = [ 0 ] * 2560 self . P = None self . Q = None for i in range ( len ( self . key ) ) : w [ i ] = self . key [ i ] w [ i + 8 ] = self . iv [ i ] for i in range ( 16 , 2560 ) : w [ i ] = ( ( ( ( f2 ( w [ i - 2 ] ) + w [ i - 7 ] ) & 0xffffffff ) + ( f1 ( w [ i - 15 ] ) + w [ i - 16 ] ) & 0xffffffffff ) + i ) & 0xffffffff self . P = w [ 512 : 1536 ] self . Q = w [ 1536 : ] if len ( self . P ) != 1024 or len ( self . Q ) != 1024 : raise ValueError ( f"P/Q invalid len: P: { len ( self . P ) } , Q: { len ( self . Q ) } " ) # Run 4096 iters. self . logger . debug ( "Running keystream iterations before cipher" ) for i in range ( 4096 ) : self . keystream ( ) def keystream ( self ) -> int : r = None def g1 ( x : int , y : int ) -> int : return ( ( rotr32 ( x , 10 ) ^ rotr32 ( y , 23 ) ) + self . Q [ ( x ^ y ) % 1024 ] ) & 0xffffffff def g2 ( x : int , y : int ) -> int : return ( ( rotr32 ( x , 10 ) ^ rotr32 ( y , 23 ) ) + self . P [ ( x ^ y ) % 1024 ] ) & 0xffffffff def h1 ( x : int ) -> int : r = self . Q [ x & 0xff ] r = ( r + self . Q [ 256 + ( ( x >> 8 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . Q [ 512 + ( ( x >> 16 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . Q [ 768 + ( ( x >> 24 ) & 0xff ) ] ) & 0xffffffff return r & 0xffffffff def h2 ( x : int ) -> int : r = self . P [ x & 0xff ] r = ( r + self . P [ 256 + ( ( x >> 8 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . P [ 512 + ( ( x >> 16 ) & 0xff ) ] ) & 0xffffffff r = ( r + self . P [ 768 + ( ( x >> 24 ) & 0xff ) ] ) & 0xffffffff return r & 0xffffffff j = self . ctr % 1024 j3 = ( j - 3 ) % 1024 j10 = ( j - 10 ) % 1024 j12 = ( j - 12 ) % 1024 j1023 = ( j - 1023 ) % 1024 if self . ctr < 1024 : self . P [ j ] = ( self . P [ j ] + self . P [ j10 ] + g1 ( self . P [ j3 ] , self . P [ j1023 ] ) ) & 0xffffffff r = h1 ( self . P [ j12 ] ) r = ( r ^ self . P [ j ] ) & 0xffffffff else : self . Q [ j ] = ( self . Q [ j ] + self . Q [ j10 ] + g2 ( self . Q [ j3 ] , self . Q [ j1023 ] ) ) & 0xffffffff r = h2 ( self . Q [ j12 ] ) r = ( r ^ self . Q [ j ] ) & 0xffffffff self . ctr = ( self . ctr + 1 ) & 0x7ff return r def crypt ( self , cipher : bytes ) -> bytes : uncipher = list ( cipher ) i = 0 while i < len ( cipher ) : k = self . keystream ( ) self . logger . debug ( f"keystream[ { i } ]: { k } " ) j = 0 while ( j < 4 and i < len ( cipher ) ) : uncipher [ i ] ^= ( k & 0xff ) i += 1 j += 1 k = ( k >> 8 ) return bytes ( uncipher ) key = b'LH*x239udC<*sd_Sej%lOa0$&ujHl(.R' encdat = open ( 'enc_dat' , 'rb' ) . read ( ) cipher = HC256 ( key , key ) decdat = cipher . crypt ( encdat ) open ( 'PK_dat' , 'wb' ) . write ( decdat ) zipfile . ZipFile ( 'PK_dat' , 'r' ) . extractall ( '.' ) 엔키화이트햇 엔키화이트햇 ENKI Whitehat ENKI Whitehat Offensive security experts delivering deeper security through an attacker's perspective. Offensive security experts delivering deeper security through an attacker's perspective. Popular Articles ENKI Redteam CTF Writeup : Jeopardy ENKI Redteam CTF Writeup : Jeopardy Security Insights ENKI Redteam CTF Writeup : Jeopardy Contagious Interview Campaign Abusing VSCode Distributed on Github Contagious Interview Campaign Abusing VSCode Distributed on Github Threat Intelligence Contagious Interview Campaign Abusing VSCode Distributed on Github When Disclosure Becomes a Security Scorecard: Responding to the 2027 Information Security Disclosure Expansion When Disclosure Becomes a Security Scorecard: Responding to the 2027 Information Security Disclosure Expansion Security Insights When Disclosure Becomes a Security Scorecard: Responding to the 2027 Information Security Disclosure Expansion Back to List Back to List More Articles Threat Intelligence Contagious Interview Campaign Abusing VSCode Distributed on Github ENKI WhiteHat Feb 27, 2026 Threat Intelligence Contagious Interview Campaign Abusing VSCode Distributed on Github ENKI WhiteHat Feb 27, 2026 Threat Intelligence EtherRAT Targeting Windows Disguised as a Game Mod Installer EnkiWhiteHat Jan 20, 2026 Threat Intelligence EtherRAT Targeting Windows Disguised as a Game Mod Installer EnkiWhiteHat Jan 20, 2026 Threat Intelligence Kimsuky Distributing Malicious Mobile App via QR Code EnkiWhiteHat Dec 16, 2025 Threat Intelligence Kimsuky Distributing Malicious Mobile App via QR Code EnkiWhiteHat Dec 16, 2025 The Beginning of Flawless Security System, From the Expertise of the No.1 White Hacker Prepare Before a Security Incident Occurs Contact " style="--framer-icon-mask-mode:intersect;--framer-icon-mask:linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 28.999999999999996%) add"> The Beginning of Flawless Security System, From the Expertise of the No.1 White Hacker Prepare Before a Security Incident Occurs Contact " style="--framer-icon-mask-mode:intersect;--framer-icon-mask:linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 28.999999999999996%) add"> The Beginning of Flawless Security System, From the Expertise of the No.1 White Hacker Prepare Before a Security Incident Occurs Contact " style="--framer-icon-mask-mode:intersect;--framer-icon-mask:linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 28.999999999999996%) add"> êµ¬ë í•˜ê¸° ì½˜í ì¸ ê°€ ìœ ìš©í–ˆë‹¤ë©´? 엔키 ë ˆí„°ë¥¼ êµ¬ë í•˜ì„¸ìš”!