两个多月前,在Red Timmy Security,我们发布了EnumJavaLibs,该工具允许发现远程Java应用程序的类路径中可用的库。
开发这种工具的最初想法非常有趣,我们认为不谈论它是可耻的。它来自一年半前进行的评估,导致发现了关键应用程序上的Java反序列化漏洞。
由于我们处于该项目的保密协议之下,因此我们没有任何可能透露供应商名称和受影响产品的名称。但是,我们可以对体系结构进行一般性描述,并为您提供一些可借鉴的知识。
通用架构
分析的解决方案是一个用GWT编写的Web应用程序,该应用程序只能通过HSM(硬件安全模块)设备使用以前的TLS相互身份验证进行访问。身份验证过程需要Java Applet。
Google Web Toolkit框架(GWT)用于服务器和客户端组件。如前所述,与GWT实施分开的是Java Java Applet。身份验证之前,它是在与前端Web应用程序交互期间由用户的浏览器下载的。它仅用于与存储专用密钥的客户端硬件令牌进行通信。
基本上,GWT服务器应用程序和GWT客户端组件是直接通信的。GWT客户端组件还与Java Applet对话,后者又与硬件令牌对话以执行身份验证过程中所需的操作,例如数据签名。该信息最终由GWT客户端传输到服务器。下图应阐明通用体系结构。
但是,让我们从头开始。
认证过程
插入硬件令牌后,将输入PIN码以解锁设备,然后可以开始登录。此时,身份验证过程已启动,并且向服务器发出了登录请求(请参见下面的屏幕截图)。
登录请求非常有趣,因为其中包含ASN.1 DER编码流(MII[..]
在POST主体中以“ ” 开头),该流包裹在用Base64完成的另一层编码中。为什么我们不使用“ openssl
”二进制文件对其进行解码并查看内部?
第62行显示了Java序列化对象(序列“ ACED0005
”)的存在,该对象似乎是随机数,会话ID和时间戳。可能会在服务器端反序列化吗?为了探索我们的假设,我们决定在第62行使用其他内容(用“ ysoserial
” 生成的另一个有效负载)更改对象,并将其提交给服务器。但是,该过程似乎无法正常工作。但为什么?我们也决定对Java applet进行反编译和分析,因此我们正在寻找答案。
分析指出,是的,我们是对的,在第62行有一个Java序列化对象,但是第1591行包含RSA-SHA256保护Java对象本身不受篡改的数字签名。现在很清楚为什么服务器不协作:为了在第62行注入我们自己的Java序列化有效负载,我们需要一种绕过该数字签名的方法。一种方法可能是找到一种从硬件令牌中获取私钥的方法,但这可能会使我们长时间忙碌。相反,我们选择了“协作”方法。Java applet无法直接访问私钥,但是它可以要求HSM(通过专门暴露的接口)对任意值进行签名,然后将其返回给applet。您可以按照上述说明修改Java小程序并充当我们的代理,但是还有另一个问题:它也进行了数字签名,并且使用最新版本的JRE,
绕过Java检查并向JD开放
这是我们分九步绕过它并设法注入任意序列化有效负载的方式:
- 持有攻击者有效负载的Java序列化对象是使用“
ysoserial
”或类似工具生成的。 - 该applet是从Web服务器下载,反编译并修改其代码的,以便从磁盘读取在点1处生成的自定义Java对象,就在对其进行数字签名之前。
- “
appletfile.jar/META-INF
” 内的所有签名文件均被删除。这将使该小程序无符号。 现在再次对Java小程序进行了签名,但是这次使用了自签名证书:
`$ **keytool -genkey -keyalg RSA -alias myKey -keystore myKeystore -validity 360**`<br /> `$ **jarsigner -keystore myKeystore -verbose appletfile.jar myKey**`
Java配置中的安全设置设置为“ 高 ”(最低值)。还将目标网站添加到例外列表,并将在步骤4生成的证书作为“ 安全站点CA ” 导入。实际上,这减少并绕过了本地Java安装的安全控制。但是,除此以外,还有多种方法可以绕过它。因此,请随时按照自己喜欢的任何程序进行操作。
- 第一次,我们像往常一样浏览目标网站。
appletfile.jar
Java下载“ ”并将其存储在缓存中。 - 然后,我们在浏览器缓存目录中搜索并找到jar文件,并将其替换为经过修改并重新签名的applet。
- 现在,我们关闭浏览器,然后再次打开它,第二次访问目标网站。Java将抱怨修改后的applet,但是可以安全地忽略所有弹出的安全警告消息。
- 执行登录后,我们的自定义序列化Java对象将被签名并发送到服务器。
登录当然会失败,但是此时应执行Java反序列化攻击。在我们的特定情况下,我们注意到服务器的回复将根据我们存储在对象中的内容而改变。例如,如果我们序列化一个空的“ String
”对象,则回复显示代码XXXX5:
`HTTP/1.1 200 OK
Connection: close
//EX[4,0,3,’WdZtoTv’,2,0,1,[“com.xxxxxxxxxxxxxxxx.xxxxClientException/1437829146”,”java.util.Date/3385151746”,”XXXX5“,”Your operation failed due to an internal error. “],0,7]<br />如果相反,我们序列化了一个不存在的对象(不在Java远程应用程序的类路径中),则在回复中会收到类似的文本消息,但会使用“ **XXXX9** ”代码。简而言之,应用程序的行为就像一个先知!从这里创建_EnumJavaLibs_的想法!当服务器使用两个不同的错误代码进行响应时,可以利用这一事实来找出服务器在类路径中具有哪些库并自定义攻击,而不必盲目生成有效负载。<br />由于完成调查的时间有限,并且为了确认我们发现的内容实际上是否是Java反序列化漏洞或其他原因,我们已要求供应商查看服务器端代码的权限。最终,我们的请求已被接受,这就是我们所发现的。<br />在服务器端,在处理传入的登录请求的代码部分中,在“
pkcs7.getContentStream()” 内部存在对
readObject()执行反序列化的“ ” 的调用。这证实了该漏洞的严重性。<br />![](https://www.redtimmy.com/wp-content/uploads/2020/04/JD_img3_.jpg#align=left&display=inline&height=328&margin=%5Bobject%20Object%5D&originHeight=328&originWidth=972&status=uploading&style=none&width=972)<br />从上面的两个代码段可以看出,如果“ ”失败,则在反序列化期间出现任何错误时,将引发异常“ **XXXX9** ”
readObject()`。如果读取的对象不是LoginRequest的实例,则抛出“ XXXX5 ”异常,该实例实际上是在反序列化之后发生的,并且不会阻止进行攻击。当然,只有提供有效的小工具链(不好用的方式没有实现超前保护),坏事情才能解决,但这并不是本博客文章的目的。
结论
很好的经验!通常建议不要在执行反序列化时信任用户输入,并实施预见保护以防止意外对象反序列化。
从攻击者的角度来看,请注意,Java序列化的对象可以隐藏在其他数据结构(例如证书)中。而且,Java小程序上的数字签名仅能防止MITM小程序带有恶意小程序的攻击者,因此客户端将收到警告。它不能防止攻击者篡改在自己的系统上运行的applet :他们只能忽略警告。
最后,如果您有一个在发送(格式错误)序列化对象时充当oracle的应用程序,请尝试EnumJavaLibs。如果您想深入了解本主题,请在一些参考下面。并且不要忘记在Twitter上关注我们:https://twitter.com/redtimmysec
参考文献:
https://www.owasp.org/index.php/Top_10-2017_A8-Insecure_Deserialization
https://www.ibm.com/developerworks/library/se-lookahead/index.html