// HackTheBox  ·  writeup

SneakyKeys

HackTheBox Medium unziptsharkobjdumpPython
Malware Analysis

Scenario

In this scenario, players step into the role of an IT security analyst investigating a malware incident involving an employee who unknowingly installed a malicious update. Through reverse engineering the malware and analyzing its execution flow, players will uncover the sensitive information that was exfiltrated and determine the scope of the compromise. The challenge emphasizes practical malware analysis, custom encryption reversal, and real-world malware analysis skills.

Executive Summary

An employee installed a fake update that was really a keystroke-capturing implant. The binary hides its strings and commands behind a stream cipher, copies itself into a per-user autorun location, and reports to a command server over a chat protocol, posting captured data into a dedicated channel. Static analysis of the executable recovered the cipher constants, the hardcoded nonce and the way the per-victim key is built, and the same material decrypted the messages in the packet capture. The decrypted traffic showed the victim emailing a colleague and typing a website password in clear, so the exfiltrated credential was recoverable from the capture alone. The sample was never executed.

Artifacts

Safety note: Everything here is static. Disassemble the PE, never run it.

Defense Evasion

Q1 — Which encryption algorithm is used by the malware to protect its strings and commands (view constants)?

Triage with file, then grep the raw binary for well-known cipher constants instead of reading the whole disassembly. The .rdata section holds the sigma constant string that stream ciphers of this family seed their state with, and a library header string sits beside it that names the algorithm outright. Dump the full disassembly to a file at the same time, because the key and nonce hunt in later questions needs it.

unzip -P <password> Danger.zip; file SneakyKeys.exe
strings -a -t x SneakyKeys.exe | grep -iE "expand 32-byte|\.h$"
objdump -d -M intel SneakyKeys.exe > dis.txt

Tip: Search for the cipher constants first. It is faster than tracing the init routine, and the same disassembly dump is reused for Q2, Q5 and Q6.

Click flag to reveal chacha20

Persistence

Q2 — Which directory does the malware use to drop its duplicated copy ?

This one was reasoned from the code rather than read from a decrypted path string. The disassembly calls SHGetFolderPath with CSIDL value 7, the per-user Startup folder, and then appends an encrypted \<name>.exe suffix to the result. A copy placed there runs at every logon, which is the persistence mechanism. Expand the CSIDL into its path relative to the profile and fit it to the format mask segment by segment.

grep -n "SHGetFolderPath" dis.txt

Note: The answer comes from the API call and the CSIDL constant, not from a decrypted string.

Click to reveal answer \AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\

Command and Control

Q3 — Which protocol does the malware rely on to communicate with its command server?

Start with the protocol hierarchy and TCP conversations in the capture. One long-lived session dominates, on a port that looks like a TLS variant of a well-known service, but the payload is cleartext. Pulling the data frames shows NICK, USER, JOIN and PRIVMSG commands, which are unmistakable chat-protocol verbs.

tshark -r cap.pcapng -q -z phs
tshark -r cap.pcapng -q -z conv,tcp
tshark -r cap.pcapng -Y "tcp.len>0" -T fields -e frame.number -e data

Gotcha: The server port suggests TLS, but the stream is not encrypted at the transport layer. Read the data frames anyway; the hex decodes straight to protocol commands.

Click flag to reveal IRC
Q4 — Which channel does the malware use to store the captured data?

The JOIN command in the same stream names the channel the implant enters before it starts posting. Everything it sends afterward is a PRIVMSG to that channel. Keep the exact spelling from the capture, typo included, because the format mask follows it.

tshark -r cap.pcapng -Y "tcp.len>0" -T fields -e data | grep -i 4a4f494e
Click to reveal answer #key_storrage

Collection

Q5 — Which application is being targeted by the malware?

The target name is stored encrypted in the binary, so decrypting the embedded strings is the route. The key and nonce sit in .data near 0xd3440: the key is a longer readable string followed by padding, and the 8-byte nonce is a readable string too. Feed both to a small Python ChaCha20 implementation and brute-decrypt each embedded blob. One blob decrypts to the name of the targeted application.

objdump -s -j .data SneakyKeys.exe | sed -n "/d3440/,+6p"
python3 -I chacha_decrypt.py SneakyKeys.exe --key-off 0xd3440 --nonce "$NONCE"

Tip: The key is the readable run of characters in .data, not the whole 32-byte slot read as text. The trailing bytes are binary, so take the first 32 bytes.

Click flag to reveal Google Chrome

Exfiltration

Q6 — Identify the encryption key the malware applied before sending data from the victim system.

Exfiltrated messages use a different key from the hardcoded string decryptor. The USER line in the capture carries a GUID as its client identifier, which is the machine’s MachineGuid. With the dashes removed it is exactly 32 hex characters, the right size for a 256-bit key. The nonce is the static .data string from Q5, referenced at 0x1400d4010. The format mask is key:nonce. Decrypting the PRIVMSG payloads with that pair produces readable text, which confirms it.

tshark -r cap.pcapng -Y "tcp.len>0" -T fields -e data | xxd -r -p | grep -a "^USER"
python3 -I chacha_decrypt.py cap.pcapng --key <guid-without-dashes> --nonce "$NONCE"

Gotcha: The GUID as key is only half of it. Using the first PRIVMSG hex as the nonce fails; the nonce is the static string in .data, found by reading the disassembly around the cipher setup.

Click to reveal answer 9d9a51bfb38f496499ad31c1249d5a70:on3_n0nc
Q7 — Who is Alice communicating with?

Decrypt the captured PRIVMSG payloads with the Q6 pair. One is an email-style message addressed by first name, which tells you who the victim was writing to.

python3 -I chacha_decrypt.py cap.pcapng --key 9d9a51bfb38f496499ad31c1249d5a70 --nonce on3_n0nc
Click flag to reveal john
Q8 — What is the leaked password associated with Alice?

Another message in the same decrypted output carries the credential Alice typed. It names the service it belongs to, and the password follows in plain text.

python3 -I chacha_decrypt.py cap.pcapng --key 9d9a51bfb38f496499ad31c1249d5a70 --nonce on3_n0nc | grep -i password
Click to reveal answer ALICE1SO

Attack Summary

PhaseAction
Defense EvasionImplant delivered as a fake update; strings and commands protected with a stream cipher
PersistenceCopies itself into the per-user Startup folder
Command and ControlConnects to an IRC server and joins a storage channel
CollectionTargets the Chrome browser and captures typed input
ExfiltrationEncrypts captured text with a key derived from the machine GUID and a hardcoded nonce, then posts it as channel messages
ImpactA message to a colleague and a WordPress password leak in the clear to anyone with the key

IOCs

TypeValue
FileSneakyKeys.exe
Persistence path%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\
IRC channel#key_storrage
Nonce (static)on3_n0nc
Exfil key (this victim)9d9a51bfb38f496499ad31c1249d5a70
NetworkTCP 6697, cleartext IRC

MITRE ATT&CK

TechniqueIDDescription
MasqueradingT1036Malware delivered as a legitimate-looking update
Obfuscated Files or InformationT1027Embedded strings and commands encrypted in the binary
Boot or Logon Autostart: Startup FolderT1547.001Self-copy placed in the Startup folder
Application Layer ProtocolT1071IRC as the command and exfil channel
Encrypted Channel: Symmetric CryptographyT1573.001Stream cipher over the payloads with a machine-derived key
Input Capture: KeyloggingT1056.001Captured text from the targeted browser session
Exfiltration Over C2 ChannelT1041Captured data posted into the IRC channel

Defender Takeaways

A chat protocol on an unusual port from a workstation is a detection on its own. The session ran cleartext IRC on a TLS-looking port, so a payload-aware protocol check would have flagged it where a port-based rule passed it. Alert on protocol and port mismatches in egress.

Static strings in an implant make the key recoverable. The nonce was hardcoded and the key came from MachineGuid, so the capture decrypted with no help from the C2. Once one sample is analysed, the same material decrypts every victim’s traffic and gives responders the full leak scope.

Startup-folder persistence is cheap to monitor. A new executable in %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup outside software install is rare. File-creation telemetry on that path is a reliable early signal.

Fake update installers need provenance checks. The user ran an unsigned update. Application allowlisting and update-channel verification would have blocked the sample before its first beacon.

Credentials typed in a browser end up in the capture. Anything the implant records is exfiltrated, so password manager autofill and MFA matter even when the endpoint is already compromised.

Conclusion

A fake update gave the attacker a keylogger that quietly streamed a colleague email and a WordPress password into an IRC channel. The cipher was only as strong as its secrecy: the nonce and key derivation were readable in the binary, which is why static analysis alone was enough to recover everything that left the network.