Multiple vulnerabilities found in ibmsecurity
Closed this issue · 7 comments
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:
- V-93 Insecure ibmsecurity library – insecure communications 1/2
- V-94 Insecure ibmsecurity library – insecure communications 2/2
- V-95 Insecure ibmsecurity library – hardcoded passwords
- V-96 Insecure ibmsecurity library – uninitialized variables
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,
- Pierre Barre
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:
- invoke_post_files (line 148)
- invoke_put_files (line 203)
- invoke_get_file (line 246)
- _invoke_request (line 305)
- _invoke_request_with_headers (line 355)
- invoke_post_snapshot_id (line 427)
- invoke_request (line 516)
These methods defined in ibmsecurity/appliance/ isdsappliance.py are also insecure: - invoke_post_files (line 135)
- invoke_put_files (line 184)
- invoke_get_file (line 228)
- _invoke_request (line 288)
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:
- invoke_post_files – vulnerability present on line 170;
- invoke_put_files – vulnerability present on line 214;
- invoke_get_file – vulnerability present on line 254;
- _invoke_request – vulnerabilities present on lines 322, 325 and 329;
Vulnerable functions identified in ibmsecurity/appliance/isamappliance.py:
- invoke_post_files – vulnerabilities present on lines 187 and 189;
- invoke_put_files – vulnerability present on line 232;
- invoke_get_file – vulnerability present on line 272;
- _invoke_request – vulnerabilities present on lines 338, 340 and 343;
- _invoke_request_with_headers – vulnerabilities present on lines 383, 385 and 388;
- invoke_post_snapshot_id – vulnerability present on line 453;
- invoke_request – vulnerability present on line 553.
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:
- ibmsecurity/isam/aac/attribute_matchers.py
- ibmsecurity/isam/aac/risk_profiles.py
The json_data variable is used on line 106 but is only defined on line 108:
…
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.
Thanks for bringing this to our attention .
Hello,
can you confirm these issues and let me know if you plan to fix them?
Thank you,
Best regards,
Yes, these will be fixed.
closed by #418
Hello,
can you provide the CVEs and publish the corresponding advisories in https://github.com/IBM-Security/ibmsecurity/security?
Thank you
The security bulletin has been published at https://www.ibm.com/support/pages/node/7147932.
Hi Pierre, we're looking into this.