IBM-Security / ibmsecurity

Idempotent functions for IBM Security Appliance REST APIs. Currently covering ISAM and ISDS Appliances.
Apache License 2.0
47 stars 73 forks source link

Multiple vulnerabilities found in ibmsecurity #416

Closed pba-report-isva closed 7 months ago

pba-report-isva commented 7 months ago

This report has been previously deleted (by mistake?) -

Hello,

During the security assessment of ISVA, several vulnerabilities in ibmsecurity were reported to IBM in March 2023 (case TS012127268 and case TS015273561). A full report detailing these vulnerabilities has been provided to IBM, containing:

Additionally, as a result of this security assessment, several CVEs in ISVA have been published by IBM and additional non-public vulnerabilities will be patched in future releases of ISVA:

Regarding the ibmsecurity Python library, IBM confirmed on March 29, 2024 that these vulnerabilities should be directly reported via Github and that security patches will not be provided via IBM Support (which is the usual case when reporting vulnerabilities to IBM , because IBM PSIRT will redirect to IBM Support).

Following the analysis on ibmsecurity carried out in 2023, IBM confirmed the vulnerabilities in 2023 but stated that they could not manage the security vulnerabilities found in ibmsecurity in March 2024. Therefore, the present analysis was performed on an old version (ibmsecurity-2022.8.22.0 was the latest version in early 2023) but the current version is still vulnerable as shown below.

Please find attached vulnerabilities found in ibmsecurity.

Please credit the security researcher who found the vulnerability as Pierre Barre.

Best regards,

V-93 insecure communications 1/2

The package ibmsecurity has been partially audited as it provides the underlying APIs used by the Python scripts stored in the [REMOVE] directory.

Unfortunately, the security of the ibmsecurity package is very poor and by default, all the SSL/TLS connections to the remote ISVA server are configured in an insecure way.

The latest version of the ibmsecurity library (ibmsecurity-2022.8.22.0) has been downloaded using pip in order for the source code to be reviewed:

kali% pip download ibmsecurity
Collecting ibmsecurity
Using cached ibmsecurity-2022.8.22.0-py3-none-any.whl (391 kB)
Collecting requests
Using cached requests-2.28.1-py3-none-any.whl (62 kB)
Collecting charset-normalizer<3,>=2
Using cached charset_normalizer-2.1.1-py3-none-any.whl (39 kB)
Collecting urllib3<1.27,>=1.21.1
Using cached urllib3-1.26.12-py2.py3-none-any.whl (140 kB)
Collecting certifi>=2017.4.17
Using cached certifi-2022.9.24-py3-none-any.whl (161 kB)
Collecting idna<4,>=2.5
Using cached idna-3.4-py3-none-any.whl (61 kB)
Saved ./ibmsecurity-2022.8.22.0-py3-none-any.whl
Saved ./requests-2.28.1-py3-none-any.whl
Saved ./certifi-2022.9.24-py3-none-any.whl
Saved ./charset_normalizer-2.1.1-py3-none-any.whl
Saved ./idna-3.4-py3-none-any.whl
Saved ./urllib3-1.26.12-py2.py3-none-any.whl
Successfully downloaded ibmsecurity requests certifi charset-normalizer idna urllib3
kali%
Figure 4 – Download of the latest ibmsecurity package using pip

The invoke_* functions in the ibmsecurity library will use by default the _suppress_ssl_warning() method that will remove any security related to SSL/TLS. For example, for the previous [REMOVED] function, the method invoke_put() is defined in the file ibmsecurity/appliance/isamappliance.py, as shown below on line 402:

...
  3 from requests.packages.urllib3.exceptions import InsecureRequestWarning
...
 17 class ISAMAppliance(IBMAppliance):
...
 45     def _suppress_ssl_warning(self):
 46         # Disable https warning because of non-standard certs on appliance
 47         try:
 48             self.logger.debug("Suppressing SSL Warnings.")
 49             requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
 50         except AttributeError:
 51             self.logger.warning("load requests.packages.urllib3.disable_warnings() failed")
...
402     def invoke_put(self, description, uri, data, ignore_error=False, requires_modules=None, requires_version=None,
403                    warnings=[], requires_model=None):
404         """ 
405         Send a PUT request to the LMI.
406         """ 
407 
408         self._log_request("PUT", uri, description)
409         response = self._invoke_request(self.session.put, description, uri,
410                                         ignore_error, data,
411                                         requires_modules=requires_modules, requires_version=requires_version,
412                                         requires_model=requires_model, warnings=warnings)
413         return response
...

Figure 5 – ibmsecurity/appliance/isamappliance.py

The method _invoke_request() called on line 409 inside the invoke_put() method will disable any SSL/TLS security on line 334 by calling the method _suppress_ssl_warning() previously defined on line 45.

...
305     def _invoke_request(self, func, description, uri, ignore_error, data={}, requires_modules=None,
306                         requires_version=None, warnings=[], requires_model=None):
307         """
308         Send a request to the LMI.  This function is private and should not be
309         used directly.  The invoke_get/invoke_put/etc functions should be used instead.
310         """
...
334         self._suppress_ssl_warning()    
...
336         try:
337             if func == self.session.get or func == self.session.delete:
338             
339                 if data != {}:
340                     r = func(url=self._url(uri), data=json_data, verify=False, headers=headers)
341                 else:
342                     r = func(url=self._url(uri), verify=False, headers=headers)
343             else:
344                 r = func(url=self._url(uri), data=json_data,
345                          verify=False, headers=headers)
346 
347             if func != self.session.get:
348                 return_obj['changed'] = True  # Anything but GET should result in change
349 
350             self._process_response(return_obj=return_obj, http_response=r, ignore_error=ignore_error)
...

Figure 6 – ibmsecurity/appliance/isamappliance.py

The invoke_put() method called in [REMOVED] is defined on the line 402 of the ibmsecurity/appliance/isamappliance.py file. This method will then use the _invoke_request() method, defined on line 305. This _invoke_request() method will call the suppress_ssl_warning() method on line 334. The suppress_ssl_warning() method is defined on line 45: any security related to SSL/TLS will be then removed.

These methods defined in ibmsecurity/appliance/isamappliance.py are insecure:

In the ibmsecurity/appliance/isdsappliance.py Python file, we can find again the same suppress_ssl_warning() method which is also used by other methods (e.g. invoke_post_files() as shown below):

  3 from requests.packages.urllib3.exceptions import InsecureRequestWarning
...
 17 class ISDSAppliance(IBMAppliance):
...
 39     def _suppress_ssl_warning(self):
 40         # Disable https warning because of non-standard certs on appliance
 41         try:
 42             self.logger.debug("Suppressing SSL Warnings.")
 43             requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
 44         except AttributeError:
 45             self.logger.warning("load requests.packages.urllib3.disable_warnings() failed")
...
135     def invoke_post_files(self, description, uri, fileinfo, data, ignore_error=False, requires_modules=None,
136                           requires_version=None, warnings=[], json_response=True):
...
166         self._suppress_ssl_warning()
167 
168         try:
169             r = requests.post(url=self._url(uri=uri), data=data, auth=(self.user.username, self.user.password),
170                               files=files, verify=False, headers=headers)
171             return_obj['changed'] = True  # POST of file would be a change
172             self._process_response(return_obj=return_obj, http_response=r, ignore_error=ignore_error)
...

Figure 7 – ibmsecurity/appliance/isdsappliance.py

These methods are used everywhere in the ibmsecurity library to communicate with the remote ISVA infrastructure. 1162 calls to insecure methods have been identified:

kali% rgrep invoke_ ibmsecurity
ibmsecurity/isds/server.py: return isdsAppliance.invoke_get("Retrieving Server Status", "/widgets/server")
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/available_updates.py: return isdsAppliance.invoke_get("Retrieving available updates",
ibmsecurity/isds/available_updates.py: return isdsAppliance.invoke_get("Discover available updates",
ibmsecurity/isds/available_updates.py: return isdsAppliance.invoke_post_files(
ibmsecurity/isds/available_updates.py: ret_obj = isdsAppliance.invoke_post("Install Available Update",
ibmsecurity/isds/fixpack.py: return isdsAppliance.invoke_get("Retrieving fixpacks",
ibmsecurity/isds/fixpack.py: return isdsAppliance.invoke_post_files(
…
kali% rgrep invoke_ ibmsecurity | wc -l
1162

Figure 8 – Calls to insecure invoke_* methods

Risk

The ibmsecurity Python library massively uses insecure methods to communicate with the remote ISVA infrastructure, with 1162 calls to insecure functions.

V-94 Insecure ibmsecurity library – insecure communications 2/2

The package ibmsecurity has been partially audited as it provides the underlying APIs used by the Python scripts stored in the [REMOVED] directory. Unfortunately, the security of the ibmsecurity package is very poor, and by default all the SSL/TLS connections to the remote ISVA server are insecure due to the insecure option (Verify=false) used with the methods provided by the requests module (to send HTTPS requests to the remote ISVA infrastructure). For example, the method invoke_post_snapshot_id() will use Verify=false in the HTTPS request on line 455 to disable any verification of the remote SSL certificate (in addition with the previous insecure _suppress_ssl_warning() method found in “V-93 Insecure ibmsecurity library – insecure communications 1/2”):

...
429     def invoke_post_snapshot_id(self, description, uri, data, ignore_error=False, requires_modules=None,
430                                 requires_version=None, warnings=[], requires_model=None):
431         """ 
432         Send a POST request to the LMI.  Snapshot id is part of the uri.
433         Requires different headers to normal post.
434         """ 
...
452         self._suppress_ssl_warning()
453 
454         try:
455             r = self.session.post(url=self._url(uri=uri), data=data, verify=False, headers=headers)
456             return_obj['changed'] = False  # POST of snapshot id would not be a change
457             self._process_response(return_obj=return_obj, http_response=r, ignore_error=ignore_error)
...

Figure 10 – ibmsecurity/appliance/isamappliance.py

Similar vulnerabilities can be found everywhere in the Python sources. For example, in the method invoke_request() inside ibmsecurity/appliance/isamappliance.py and invoke_get_file() inside ibmsecurity/ appliance/isdsappliance.py:

...
518     def invoke_request(self, description, method, uri, filename=None, ignore_error=False, requires_modules=None,
519                        requires_version=None,
520                        warnings=[], requires_model=None, **kwargs):
...
551         self._suppress_ssl_warning()
...
555             r = self.session.request(method, url=self._url(uri), verify=False, **args)

Figure 11 – ibmsecurity/appliance/isamappliance.py

...
228     def invoke_get_file(self, description, uri, filename, no_headers=False, ignore_error=False, requires_modules=None,
229                         requires_version=None, warnings=[]):
230         """ 
231         Invoke a GET request and download the response data to a file
232         """ 
...
251         self._suppress_ssl_warning()
252 
253         try:
254             r = requests.get(url=self._url(uri=uri), auth=(self.user.username, self.user.password), verify=False,
255                              stream=True, headers=headers)

Figure 12 – ibmsecurity/appliance/isdsappliance.py

Vulnerable functions identified in ibmsecurity/appliance/isdsappliance.py:

Vulnerable functions identified in ibmsecurity/appliance/isamappliance.py:

The vulnerable library can also be found in Github:

Figure 13 – https://github.com/IBM-Security/ibmsecurity/blob/master/ibmsecurity/appliance/isamappliance.py#L187

        try:
            if data_as_files is False:
                r = self.session.post(url=self._url(uri=uri), data=data, files=files, verify=False, headers=headers)
            else:
                r = self.session.post(url=self._url(uri=uri), files=files, verify=False, headers=headers)

Risk

The ibmsecurity Python library massively uses insecure methods to communicate with the remote ISVA infrastructure, with 1162 calls to insecure functions.

V-95 Insecure ibmsecurity library – hardcoded passwords

It was observed that the ibmsecurity library contains hardcoded users and passwords:

…
7 def config(isamAppliance, instance_id, federation_id=None, federation_name=None, hostname='127.0.0.1', port='443', username='easuser',
8 password='passw0rd', reuse_certs=False, reuse_acls=False, check_mode=False, force=False):
…

Figure 14 – ibmsecurity/isam/web/reverse_proxy/federation_configuration.py

Similar vulnerabilities can be found in the source codes. For example, in the method invoke_request() inside ibmsecurity/appliance/isamappliance.py and invoke_get_file() inside ibmsecurity/ appliance/isdsappliance.py, we can find hardcoded credentials

…
10 def config(isamAppliance, instance_id, hostname='127.0.0.1', port=443, username='easuser', password='passw0rd',
11 junction="/mga", reuse_certs=False, reuse_acls=False, api=False, browser=False, auth_register=None, fapi_compliant=None,
12 check_mode=False, force=False):
…

Figure 15 – ibmsecurity/isam/web/reverse_proxy/oauth_configuration.py

…
9 def config(isamAppliance, instance_id, hostname='127.0.0.1', port=443, username='easuser', password='passw0rd',
10 junction="/mga", reuse_certs=False, reuse_acls=False, check_mode=False, force=False):
…

Figure 16 – ibmsecurity/isam/web/reverse_proxy/aac_configuration.py

Risk Attacker can use hardcoded passwords to compromise installations.

V-96 Insecure ibmsecurity library – uninitialized variables

It was observed that the ibmsecurity library uses variables before they are initialized in:

…
98 def _check(isamAppliance, description, properties):
99 """
100 Check and return True if update needed
101 """
102 update_required = False
103 ret_obj = get(isamAppliance, description)
104 if ret_obj['data'] == {}:
105 logger.warning("Attribute Matcher not found, returning no update required.")
106 return None, update_required, json_data
107 else:
108 json_data = {
109 "properties": properties,
110 "predefined": ret_obj['data']['predefined'],
111 "supportedDatatype": ret_obj['data']['supportedDatatype'],
112 "uri": ret_obj['data']['uri']
113 }
…

Figure 17 – ibmsecurity/isam/aac/attribute_matchers.py

We can find similar vulnerability in ibmsecurity/isam/aac/risk_profiles.py:

…
152 def _check(isamAppliance, name, active, description, attributes, predefined):
153 """
154 Check and return True if update needed
155 """
156 update_required = False
157 ret_obj = get(isamAppliance, name)
158 if ret_obj['data'] == {}:
159 logger.warning("Risk Profile not found, returning no update required.")
160 return None, update_required, json_data
161 else:
162 if ret_obj['data']['predefined'] is True:
163 logger.warning("Predefined Risk Profiles can NOT be updated, returning no
update required.")
164 return ret_obj['data']['id'], update_required, {}
165 else:
166 json_data = {
167 "name": name,
168 "active": active,
169 "predefined": predefined
170 }
171 if attributes is not None:
172 json_data['attributes'] = attributes
…

Figure 18 – ibmsecurity/isam/aac/risk_profiles.py

Risk The Python code can crash.

tombosmansibm commented 7 months ago

Thanks for bringing this to our attention .

pba-report-isva commented 7 months ago

Hello,

can you confirm these issues and let me know if you plan to fix them?

Thank you,

Best regards,

tombosmansibm commented 7 months ago

Yes, these will be fixed.

tombosmansibm commented 7 months ago

closed by #418

pba-report-isva commented 7 months ago

Hello,

can you provide the CVEs and publish the corresponding advisories in https://github.com/IBM-Security/ibmsecurity/security?

Thank you

pba-report-isva commented 7 months ago

The security bulletin has been published at https://www.ibm.com/support/pages/node/7147932.

tombosmansibm commented 7 months ago

Hi Pierre, we're looking into this.