CVE-2018-1335

April 18, 2019 2 minutes

前言

这篇文章我觉得很有意思,作者从一个公开的CVE开始分析处Apache Tika 命令注入漏洞的完整利用,觉得很有参考价值。故翻译一下,供大家参考。 原文链接:https://rhinosecuritylabs.com/application-security/exploiting-cve-2018-1335-apache-tika/

正文

介绍

本文介绍了从一个未公开的CVE中获取Apache Tika的命令注入的信息,到完全利用的一个过程。CVE为 https://nvd.nist.gov/vuln/detail/CVE-2018-1335 。由于Apache Tika是开源的,我们能够从中获取一些基本信息,并且通过分析Apache Tika的代码来确定问题所在。虽然命令注入通常很简单,但正如你将看到的那样,由于Java处理执行操作系统命令的方式以及Apache Tika代码本身的一些复杂性,要实现命令执行或者代码执行还是要克服一些困难的。不过,仍然可以使用Windows脚本宿主(Cscript.exe)绕过这些阻止程序。

什么是 Apache Tika

The Apache Tika™ toolkit detects and extracts metadata and text from over a thousand different file types (such as PPT, XLS, and PDF). All of these file types can be parsed through a single interface, making Tika useful for search engine indexing, content analysis, translation, and much more. (https://tika.apache.org/)

Apache Tika 有一些不同的组件:一个java库、命令行工具和一个实现REST API的 tika-server。此次漏洞就是tika-server所出现的问题, 文档 <https://wiki.apache.org/tika/TikaJAXRS>, 可以在此处下载有漏洞的Tika-server:https://archive.apache.org/dist/tika/tika-server-1.17.jar

CVE 中的信息

要想了解问题的所在,我们首先要阅读CVE的描述,看看可以获取哪些信息,找到立足点。 原始的问题描述:

From Apache Tika versions 1.7 to 1.17, clients could send carefully crafted headers to tika-server that could be used to inject commands into the command line of the server running tika-server. This vulnerability only affects those running tika-server on a server that is open to untrusted clients. The mitigation is to upgrade to Tika 1.18.

从Apache Tika的1.17版本到1.7版本,可以通过在客户端精心构造HTTP头部信息发送到tika-server,将命令注入到正在运行的tika-server中。此漏洞仅影响在对不受信任的客户端开放的服务器上运行tika-server。缓解措施是升级到Tika 1.18。

我们能够获取到的信息如下:

  • 1.18版本是安全的
  • 1.17版本漏洞未修复
  • 该漏洞是命令注入漏洞
  • 漏洞的切入点为请求头
  • 可以在tika-server的代码中看到改动

有了这些信息,我们现在有了一个识别漏洞的很好的立足点。接下来就是找出Tika修复版本和未修复版本间的差异,特别是tika-server部分。从代码中找出可以执行系统命令的函数是另一个不错的想法。最后,搜索tika-server代码的各个部分,从某些HTTP请求中找到解析HTTP头部的代码。

进一步了解

使用icdiff递归的对tika-server 1.17和1.18的代码进行比较,只返回了一个已修改的文件。

我们的目标是找打hreader字段中的命令注入,我们获取到的第一个结果是在已修复版本中添加了被称为“ALLOWABLE_HEADER_CHARS”的静态私有变量,这是一个不错的开始。我们可以大胆假设一下,这部分可能用来过滤header字段中可能造成命令注入的字符。

继续向下看,是一个名为 processHeaderConfig 函数内部的部分代码,这部分在1.18中已被删除,看起来有点意思。这部分代码利用java反射机制动态创建一个方法,并给该方法设置了某个对象的属性值,并调用该方法。

这里是这个函数的描述:

apache/tika/tika-server/src/main/java/org/apache/tika/server/resource/TikaResource.java

在上面的图中可以看到 prefix 是不同属性的HTTP头的前缀,并且在代码开始部分已经声明为静态变量。

因此我们可以把这个静态变量为前缀写入HTTP请求头内,并且设置相应的值,最终看起来像: X-Tika-OCRsomeproperty:somevaluesomeproperty 会找到相应的 setSomeproperty 函数,并将 somevalue 当做参数传入。

到这里,你可以看到正在使用一个函数来检测HTTP header中的前缀,以确定该如何调用函数。最终,所有的参数都传入到了 processHeaderConfig 中。

看一下 processHeaderConfig 函数,可以看到在 TesseractOCRConfig 的对象上设置一个属性(就是将 somevalue 赋值给了 Someproperty ),搜索哪里使用了 TesseractOCRConfig 对象:

   tika-parsers/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java

结果非常有趣。

这是来自 TesseractOCRParser.javadoOCR 函数,他将 TesseractOCRConfig 对象的属性传递到一个字符串数组中,然后使用这些字符串构造 ProcessBuilder

这看起来很有希望,我们把之前所有的信息放在一起发现,我们现在可以向服务器发送某种HTTP请求,在header中添加 X-Tika-OCRTesseractPath: <some command> 字段。 command 会被插入 cmd 字符串中得到执行。唯一的问题就是 config.getTesseractPath(),之后被添加了一个 getTesseractProg,这个是我们无法控制的, “getTesseractProg 最终会生成一个 tesseract.exe 。为了解决这个问题,我们可以用双引号包裹我们想要执行的命令,Windows将忽略引号后附加的任何内容,只执行我们的注入命令。

为了进行测试,我们可以使用tika-server文档中的示例来检索有关文件的一些元数据。

由于OCR代表光学字符识别,用于从图像中提取文本和内容,我们将使用图像上传而不是docx,以期望运行 doOCR 函数。

   curl -T test.tiff http://localhost:9998/meta --header "X-Tika-OCRTesseractPath:    "calc.exe   ""

目前为止,我们可以提交一个名为 X-Tika-OCRTesseractPath ,值为引号括起来的命令,来达到命令注入的目的。

仅仅能够弹一个计算器吗?

此时,由于命令作为数组传递给Java ProcessBuilder,我们现在仅仅只是直接执行应用程序的命令,不能够运行多个命令或在命令中添加参数。这是因为将一组字符串传递给Java ProcessBuilder 或runtime.exec的调用方式如下:

通常像 cmd.exe/bin/sh 这样的shell中的字符 &<> 和`等,不会被ProcessBuilder解析,只会忽略。因此你不能执行多条命令或给命令添加参数。它不像做“X-Tika-OCRTesseractPath: “cmd.exe /c some args”这样或者其它任何组合一样简单。

回到 cmd 数组的构造,你可以看到我们可以控制很多参数,那些形似 config.get*() 的项。但这些参数被一些其它我们不能控制的项所分割。

我首先想到的是运行 cmd.exe ,然后将参数 /C 使用 config.getLanguage() 传入,之后使用 config.getPageSegMode()||somecode|| 插入。但是,这不起作用,因为在调用 doOCR 之前,还有另一个函数调用 config.getTesseractPath() ,目的是检查调用程序是否为有效程序。这里的问题是只运行没有参数的 cmd.exe 将会导致服务器挂起,这会导致 doOCR 函数不被执行。

解决方案

为了能够执行多个命令,使用 Process Monitor 我们可以更深入的了解 doOCR 函数执行时会有什么动作。查看当前进程属性,当tika-server启动时,会生成以下命令行

   "calc.exe"tesseract.exe C:   Users   Test   AppData   Local   Temp   apache-tika-3299124493942985299.tmp C:   Users   Test   AppData   Local   Temp   apache-tika-7317860646082338953.tmp -l eng -psm 1 txt -c preserve_interword_spaces=0

我们能够控制的部分使用红框框出,我们可以从3个地方注入,1个传入命令,2个用来传入参数。另一个有趣的发现是,Tika实际上创建了2个临时文件,其中一个作为第一个参数传递。

经过进一步调查,我能够确认第一个临时文件的内容是我上传的内容。这意味着我们可以把命令写入该文件。

现在我们所需要的就是找到Windows下的一个应用,他将忽略tika-server创建的一些参数,并且能够执行第一个临时文件中的命令或代码。这对我来说有点困难,在查看https://github.com/api0cradle/LOLBAS 时,我幸运的发现了 Cscript.exe,看起来有点意思。

事实证明, Cscript.exe 正是我们所需要的。他将第一个参数作为脚本,使用 //E:engine 来指定执行脚本的解释器(Jscript或VBS),因此文件的扩展名是无关紧要的。现在我们的命令如下:

   "cscript.exe"tesseract.exe C:   Users   Test   AppData   Local   Temp   apache-tika-3299124493942985299.tmp C:   Users   Test   AppData   Local   Temp   apache-tika-7317860646082338953.tmp -l //E:Jscript -psm 1 txt -c preserve_interword_spaces=0

http头部信息如下:

   X-Tika-OCRTesseractPath: "cscript.exe"
   X-Tika-OCRLanguage: //E:Jscript

图片的内容如下:

   var oShell = WScript.CreateObject("WScript.Shell");
   var oExec = oShell.Exec('cmd /c calc.exe');

首先,上传肯定会是失败的,因为我们上传的不是有效的图像,无法验证 magic bytes 。之后我发现将header中加入 Content-type:image/jp2,会使得Tika不会检查图像中的 magic bytes ,但仍然会通过 doOCR 来处理图像。这样我们就可以上传包含Jscript代码的图像了。

最后。我们把所获取的信息结合在一起,得到了一个执行链条。

结论

这看起来是一个简单的命令注入漏洞,为了去利用它,我们还是要克服很多困难。在遇到困难提出解决办法的这一过程中是十分有趣的,虽然这个洞利用起来比较麻烦,但还是可以利用的。在这里我们强调在构造执行系统命令的时候,不应该使用不受信任的输入,Apache官方不建议在不受信任的环境中运行Tika服务器或将其暴露给不受信任的用户。此漏洞已被修复,当前版本为1.20,因此如果您使用此服务,请及时更新。

你可以在Rhino安全实验室找到PoC:https://github.com/RhinoSecurityLabs/CVEs/tree/master/CVE-2018-1335