Part 1 - Hornet Stealer
On January 24th, the Twitter account @spamhaus posted a tweet regarding a newly discovered C2 panel. Based on the panel’s logo and name, they have identified this malware as Hornet Stealer
.
According to @spamhaus’s tweets, Hornet Stealer
, which is downloaded via Smokeloader
, is written in Golang
. It targets several applications, such as browsers
, wallets
, Steam
, Telegram
, among others. The malware decrypts strings using Fernet
with a hardcoded key, then encrypts the acquired data using AES GCM
, and finally transmits this encrypted data to a server via TCP connection
.
In this article, we aim to discover and analyze the earliest known sample of the Hornet Stealer
malware.
Part 2 - Dissecting the First Hornet Stealer Sample
1. Tracing the Origins of the Hornet Stealer
To locate the initial sample of the Hornet Stealer
, we can examine the IP address relationships mentioned in @spamhaus’s tweet on VirusTotal.
Upon searching for the IP address 185.221.198.118
and navigating to the Relations tab on VirusTotal, one can observe a file under the ‘Communicating Files’ section. This file appears to be malicious, indicated by its high detection score of 42 out of 71
.
Navigating to the Details tab of this file reveals that it is programmed in Golang, and its initial submission date of January 23, 2024
, suggests that it’s a recent addition. These details strongly point to it being the Hornet Stealer
sample we’re investigating.
SHA256: bc3ee10c21cb07bc0dd6b84a6eaf8efbd0af889467ab7ef647acf60f8c188e83
Prior to analyzing this file, it’s worthwhile to investigate its potential connection with Smokeloader
. However, the Relations
tab in VirusTotal
doesn’t display any links to Smokeloader
. A quick Google search using the file’s hash led to a result from Unpacme
, revealing that this file is hosted on the transfer.sh website with a name setup.exe
.
Link: https://transfer.sh/get/q4ccSmjmTB/setup.exe
A search on VirusTotal using this link reveals a Crowdsourced Context
note stating Activity related to SMOKELOADER
. However, it doesn’t provide an associated sample.
2. Deep Dive into the Hornet Stealer’s First Sample
SHA256: bc3ee10c21cb07bc0dd6b84a6eaf8efbd0af889467ab7ef647acf60f8c188e83
Aware that this sample contains encrypted strings and utilizes Fernet
for runtime decryption, we can proceed by loading the sample into IDA. This will allow us to closely examine its string decryption routine.
Given that the sample’s symbolic information is not stripped, IDA has been able to rename all functions, thereby facilitating an easier analysis.
The presence of meaningful function names simplifies our task to reading the code. Nonetheless, the challenge lies in the encryption of crucial strings. To uncover how they are decrypted, we concentrate on the first function call, following the path: main.main
> setup_utils_AntiCIS
> setup_utils_GetkeyboardLayoutList
> setup_utils_DecryptString
. This path leads us to identify setup_utils_DecryptString
as the key function for string decryption.
The recurring use of a string at the offset off_70CC10
before every call to setup_utils_DecryptString
suggested to us that this might be the decryption key.
Although @spamhaus’s tweet already indicated the use of Fernet, we can confirm this by examining setup_utils_DecryptString
more closely.
With the algorithm, decryption key, and encrypted data at hand, we have the opportunity to use Python
for decrypting a string, which will validate the correctness of our approach.
1
2
3
4
5
6
7
8
9
>>> from cryptography.fernet import Fernet
>>> key = 'MpQzH0ne3b-TkBgkJ0tbdALxiCiJuLBleGUlEoIGQoo='
>>> encrypted_data = 'gAAAAABlsBIs-I3FZyAavfZo8FAeeSmwVqn5DHwjQGrATv5Mz3jzjEk9KD9LBJiTzKDvGmb-RFX1Z-jBO4x5JUIy-ZD6Zf103A=='
>>> fernet = Fernet(key)
>>> decrypted_data = fernet.decrypt(encrypted_data)
>>> decrypted_data
**b'user32.dll'**
>>> decrypted_data.decode()
**'user32.dll'**
The effectiveness of our method was proven when our Python code decrypted a string into user32.dll
. Henceforth, I will decrypt and annotate every string in the functions with their decrypted counterparts as comments. Furthermore, at the end of this article, a custom string extractor
for this malware sample will be introduced.
Armed with this knowledge, let’s delve into analyzing some key functionalities of the Hornet Stealer
, starting with the setup_utils_init
function.
setup_utils_init Function
We begin our analysis with the setup_utils_init
function, which is executed even before the main function. It plays a crucial role in decrypting several important strings, one of which is the C2 address.
setup_Grabber_init Function
The setup_Grabber_init
function, executing prior to the main function, decrypts several crucial strings, including a list of wallet applications. The utilization of these decrypted strings will be explored in a later part of our analysis.
First decrypted string;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Exodus || exodus || Partitions || cache || dictionar
Atomic || atomic || cache || IndexedDB
JaxxLiberty || com.liberty.jaxx || cache
Coinomi || Coinomi\Coinomi\wallets || null-
Electrum || Electrum\wallets || null-
Electrum-LTC || Electrum-LTC\wallets || null-
ElectronCash || ElectronCash\wallets || null-
Guarda || Guarda || cache || IndexedDB
MyMonero || MyMonero || cache
Monero || Monero\\wallets || null-
Wasabi || WalletWasabi\\Client || tor || log
TokenPocket || TokenPocket || cache
Ledger Live || Ledger Live || cache || dictionar || sqlite
Binance || Binance || cache || null-
Second decrypted string;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
fhbohimaelbohpjbbldcngcnapndodjp || BinanceChain
fnjhmkhhmkbjkkabndcnnogagogbneec || Ronin
kjmoohlgokccodicjjfebfomlbljgfhk || Ronin
nkbihfbeogaeaoehlefnkodbefgpgknn || MetaMask
ejbalbakoplchlghecdalmeeeajnimhm || MetaMask
ibnejdfjmmkpcnlpebklmnkoeoihofec || TronLink
egjidjbpglichdcondbcbdnbeeppgdph || TrustWallet
bfnaelmomeimhlpmgjnjophhpkkoljpa || Phantom
hnfanknocfeofbddgcijnmhnfnkdnaad || Coinbase
odbfpeeihdkbihmopkbjmoonfanlbfcl || Brave
cgeeodpfagjceefieflmdfphplkenlfk || TON
aeachknmefphepccionboohckonoeemg || Coin98
mcohilncbfahbmgdjkbpemcciiolgcge || MetaX
hmeobnfnfcmdkdcmlblgagmfpfboieaf || XDEFI
lpilbniiabackdjcionkobglmddfbcjo || WavesKeeper
bhhhlbepdkbapadjdnnojkbgioiodbic || Solflare
acmacodkjbdgmoleebolmdjonilkdbch || Rabby
dkdedlpgdmmkkfjabffeganieamfklkm || CyanoWallet
cnmamaachppnkjgnildpdmkaakejnhae || AuroWallet
hcflpincpppdclinealmandijcmnkbgn || KHC
mnfifefkajgofkcjkemidiaecocnkjeh || TezBox
ookjlbkiijinhpmnjffcofjonbfbgaoc || Temple
flpiciilemghbmfalicajoolhkkenfel || ICONex
fhmfendgdocmcbmfikdcogofphimnkno || Sollet
nhnkbkgjikgcigadomkphalanndcapjk || CloverWallet
jojhfeoedkpkglbfimdfabpdfjaoolaf || PolymeshWallet
cphhlgmgameodnhkjdmkpanlelnlohao || NeoLine
dmkamcknogkgcdfhhbddcghachkejeap || Keplr
ajkhoeiiokighlmdnlakpjfoobnjinie || TerraStation
aiifbnbfobpmeekipheeijimdpnlpgpp || TerraStation
kpfopkelmapcoipemfendmdcghnegimn || Liquality
nkddgncdjgjfcddamfgcmfnlhccnimig || SaturnWallet
nanjmdknhkinifnkgdcggcfnhdaammmj || GuildWallet
jnkelfanjkeadonecabehalmbgpfodjm || Goby
nphplpgoakhhjchkkhmiggakijnkhfnd || TON
fpkhgmpbidmiogeglndfbkegfdlnajnf || Cosmostation
jiidiaalihmmhddjgbnbgdfflelocpak || BitKeep
pgiaagfkgcbnmiiolekcfmljdagdhlcm || Stargazer
cjelfplplebdjjenllpjcblmjkfcffne || JaxxLiberty
kkpllkodjeloidieedojogacfhpaihoh || Enkrypt
pkkjjapmlcncipeecdmlhaipahfdphkd || GameStopWallet
aholpfdialjgjfhomihkjbmgjidlcdno || ExodusWeb3Wallet
nngceckbapebfimnlniiiahkandclblb || Bitwarden
efbglgofoippbgcjepnhiblaibcnclgk || MartianAptos
jnlgamecbpmbajjfhmmmlhejkemejdma || Braavos
mcohilncbfahbmgdjkbpemcciiolgcge || OKX
phkbamefinggmakgklpkljjmgibohnba || PontemAptos
epapihdplajcdnnkdeiahlgigofloibg || SenderWallet
gjagmgiddbbciopjhllkdnddhcglnemk || Hashpack
cgeeodpfagjceefieflmdfphplkenlfk || EVER
cjmkndjhnagcfbpiemnkdpomccnjblmj || Finnie
aijcbedoijmgnlmjeegjaglmepbmpkpi || LeapTerra
ejjladinnckdgjemekebdpeokbikhfci || PetraAptos
kmhcihpebfmpgmihbkipmjlmmioameka || Eternl
bgpipimickeadkjlklgciifhnalhdjhe || GeroWallet
lpfcbjknijpeeillifnkikgncikgfhdo || NamiWallet
pocmplpaccanhmnllbbkpgfliimjljgo || SlopeWallet
ffnbelfdoeiohenkjibnmadjiehjhajb || Yoroi
afbcbjpbpfadlkmhmclhkeeodmamcflc || Math
hpglfhgfnhbgpjdenjgmdgoeiappafln || Guarda
kncchdigobghenbbaddojjnnaogfppfj || iWallet
amkmjjmmflddogmhpjloimipbofnfjih || Wombat
nlbmnnijcnlegkjjpcfjclmcfggfefdm || MEWCX
nknhiehlklippafakaeklbeglecifhad || NaboxWallet
jnmbobjmhlngoefaiojfljckilhhlhcj || OneKey
pdadjkfkgcafgbceimcpbkalnfnepbnk || KardiaChainWallet
setup_utils_AntiCIS Function
In the main
function screenshot, we see that the first call made by the malware is to setup_utils_AntiCIS
. This function begins by decrypting and utilizing user32.dll
and GetKeyboardLayoutList
. It then invokes GetKeyboardLayoutList
and compares the obtained keyboard layouts with a preset list. From its name and behavior, it’s clear that this function is designed to check for keyboard layouts typical of the CIS region countries
, and if a match is detected, the malware promptly exits without executing further.
Interestingly, one of the hex values, 443h
, does not have a corresponding keyboard identifier in Microsoft’s official documentation on Windows keyboard layouts.
The AntiCIS
function uniquely targets devices based on their geographic location, specifically halting execution on those with CIS region keyboard layouts. This is a common tactic in malware, typically to avoid affecting systems in the developers’ own region, possibly due to legal or ethical considerations.
setup_utils_Connection Function
The setup_utils_Connection
function straightforwardly initiates a TCP
connection between the C2 server (185.221.198.118:8080) and the infected device. Should it fail to establish this connection to the C2 address, the function will terminate the process.
Once this connection is successfully established, the malware initiates its data collection process.
main_launchUserInfo Function
The main_launchUserInfo
function initially creates a folder named \logs\MainFolderLog
, then proceeds to gather various system details like CPU
, GPU
, RAM
, OS
, TimeZone
, Language
, and Architecture
, storing this information in \logs\MainFolderLog\UserInformation.txt
. It also compiles a list of installed applications
, saving it in \logs\MainFolderLog\InstalledSoftware.txt
’ Finally, it encrypts this data with AES GCM and transmits it to the C2 server. The specifics of C2 traffic encryption will be addressed later in our analysis.
Data format;
1
2
3
4
5
6
7
8
9
10
11
12
13
Tags: %s
BuildId: %s
UserName: %s
CPU: %s
GPU: %s
RAM: %s
OS: %s
Current date: %s
TimeZone: %s
Language: %s
Architecture: %s
Screen: %s
HWID: %s
main_launchDesktopwallet Function
The main_launchDesktopwallet
function scans for the decrypted wallet folder names within the \AppData\Roaming
directory. Upon finding a match, it encrypts the relevant data and transmits it to the C2 server.
Wallet list;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Exodus || exodus || Partitions || cache || dictionar
Atomic || atomic || cache || IndexedDB
JaxxLiberty || com.liberty.jaxx || cache
Coinomi || Coinomi\Coinomi\wallets || null-
Electrum || Electrum\wallets || null-
Electrum-LTC || Electrum-LTC\wallets || null-
ElectronCash || ElectronCash\wallets || null-
Guarda || Guarda || cache || IndexedDB
MyMonero || MyMonero || cache
Monero || Monero\\wallets || null-
Wasabi || WalletWasabi\\Client || tor || log
TokenPocket || TokenPocket || cache
Ledger Live || Ledger Live || cache || dictionar || sqlite
Binance || Binance || cache || null-
main_launchwallet Function
Functioning in a recursive manner, main_launchwallet
explores both the \AppData\Roaming
and \AppData\Local
directories, targeting the previously decrypted names of wallet browser extensions located in the various browsers’ Extensions
folders.
Wallet Extension List;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
fhbohimaelbohpjbbldcngcnapndodjp || BinanceChain
fnjhmkhhmkbjkkabndcnnogagogbneec || Ronin
kjmoohlgokccodicjjfebfomlbljgfhk || Ronin
nkbihfbeogaeaoehlefnkodbefgpgknn || MetaMask
ejbalbakoplchlghecdalmeeeajnimhm || MetaMask
ibnejdfjmmkpcnlpebklmnkoeoihofec || TronLink
egjidjbpglichdcondbcbdnbeeppgdph || TrustWallet
bfnaelmomeimhlpmgjnjophhpkkoljpa || Phantom
hnfanknocfeofbddgcijnmhnfnkdnaad || Coinbase
odbfpeeihdkbihmopkbjmoonfanlbfcl || Brave
cgeeodpfagjceefieflmdfphplkenlfk || TON
aeachknmefphepccionboohckonoeemg || Coin98
mcohilncbfahbmgdjkbpemcciiolgcge || MetaX
hmeobnfnfcmdkdcmlblgagmfpfboieaf || XDEFI
lpilbniiabackdjcionkobglmddfbcjo || WavesKeeper
bhhhlbepdkbapadjdnnojkbgioiodbic || Solflare
acmacodkjbdgmoleebolmdjonilkdbch || Rabby
dkdedlpgdmmkkfjabffeganieamfklkm || CyanoWallet
cnmamaachppnkjgnildpdmkaakejnhae || AuroWallet
hcflpincpppdclinealmandijcmnkbgn || KHC
mnfifefkajgofkcjkemidiaecocnkjeh || TezBox
ookjlbkiijinhpmnjffcofjonbfbgaoc || Temple
flpiciilemghbmfalicajoolhkkenfel || ICONex
fhmfendgdocmcbmfikdcogofphimnkno || Sollet
nhnkbkgjikgcigadomkphalanndcapjk || CloverWallet
jojhfeoedkpkglbfimdfabpdfjaoolaf || PolymeshWallet
cphhlgmgameodnhkjdmkpanlelnlohao || NeoLine
dmkamcknogkgcdfhhbddcghachkejeap || Keplr
ajkhoeiiokighlmdnlakpjfoobnjinie || TerraStation
aiifbnbfobpmeekipheeijimdpnlpgpp || TerraStation
kpfopkelmapcoipemfendmdcghnegimn || Liquality
nkddgncdjgjfcddamfgcmfnlhccnimig || SaturnWallet
nanjmdknhkinifnkgdcggcfnhdaammmj || GuildWallet
jnkelfanjkeadonecabehalmbgpfodjm || Goby
nphplpgoakhhjchkkhmiggakijnkhfnd || TON
fpkhgmpbidmiogeglndfbkegfdlnajnf || Cosmostation
jiidiaalihmmhddjgbnbgdfflelocpak || BitKeep
pgiaagfkgcbnmiiolekcfmljdagdhlcm || Stargazer
cjelfplplebdjjenllpjcblmjkfcffne || JaxxLiberty
kkpllkodjeloidieedojogacfhpaihoh || Enkrypt
pkkjjapmlcncipeecdmlhaipahfdphkd || GameStopWallet
aholpfdialjgjfhomihkjbmgjidlcdno || ExodusWeb3Wallet
nngceckbapebfimnlniiiahkandclblb || Bitwarden
efbglgofoippbgcjepnhiblaibcnclgk || MartianAptos
jnlgamecbpmbajjfhmmmlhejkemejdma || Braavos
mcohilncbfahbmgdjkbpemcciiolgcge || OKX
phkbamefinggmakgklpkljjmgibohnba || PontemAptos
epapihdplajcdnnkdeiahlgigofloibg || SenderWallet
gjagmgiddbbciopjhllkdnddhcglnemk || Hashpack
cgeeodpfagjceefieflmdfphplkenlfk || EVER
cjmkndjhnagcfbpiemnkdpomccnjblmj || Finnie
aijcbedoijmgnlmjeegjaglmepbmpkpi || LeapTerra
ejjladinnckdgjemekebdpeokbikhfci || PetraAptos
kmhcihpebfmpgmihbkipmjlmmioameka || Eternl
bgpipimickeadkjlklgciifhnalhdjhe || GeroWallet
lpfcbjknijpeeillifnkikgncikgfhdo || NamiWallet
pocmplpaccanhmnllbbkpgfliimjljgo || SlopeWallet
ffnbelfdoeiohenkjibnmadjiehjhajb || Yoroi
afbcbjpbpfadlkmhmclhkeeodmamcflc || Math
hpglfhgfnhbgpjdenjgmdgoeiappafln || Guarda
kncchdigobghenbbaddojjnnaogfppfj || iWallet
amkmjjmmflddogmhpjloimipbofnfjih || Wombat
nlbmnnijcnlegkjjpcfjclmcfggfefdm || MEWCX
nknhiehlklippafakaeklbeglecifhad || NaboxWallet
jnmbobjmhlngoefaiojfljckilhhlhcj || OneKey
pdadjkfkgcafgbceimcpbkalnfnepbnk || KardiaChainWallet
main_launchBrowser Function
Serving as a typical stealer function, main_launchBrowser
is dedicated to harvesting browser data, including login details
and cookies
. After gathering this data, the function encrypts it and then sends it off to the C2.
main_launchTelegram Function
The main_launchTelegram
function targets the \AppData\Roaming\Telegram Desktop\tdata
directory, extracting critical files such as key_data
and usertag
. Following the theft, it encrypts this data and dispatches it to the C2 server.
main_launchSteam Function
The main_launchSteam
function is designed to gather data from the Steam
desktop application, including auto-login
information, game lists
, and statuses
. This stolen data is compiled into SteamInfo.txt
, encrypted, and then transmitted to the C2 server.
main_launchScreenshot Function
As its name suggests, the main_launchScreenshot
function captures screenshots of the current windows, saving them with the naming format ScreenShot (%d_%dx%d).png
. After saving, it encrypts these screenshots and transmits them to the C2 server.
main_launchDiscord Function
The main_launchDiscord
function searches through the \AppData\Local
and \AppData\Roaming
directories to locate leveldb files associated with the Discord Desktop app. After collecting this data, it compiles it into ‘Tokens.txt’, encrypts the file, and sends it to the C2 server.
setup_utils_Send Function
The setup_utils_Send
function initially stores the stolen data files in the \logs
folder. It then decrypts a string for use in the traffic encryption routine, generates a nonce for AES GCM
encryption, and calculates the MD5
of previously decrypted string to serve as the AES GCM
encryption key. Finally, it encrypts the data and transmits it to the C2 server.
Decrypted string;
Calculating MD5 and starts AES GCM;
The encrypted traffic can be decrypted using Python, as demonstrated by the following code snippet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from binascii import unhexlify
def decrypt(cipher_hex, key):
cipher_bytes = unhexlify(cipher_hex)
nonce_size = 12
tag_size = 16
nonce = cipher_bytes[:nonce_size]
tag = cipher_bytes[-tag_size:]
cipher_text = cipher_bytes[nonce_size:-tag_size]
decryptor = Cipher(
algorithms.AES(key),
modes.GCM(nonce, tag),
backend=default_backend()
).decryptor()
return decryptor.update(cipher_text) + decryptor.finalize()
key = b'7b1da832deb12b2dd4065c7c43aec101'
encrypted_hex = ''
decrypted_text = decrypt(encrypted_hex, key)
print("Decrypted text:", decrypted_text)
To maintain the confidentiality of my VM’s specifics, I’ve truncated and randomized the decrypted data, but the representation below illustrates how it appears.
1
Decrypted text: b'G\xff\x81\x03\x01\x01\x07FileObj\x01\xff\x82\x00\x01\x04\x01\x08FileName\x01\x0c\x00\x01\x08IsFolder\x01\x02\x00\x01\x08FileByte\x01\n\x00\x01\x05Files\x01\xff\x84\x00\x00\x00\x1e\xff\x83\x02\x01\x01\x0f[]utils.FileObj\x01\xff\x84\x00\x01\xff\x82\x00\x00\xfe\x07\xdc\xff\x82\x01\rMainFolderLog\x01\x01\x02\x02\x01\x13UserInformation.txt\x02\xfe\x01NTags: Installs\nBuildId: Installs\nUserName: WIN10\\admin\nCPU: 14th Gen Intel(R) Core(TM) i15-15200 (1 cores)\nGPU: VMware SVGA 3D\nRAM: 8190 MB\nOS: Microsoft Windows 10 Enterprise\nCurrent date: 2099.08.29 00:05:04\nTimeZone: UTC +2 Hours\nLanguage: en-US\nArchitecture: x64-based PC\nScreen: 3238 x 1274\nHWID: 3782542AB18F411BC42897C0D3D0FB3E\n\n\x00\x01\x15InstalledSoftware.txt\x02\xfe\x06B%!(EXTRA string=\t7-Zip 18.01 (x64)\n\tExplorer Suite IV\n\tHxD Hex Editor 2.5\n\tIDA Freeware 8.3\n\t\n)\x00\x00'
The presence of non-ASCII characters in the decrypted data suggests the possibility of serialized object usage.
Part 3 - C2 Panel
While all traffic between the malware and the C2 server is encrypted and sent via TCP
to the server’s port 8080
, @spamhaus’s tweet also indicates the existence of an HTTP web application used for the C2 panel.
As of the time of writing this article, the C2 server shared by @spamhaus is currently inaccessible.
Guided by @spamhaus’s tweet, which identifies the malware as Hornet Stealer
based on the panel logo
and name
, we can conduct targeted Censys searches using queries like;
1
2
3
4
5
services.http.response.body:"Welcome back!" AND services.http.response.body:"Happy to see you again!" AND services.http.response.body:"Username" AND services.http.response.body:"Password" AND services.port:8080
services.http.response.body:"Hornet Stealer"
services.http.response.body:"Hornet" AND services.http.response.body:"Stealer"
However, none of these queries succeeded in finding a new C2 address.
Part 4 - Yara Rule and String Extractor
Yara Rule
In the Yara Rule section of our article, considering the encryption of most strings, we will concentrate on certain key aspects and plaintext strings for crafting our Yara rule. This includes focusing on encryption methods utilized by the malware, such as Fernet
and AES-GCM
, as well as unique user code function names.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
rule HornetStealer_Golang {
meta:
author = "tufan - @tufan_gngr"
description = "Detects Hornet stealer non-stripped samples"
date = "2024-01-30"
references = "https://tufan-gungor.github.io/"
references = "https://twitter.com/spamhaus/status/1750170178493526350"
hash = "bc3ee10c21cb07bc0dd6b84a6eaf8efbd0af889467ab7ef647acf60f8c188e83"
strings:
$s1 = "main"
$s2 = "setup/utils"
$s3 = "crypto/cipher.NewGCM"
$s4 = "fernet"
$a1 = "launchBrowser"
$a2 = "launchDesktopWallet"
$a3 = "launchDiscord"
$a4 = "launchSteam"
$a5 = "launchTelegram"
$a6 = "launchWallet"
$a7 = "launchUserInfo"
condition:
uint16(0) == 0x5a4d and
(5 of ($a*)) or
(#s1 > 10 and #s2 > 20 and $s3 and $s4)
}
The Yara searches conducted on Unpacme
and VirusTotal
did not yield any new samples, only returning the one we already possess.
In addition to our Yara rule, other string searches, as listed below, also failed to yield any results.
1
2
3
C:/Users/admin/Desktop/GOAdmin
main.launchDesktopwallet
setup/utils.Send
String Extractor / Decryptor
In the String Extractor/Decryptor segment, we now focus on developing a Python decryptor for the malware’s encrypted strings, given that we have the necessary tools at hand. A notable difficulty, however, lies in the Golang structure, where the lack of null bytes
between strings complicates their extraction without causing breaks.
Consequently, we will employ Mandiant’s tool known as FLOSS (FLARE Obfuscated String Solver) for extracting strings from the Golang binary. As of December 12, 2023, FLOSS has included support for Golang string extraction.
Our first step is to execute FLOSS
on the Hornet Stealer
, and then we will capture and store the resulting output in an output.txt
file.”
1
.\floss.exe .\hornet.exe > output.txt
Subsequently, our string decryptor will process each line from the output.txt
file, decrypting them with the Fernet
key. Successful decryptions will result in the decrypted text being printed.
Here is our Python code;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from cryptography.fernet import Fernet
def decrypt_line(line, key):
fernet = Fernet(key)
try:
decrypted_data = fernet.decrypt(line.encode())
return decrypted_data.decode()
except Exception as e:
return None
def decrypt_file(filename, key):
with open(filename, 'r', errors='ignore') as file:
for line in file:
line = line.strip()
if line and len(line) > 10:
decrypted_data = decrypt_line(line, key)
if decrypted_data:
print("Decrypted Data:", decrypted_data)
if __name__ == "__main__":
filename = 'output.txt'
key = 'MpQzH0ne3b-TkBgkJ0tbdALxiCiJuLBleGUlEoIGQoo='
decrypt_file(filename, key)
Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
Decrypted Data: Tags: %s
BuildId: %s
UserName: %s
CPU: %s
GPU: %s
RAM: %s
OS: %s
Current date: %s
TimeZone: %s
Language: %s
Architecture: %s
Screen: %s
HWID: %s
Decrypted Data: user32.dll
Decrypted Data: usertag
Decrypted Data: False
Decrypted Data: Tokens.txt
Decrypted Data: LocalFree
Decrypted Data: Kernel32.dll
Decrypted Data: Language
Decrypted Data: SteamInfo.txt
Decrypted Data: leveldb
Decrypted Data: Wallet
Decrypted Data: Login Data
Decrypted Data: MainFolderLog
Decrypted Data: key_data
Decrypted Data: .ldb
Decrypted Data: DisplayName
Decrypted Data: Web Data
Decrypted Data: vdf
Decrypted Data: False
Decrypted Data: cookies.sqlite
Decrypted Data: Telegram
Decrypted Data: Installs
Decrypted Data: Desktop Wallets
Decrypted Data: Running
Decrypted Data: Installs
Decrypted Data: logins.json
Decrypted Data: ssfn
Decrypted Data: Installed
Decrypted Data: .log
Decrypted Data: Local State
Decrypted Data: Updating
Decrypted Data: SteamPath
Decrypted Data: AutoLoginUser
Decrypted Data: tdata
Decrypted Data: False
Decrypted Data: Crypt32.dll
Decrypted Data: settings
Decrypted Data: Extensions
Decrypted Data: UserInformation.txt
Decrypted Data: GetKeyboardLayoutList
Decrypted Data: InstalledSoftware.txt
Decrypted Data: CryptUnprotectData
Decrypted Data: Software\\Valve\\Steam
Decrypted Data: discord\\Local Storage\\leveldb
Decrypted Data: os_crypt.encrypted_key
Decrypted Data: Software\\Valve\\Steam\\Apps\\
Decrypted Data: AutoLogin: %s\nLanguage: %s\n\n
Decrypted Data: 185.221.198.118:8080
Decrypted Data: Software\\Valve\\Steam\\Apps
Decrypted Data: JAGSDiusuidsgdisbdhb32te72hqbsilydfg1
Decrypted Data: discordptb\\Local Storage\\leveldb
Decrypted Data: discordcanary\\Local Storage\\leveldb
Decrypted Data: [\w-]{24}\.[\w-]{6}\.[\w-]{25,110}
Decrypted Data: 5hKEw9TAVDZPA6CblkDK86Dhd9HF1E5B
Decrypted Data: SELECT ExecutablePath FROM Win32_Process WHERE Name =
Decrypted Data: SELECT Caption, MUILanguages FROM win32_operatingsystem
Decrypted Data: SELECT Name, NumberOfCores, ProcessorId FROM Win32_Processor
Decrypted Data: SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall
Decrypted Data: SELECT VolumeSerialNumber FROM Win32_LogicalDisk WHERE DeviceID =
Decrypted Data: Select Model, SystemType, TotalPhysicalMemory, CurrentTimeZone From Win32_ComputerSystem
Decrypted Data: SELECT Name, CurrentHorizontalResolution, CurrentVerticalResolution FROM Win32_VideoController
Decrypted Data: Application %s\n\tGameID: %s\n\tInstalled: %s\n\tRunning: %s\n\tUpdating: %s\n\n
Decrypted Data: Telegram Desktop
Decrypted Data: Tags: %s
BuildId: %s
UserName: %s
CPU: %s
GPU: %s
RAM: %s
OS: %s
Current date: %s
TimeZone: %s
Language: %s
Architecture: %s
Screen: %s
HWID: %s
Decrypted Data: Exodus || exodus || Partitions || cache || dictionar
Atomic || atomic || cache || IndexedDB
JaxxLiberty || com.liberty.jaxx || cache
Coinomi || Coinomi\Coinomi\wallets || null-
Electrum || Electrum\wallets || null-
Electrum-LTC || Electrum-LTC\wallets || null-
ElectronCash || ElectronCash\wallets || null-
Guarda || Guarda || cache || IndexedDB
MyMonero || MyMonero || cache
Monero || Monero\\wallets || null-
Wasabi || WalletWasabi\\Client || tor || log
TokenPocket || TokenPocket || cache
Ledger Live || Ledger Live || cache || dictionar || sqlite
Binance || Binance || cache || null-
Decrypted Data: fhbohimaelbohpjbbldcngcnapndodjp || BinanceChain
fnjhmkhhmkbjkkabndcnnogagogbneec || Ronin
kjmoohlgokccodicjjfebfomlbljgfhk || Ronin
nkbihfbeogaeaoehlefnkodbefgpgknn || MetaMask
ejbalbakoplchlghecdalmeeeajnimhm || MetaMask
ibnejdfjmmkpcnlpebklmnkoeoihofec || TronLink
egjidjbpglichdcondbcbdnbeeppgdph || TrustWallet
bfnaelmomeimhlpmgjnjophhpkkoljpa || Phantom
hnfanknocfeofbddgcijnmhnfnkdnaad || Coinbase
odbfpeeihdkbihmopkbjmoonfanlbfcl || Brave
cgeeodpfagjceefieflmdfphplkenlfk || TON
aeachknmefphepccionboohckonoeemg || Coin98
mcohilncbfahbmgdjkbpemcciiolgcge || MetaX
hmeobnfnfcmdkdcmlblgagmfpfboieaf || XDEFI
lpilbniiabackdjcionkobglmddfbcjo || WavesKeeper
bhhhlbepdkbapadjdnnojkbgioiodbic || Solflare
acmacodkjbdgmoleebolmdjonilkdbch || Rabby
dkdedlpgdmmkkfjabffeganieamfklkm || CyanoWallet
cnmamaachppnkjgnildpdmkaakejnhae || AuroWallet
hcflpincpppdclinealmandijcmnkbgn || KHC
mnfifefkajgofkcjkemidiaecocnkjeh || TezBox
ookjlbkiijinhpmnjffcofjonbfbgaoc || Temple
flpiciilemghbmfalicajoolhkkenfel || ICONex
fhmfendgdocmcbmfikdcogofphimnkno || Sollet
nhnkbkgjikgcigadomkphalanndcapjk || CloverWallet
jojhfeoedkpkglbfimdfabpdfjaoolaf || PolymeshWallet
cphhlgmgameodnhkjdmkpanlelnlohao || NeoLine
dmkamcknogkgcdfhhbddcghachkejeap || Keplr
ajkhoeiiokighlmdnlakpjfoobnjinie || TerraStation
aiifbnbfobpmeekipheeijimdpnlpgpp || TerraStation
kpfopkelmapcoipemfendmdcghnegimn || Liquality
nkddgncdjgjfcddamfgcmfnlhccnimig || SaturnWallet
nanjmdknhkinifnkgdcggcfnhdaammmj || GuildWallet
jnkelfanjkeadonecabehalmbgpfodjm || Goby
nphplpgoakhhjchkkhmiggakijnkhfnd || TON
fpkhgmpbidmiogeglndfbkegfdlnajnf || Cosmostation
jiidiaalihmmhddjgbnbgdfflelocpak || BitKeep
pgiaagfkgcbnmiiolekcfmljdagdhlcm || Stargazer
cjelfplplebdjjenllpjcblmjkfcffne || JaxxLiberty
kkpllkodjeloidieedojogacfhpaihoh || Enkrypt
pkkjjapmlcncipeecdmlhaipahfdphkd || GameStopWallet
aholpfdialjgjfhomihkjbmgjidlcdno || ExodusWeb3Wallet
nngceckbapebfimnlniiiahkandclblb || Bitwarden
efbglgofoippbgcjepnhiblaibcnclgk || MartianAptos
jnlgamecbpmbajjfhmmmlhejkemejdma || Braavos
mcohilncbfahbmgdjkbpemcciiolgcge || OKX
phkbamefinggmakgklpkljjmgibohnba || PontemAptos
epapihdplajcdnnkdeiahlgigofloibg || SenderWallet
gjagmgiddbbciopjhllkdnddhcglnemk || Hashpack
cgeeodpfagjceefieflmdfphplkenlfk || EVER
cjmkndjhnagcfbpiemnkdpomccnjblmj || Finnie
aijcbedoijmgnlmjeegjaglmepbmpkpi || LeapTerra
ejjladinnckdgjemekebdpeokbikhfci || PetraAptos
kmhcihpebfmpgmihbkipmjlmmioameka || Eternl
bgpipimickeadkjlklgciifhnalhdjhe || GeroWallet
lpfcbjknijpeeillifnkikgncikgfhdo || NamiWallet
pocmplpaccanhmnllbbkpgfliimjljgo || SlopeWallet
ffnbelfdoeiohenkjibnmadjiehjhajb || Yoroi
afbcbjpbpfadlkmhmclhkeeodmamcflc || Math
hpglfhgfnhbgpjdenjgmdgoeiappafln || Guarda
kncchdigobghenbbaddojjnnaogfppfj || iWallet
amkmjjmmflddogmhpjloimipbofnfjih || Wombat
nlbmnnijcnlegkjjpcfjclmcfggfefdm || MEWCX
nknhiehlklippafakaeklbeglecifhad || NaboxWallet
jnmbobjmhlngoefaiojfljckilhhlhcj || OneKey
pdadjkfkgcafgbceimcpbkalnfnepbnk || KardiaChainWallet
Part 5 - IOCs
1
2
bc3ee10c21cb07bc0dd6b84a6eaf8efbd0af889467ab7ef647acf60f8c188e83
185.221.198.118:8080
References
I utilized AI assistance to fine-tune certain sentences in this post, enhancing clarity and precision.