惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
Vulnerabilities – Threatpost
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy International News Feed
H
Heimdal Security Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Secure Thoughts
MyScale Blog
MyScale Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
V
Visual Studio Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
L
LINUX DO - 最新话题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Cloudflare Blog
美团技术团队
Recorded Future
Recorded Future
T
Tailwind CSS Blog
Latest news
Latest news
Security Archives - TechRepublic
Security Archives - TechRepublic
Security Latest
Security Latest
Know Your Adversary
Know Your Adversary
Cloudbric
Cloudbric
Schneier on Security
Schneier on Security
I
Intezer
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
云风的 BLOG
云风的 BLOG
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Vercel News
Vercel News
Attack and Defense Labs
Attack and Defense Labs
人人都是产品经理
人人都是产品经理
L
LangChain Blog
爱范儿
爱范儿
博客园 - 三生石上(FineUI控件)
博客园 - 叶小钗
L
Lohrmann on Cybersecurity
S
SegmentFault 最新的问题
W
WeLiveSecurity
C
Cybersecurity and Infrastructure Security Agency CISA
S
Securelist
SecWiki News
SecWiki News
V2EX - 技术
V2EX - 技术
IT之家
IT之家
Cyberwarzone
Cyberwarzone
F
Full Disclosure
Spread Privacy
Spread Privacy
阮一峰的网络日志
阮一峰的网络日志

Elastic Security Labs

Agentic SOC architecture: specialized agents cut costs 5.7× — Elastic Security Labs ES|QL COMPLETION: LLM triage for noisy detection rules — Elastic Security Labs wp2shell: detecting WordPress pre-auth RCE end-to-end — Elastic Security Labs Contagious Interview malware in SVG images: DPRK campaign — Elastic Security Labs TELEPUZ: a modular MaaS malware spreading via CLICKFIX-VIDAR chains — Elastic Security Labs REF6045: Mexican banking fraud toolkit with signs of AI-assisted development — Elastic Security Labs Alert triage in under 3 minutes with Elastic's agentic SOC — Elastic Security Labs Security advisory automation with Elastic Agent Builder — Elastic Security Labs OXLOADER: new loader evading detection to drop infostealer — Elastic Security Labs Azure AD Graph Activity Logs: detecting directory enumeration — Elastic Security Labs From API key to live threat detections in minutes: how Elastic Security ingests Google Threat Intelligence Detecting Tycoon 2FA AiTM attacks across Entra ID and Google Workspace PHANTOMPULSE: anatomy of a hijackable blockchain-C2 RAT Elastic Security MCP App: Interactive security operations inside your AI Tools Copy Fail and DirtyFrag: Linux Page Cache Bugs in the Wild Detecting Web Server Probing & Fuzzing in Traefik with Automated Cloudflare Response TCLBANKER: Brazilian Banking Trojan Spreading via WhatsApp and Outlook Elastic Workflows GA: automation where your security data already lives Your UEBA is lying to you: Why entity record quality decides everything Know who to watch before the incident finds you AI-generated hunting leads: The hunt starts before you ask the question From plain English to production rule: AI-native Elasticsearch ES|QL detection in Elastic Security Elastic Conversational Entity Analytics: threat hunting in a single conversation One agent, the right skills: Elastic Security 9.4 brings domain expertise on demand to every SOC workflow DFIR: From alert to root cause using Osquery without leaving Elastic Security CI/CD pipeline abuse: the problem no one is watching — Elastic Security Labs Monitoring Claude Code/Cowork at scale with OTel in Elastic The Cost of Understanding: LLM-Driven Reverse Engineering vs Iterative LLM Obfuscation — Elastic Security Labs Phantom in the vault: Obsidian abused to deliver PhantomPulse RAT — Elastic Security Labs Elastic on Defence Cyber Marvel 2026: A Technical overview from the Exercise Floor Elastic Security Integrations Roundup: Q1 2026 Prioritizing Alerts Triage with Higher-Order Detection Rules How we caught the Axios supply chain attack Hooked on Linux: Rootkit Detection Engineering Inside the Axios supply chain compromise - one RAT to rule them all Elastic releases detections for the Axios supply chain compromise Fake Installers to Monero: A Multi-Tool Mining Operation Illuminating VoidLink: Technical analysis of the VoidLink rootkit framework Investigating from the Endpoint Across Your Environment with Elastic Security XDR Security Automation with Elastic Workflows: From Alert to Response Streamlining the Security Analyst Experience Supercharge Your SOC Linux & Cloud Detection Engineering - TeamPCP Container Attack Scenario From Invitation to Infection: How SILENTCONNECT Delivers ScreenConnect Linux & Cloud Detection Engineering - Getting Started with Defend for Containers (D4C) Get started with Elastic Security from your AI agent Managing Elastic Security Detection Rules with Terraform Patch diff to SYSTEM
Elastic Security Labs uncovers BRUSHWORM and BRUSHLOGGER
Salim Bitam · 2026-03-27 · via Elastic Security Labs

Key takeaways

  • A South Asian financial institution was targeted with two custom malware components: a modular backdoor (BRUSHWORM) and a keylogger (BRUSHLOGGER)
  • BRUSHWORM features anti-analysis checks, AES-CBC encrypted configuration, scheduled task persistence, modular DLL payload downloading, USB worm propagation, and broad file theft targeting documents, spreadsheets, email archives, and source code
  • The keylogger masquerades as libcurl via DLL side-loading, capturing system-wide keystrokes with window context tracking and XOR-encrypted log files
  • Multiple earlier testing versions (V1.exe, V2.exe, etc.) were discovered on VirusTotal, some using free dynamic DNS infrastructure, indicating iterative development

Introduction

During a recent investigation, Elastic Security Labs identified malware deployed on a South Asian financial institution’s infrastructure. The victim environment had only SIEM-level visibility enabled, which limited post-exploitation telemetry. The intrusion involved two custom binaries: a backdoor named paint.exe and a keylogger masquerading as libcurl.dll.

BRUSHWORM functions as the primary implant, responsible for installation, persistence, command-and-control communication, downloading additional modular payloads, spreading via removable media, and stealing files with targeted extensions. BRUSHLOGGER supplements this by capturing system-wide keystrokes via a simple Windows keyboard hook and logging the active window context for each keystroke session.

Neither binary employs meaningful code obfuscation, packing, or advanced anti-analysis techniques. The overall quality of the code is low — for example, the backdoor writes its decrypted configuration to disk in cleartext before encrypting and saving a second copy, then deletes the cleartext file. Given the absence of a kill switch, the use of free dynamic DNS servers in testing versions, and some coding mistakes, we assess with moderate confidence that the author is relatively inexperienced and may have leveraged AI code-generation tools during development without fully reviewing the output.

Through VirusTotal pivoting, we identified what appear to be earlier development and testing versions of the backdoor uploaded under filenames such as V1.exe, V2.exe, and V4.exe, with varying configurations.

BRUSHWORM code analysis

The backdoor is the primary implant responsible for establishing persistence, communicating with the C2 server, downloading modular payloads, spreading to removable media, and exfiltrating files.

Anti-analysis and sandbox detection

The malware begins execution with a series of environment checks designed to detect analysis environments, though the techniques are straightforward and lack sophistication.

Screen resolution check: If the display resolution is less than 1024×768 pixels, execution terminates immediately. This is a common sandbox detection technique.

Username and computer name check: The malware checks whether the machine's username or the computer name is “sandbox”. If either condition matches, it terminates. These checks target the default name commonly used in analysis sandboxes.

Hypervisor detection: Using the CPUID instruction, the malware queries the hypervisor vendor string and compares it against the following known virtualization platforms:

Hypervisor Vendor StringPlatform
VMWAREVMWAREVMware
KVMKVMKVMKVM
XENVMMXENVMMXen
PRL HYPERVParallels
TCGTCGTCGTCGQEMU (TCG)
ACRNACRNACRNACRN
MICROSOFT HVHyper-V

If a hypervisor is detected, the malware does not terminate — it merely sleeps for one second before continuing execution.

Mouse activity check: After the initial setup, the malware monitors mouse movement for 5 minutes. If the cursor does not move during this period, execution terminates. This acts as an additional sandbox evasion measure.

Installation and directory setup

On first execution, the malware creates multiple hidden directories with hardcoded paths. These directories serve distinct roles throughout the malware's lifecycle:

DirectoryPurpose
C:\ProgramData\Photoes\Pics\Main installation folder for the backdoor binary
C:\Users\Public\Libraries\Storage for downloaded modules from the C2 server (e.g., Recorder.dll)
C:\Users\Public\AppData\Roaming\Microsoft\Vault\Storage of the encrypted configuration file (keyE.dat)
C:\Users\Public\Systeminfo\Staging directory for stolen files
C:\Users\Public\AppData\Roaming\NuGet\Tracks exfiltrated file paths with their SHA-256 hashes (hashconfig)

The misspelling "Photoes" (instead of "Photos") is consistent across both the backdoor and keylogger components and appears to be an authentic mistake by the author to blend with the other user’s media directories.

Configuration decryption

The backdoor's configuration is stored as a JSON structure with field values encrypted using AES-CBC. The AES key is hardcoded in the binary, while the initialization vector (IV) is prepended to each encrypted field's data blob.

The decrypted configuration follows this structure:

{
  "internetCheckDomain": "<...>",
  "downloadDomain": "<...>",
  "retryCount": 0
}

This configuration is not referenced anywhere in the malware's operational logic. The C2 server address that the backdoor actually communicates with is a separate C++ global string variable stored in cleartext(resources.dawnnewsisl[.]com/updtdll), which is passed directly to the function responsible for C2 communication and payload downloads.

The decrypted configuration fields (internetCheckDomain, downloadDomain, retryCount) go entirely unused in this build. This configuration is likely intended for a future version, a separate payload component, or was simply disabled in the deployed build, reinforcing the impression of a codebase under active, disorganized development.

Persistence

The malware establishes persistence by creating a Windows scheduled task named MSGraphics through the COM Task Scheduler interface. The task is configured to execute the malware binary each time a user logs in, ensuring the backdoor survives system reboots.

Payload download and execution

The backdoor uses the WinHTTP library to issue a GET request to the C2 server at the URI /updtdll to download a DLL payload. The downloaded binary is written to C:\Users\Public\Libraries\ as Recorder.dll.

We were unable to recover the downloaded payload during our investigation, but the naming convention and execution method suggest it is a modular plugin — likely providing additional post-exploitation capabilities such as screen recording or data exfiltration.

The downloaded DLL is executed by creating a second scheduled task named MSRecorder that uses rundll32.exe to load and run it. This mirrors the same COM-based scheduled task creation method used for persistence.

The C2 server's SSL certificate is issued by Let's Encrypt.

USB spreading and file theft

Once BRUSHWORM detects that the host is already infected (by checking for the presence of the installation directory), its behavior diverges based on internet connectivity. The malware performs a connectivity check by attempting to reach www.google.com.

Scenario 1 — Internet access available:

The malware spawns two threads targeting external storage devices:

  1. Removable drive infection: The backdoor copies itself to any connected removable storage devices using socially-engineered filenames designed to entice victims in a corporate financial environment:
    • Salary Slips.exe
    • Notes.exe
    • Documents.exe
    • Important.exe
    • Dont Delete.exe
    • Presentation.exe
    • Emails.exe
    • Attachments.exe
  2. File theft from removable and optical drives: Both threads enumerate files on the connected media and exfiltrate to a folder any file matching a broad set of targeted extensions:
CategoryExtensions
Documents & Word Processing.doc, .docx, .dot, .dotx, .wps, .wpd, .wp, .rtf, .txt, .odt, .ott, .pages
Spreadsheets.xls, .xlsx, .xlsm, .xlt, .xltx, .xlw, .ods, .ots, .csv, .tsv, .dbf, .wk1, .wk3, .wk4, .123
Presentations.ppt, .pptx, .pot, .potx, .pps, .ppsx, .odp, .otp, .key, .sxi
Portable & Layout.pdf, .xps, .epub, .mobi, .ps, .prn, .tex, .latex, .pub, .p65, .fm
Archives & Disk Images.zip, .rar, .7z, .tar, .gz, .bz2, .xz, .iso, .cab, .arj, .lzh, .lha, .tgz, .tbz, .txz
Email & Notes.pst, .ost, .msg, .eml, .emlx, .mbox, .mbx, .maildir, .one
Code & Data.py, .md, .xml, .json

Stolen files are staged in the C:\Users\Public\Systeminfo\ directory. The malware also maintains a tracking file (hashconfig) at C:\Users\Public\AppData\Roaming\NuGet\ that records each exfiltrated file's path alongside its SHA-256 hash, likely to avoid re-exfiltrating the same files on subsequent runs.

Scenario 2 — No internet access:

If the internet connectivity check fails, the malware still infects removable drives with the same lure-named copies. However, in this scenario, it additionally copies stolen files and files from the user's profile directory (matching the same extension list) to the removable drives. This behavior serves as a data exfiltration bridge for environments with restricted or air-gapped network access — using USB drives to physically carry stolen data off the network.

BRUSHLOGGER code analysis

The second component is a 32-bit Windows DLL (4f1ea5ed6035e7c951e688bd9c2ec47a1e184a81e9ae783d4a0979501a1985cf) designed for DLL side-loading. It masquerades as libcurl.dll by exporting seven standard curl_easy_* API functions, all of which are empty stubs pointing to a single RET instruction. The malicious functionality executes entirely from the DllMain entry point on DLL_PROCESS_ATTACH.

Initialization

At startup, the keylogger decodes a mutex name from a Base64-encoded string: “Windows-Updates-KB852654856”. The mutex name mimics a Windows Update knowledge base identifier. If CreateMutexA returns ERROR_ALREADY_EXISTS, the process terminates immediately to enforce single-instance execution.

BRUSHLOGGER retrieves the current Windows username via GetUserNameA, computes its MD5 hash using the Windows CryptoAPI, and constructs the final log file path:

C:\programdata\Photoes\<username>_<MD5(username)>.trn

The log file is initially created with CreateFileA using CREATE_NEW, then reopened with FILE_APPEND_DATA access for append-mode writing throughout the session.

Hook installation and message pump

After file setup, the keylogger installs a low-level keyboard hook:

SetWindowsHookExA(WH_KEYBOARD_LL, keyboard_hook_callback, NULL, 0);

The WH_KEYBOARD_LL hook type captures keyboard input system-wide across all threads and processes. The hook procedure runs in the context of the installing thread, requiring a standard Windows message pump (GetMessageA / TranslateMessage / DispatchMessageA) to keep the hook alive.

Keystroke capture logic

The core capture logic resides in the hook callback, which processes every keyboard event in the system.

For each keystroke event, the callback:

  1. Retrieves the foreground window handle via GetForegroundWindow
  2. Allocates memory via VirtualAlloc for the window title
  3. Captures the current timestamp via GetLocalTime, formatted as DD-MM-YYYY HH:MM
  4. Retrieves the window title bar text via GetWindowTextA

When the foreground window changes, a context marker is appended to the keystroke buffer:

\n<timestamp> <window title>\n

Keystroke encoding: The callback processes WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and WM_SYSKEYUP messages. Each keystroke is logged as a two-digit hexadecimal virtual key.

XOR-encrypted log files

The flush routine copies the keystroke buffer to a local stack buffer, XOR-encrypts each byte with a hardcoded single-byte key 0x43, and writes the result to the log file via WriteFile. After a successful write, the global buffer is cleared.

The XOR key 0x43 is trivially reversible — the encryption serves only as basic obfuscation rather than meaningful cryptographic protection.

Conclusion

Despite their low sophistication and multiple implementation flaws, these two binaries deliver a functional collection platform that combines modular payload loading, USB worm propagation, broad file theft with air-gap bridging, and persistent keystroke capture via DLL side-loading. The iterative testing versions and active C2 infrastructure suggest an actor still refining their toolset. Elastic Security Labs will continue monitoring this activity cluster.

BRUSHLOGGER, BRUSHWORM, and MITRE ATT&CK

Elastic uses the MITRE ATT&CK framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.

Tactics

Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.

Techniques

Techniques represent how an adversary achieves a tactical goal by performing an action.

Detecting BRUSHLOGGER and BRUSHWORM

YARA

Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify the BRUSHWORM and BRUSHLOGGER:

rule Windows_Trojan_BrushLogger_304ee146 {
    meta:
        author = "Elastic Security"
        os = "Windows"
        arch = "x86"
        category_type = "Trojan"
        family = "BrushLogger"
        threat_name = "Windows.Trojan.BrushLogger"
        reference_sample = "4f1ea5ed6035e7c951e688bd9c2ec47a1e184a81e9ae783d4a0979501a1985cf"

    strings:
        $a = "%02d-%02d-%d %02d:%02d " fullword
        $b = { 81 ?? ?? A1 00 00 00 74 09 81 ?? ?? A0 00 00 00 75 09 6A 00 6A 10 E8 }
    condition:
        all of them
}

rule Windows_Trojan_BrushWorm_7c2098ef {
    meta:
        author = "Elastic Security"
        os = "Windows"
        arch = "x86"
        category_type = "Trojan"
        family = "BrushWorm"
        threat_name = "Windows.Trojan.BrushWorm"
        reference_sample = "89891aa3867c1a57512d77e8e248d4a35dd32e99dcda0344a633be402df4a9a7"

    strings:
        $a = "internetCheckDomain" wide fullword
        $b = { B8 00 00 00 40 33 C9 0F A2 48 8D ?? ?? ?? 89 07 89 5F 04 89 4F 08 89 57 0C 45 33 C0 }
    condition:
        all of them
}

Observations

The following observables were discussed in this research.

ObservableTypeNameReference
89891aa3867c1a57512d77e8e248d4a35dd32e99dcda0344a633be402df4a9a7SHA-256paint.exeBRUSHWORM
4f1ea5ed6035e7c951e688bd9c2ec47a1e184a81e9ae783d4a0979501a1985cfSHA-256libcurl.dllBRUSHLOGGER
resources.dawnnewsisl[.]com/updtdlldomain-nameC2 server