前言
这是我第一次接触java反序列化漏洞,之前也不会java,调试的过程比较艰辛,其中一些原理其实也没有太清楚,先整理一篇博客回顾一下再继续往下学习吧。CVE-2017-3506 & CVE-2017-10271漏洞产生的原因大致是Weblogic的WLS Security组件对外提供webservice服务,其中使用了XMLDecoder来解析用户传入的XML数据,在解析的过程中出现反序列化漏洞,导致可执行任意命令,攻击者发送精心构造的xml数据甚至能通过反弹shell拿到权限。
环境搭建
直接使用github上vulhub中的docker
https://github.com/vulhub/vulhub/blob/master/weblogic/CVE-2017-10271
之前我连docker都没用过,参考了这本书入门 Docker—从入门到实践
因为要动态调试,需要开启一个远程调试的端口8053,所以将docker-compose.yml文件改动如下
1 | version: '2' |
执行命令 docker-compose up -d
完成搭建,若成功启动,打开浏览器访问http://127.0.0.1:7001
应该会出现一个404页面
docker容器搭建完成后,使用命令docker exec -it weblogic /bin/bash
进入容器
修改/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh
文件
找到如下代码段
添加图中的上面两行代码 原来并没有这两行代码
1 | debugFlag="true" |
添加后运行一下这个脚本,退出并重启容器,这样就开启了debug模式,可以docker ps
一下查看容器是否开启了8453端口。
tips:退出容器快捷键 Ctrl+D
因为要远程调试weblogic,而没有源码,所以要把容器中依赖的包拷出来
使用命令docker cp weblogic:/root/Oracle/Middleware/wlserver_10.3 ./WebLogic_jars
这是如果你使用的是windows系统会报错,因为windows对命令行下的路径长度有限制,所以要先进到docker里把要拷的文件夹打个包,再拉下来。
Linux 的打包命令为 zip -r zipname.zip targetdir
docker中好像没有zip命令,需要先apt-get install zip
一下,
然后再执行zip -r wlserver_10.3.zip wlserver_10.3
这部分jar不全,还要将/root/Oracle/Middleware/modules
拷出来
IDEA远程调试
使用idea打开wlserver_10.3,将wlserver_10.3/server/lib和modules这两个文件夹添加到library
添加后,就会发现里面的.jar和.war的包都可以点开了,并且可以搜索里面的一些类和字符串了。
然后开始设置debug,点击Add Configuration...
添加一个Remote设置
端口设置为8453,并且设置Use module classpath
点击debug,出现如下字样,说明已经配置ok。
Connected to the target VM, address: 'localhost:8453', transport: 'socket'
在wlserver_10.3/server/lib/weblogic.jar!/weblogic/wsee/jaxws/WLSServletAdapter.class中的handle方法下断点,看看能否击中断点。
以上,就搭建好了docker环境和调试环境,接下来就可以复现和动态调试了。
漏洞复现
因为对原理其实也是一知半解,所以先用师傅们的poc复现了一下
现在本地开一个监听端口
nc -l -p 8888
发送如下数据包
tips:/dev/tcp/ 后的ip换成自己的即可
反弹shell成功
查看burp中返回的xml数据,可以清晰的看到调用栈,调用栈在<ns2:frame />标签中
仔细分析weblogic返回的响应,我们可以大概定位到问题点,我们重点关注<ns2:frame />标签中class以weblogic开头的部分,这部分就是weblogic处理我们请求的调用栈逻辑,weblogic处理完后就到了XMLDecoder
这样,通过漏洞复现,我们得到了weblogic的处理流程如下(为了方便查看,调用栈顺序为从上到下)
- weblogic.wsee.jaxws.workcontext.WorkContextServerTube->processRequest
- weblogic.wsee.jaxws.workcontext.WorkContextTube->readHeaderOld
- weblogic.wsee.jaxws.workcontext.WorkContextServerTube->receive
- weblogic.workarea.WorkContextMapImpl->receiveRequest
- weblogic.workarea.WorkContextLocalMap->receiveRequest
- weblogic.workarea.spi.WorkContextEntryImpl->readEntry
- weblogic.wsee.workarea.WorkContextXmlInputAdapter->readUTF
至此,漏洞复现已经完成,并根据漏洞复现中得到的信息,为动态调试打下了基础。
动态调试
从poc中可以看出,这个漏洞是wls-wsat这个接口出了问题,搜索一下文件,发现了wls-wsat.war
这个包,打开后点击web.xml
查看有哪些接口
进来后发现第一个接口就是我们想要的接口,调用栈和WebLogic servlet的分发机制比较复杂,我也不是很懂emm,只是跟着复现过程发现的函数调用栈跟了一遍流程。
具体流程如下
weblogic.wsee.jaxws.workcontext.WorkContextServerTube
1 | public NextAction processRequest(Packet var1) { |
var1为POST传进来的XML数据,var3是xml的头部解析,如果存在(头不为空),就进入readHeaderOld()方法,跟进readHeaderOld()
weblogic.wsee.jaxws.workcontext.WorkContextTube
1 | protected void readHeaderOld(Header var1) { |
第一步processRequest中我们只把头读了进来,其他的数据还在缓冲区中,使用ByteArrayOutputStream函数读取剩余数据到var4。经过一系列的处理后,如果没有问题就创建WorkContextXmlInputAdapter对象 var6,之后跟进receive()
weblogic.wsee.jaxws.workcontext.WorkContextServerTube
1 | protected void receive(WorkContextInput var1) throws IOException { |
继续跟进receiveRequest()
weblogic.workarea.WorkContextMapImpl
1 | public void receiveRequest(WorkContextInput var1) throws IOException { |
将var1传到了receiveRequest()方法中,继续跟进
weblogic.workarea.WorkContextLocalMap
1 | public void receiveRequest(WorkContextInput var1) throws IOException { |
WorkContextEntryImpl.readEntry(var1);
对传进来的数据进行处理,具体的代码好像看不太懂,但是跟进!readEntry()!
weblogic.workarea.spi.WorkContextEntryImpl
1 | public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException { |
在这里对var0执行了readUTF()方法,终于看到曙光了,最后一个方法,跟进
weblogic.wsee.workarea.WorkContextXmlInputAdapter
1 | public String readUTF() throws IOException { |
终于执行了readObject()方法,对XMLDecoder对象进行了反序列化,导致远程命令执行。
不用再跟进了,至此远程动态调试完成。
漏洞总结
WebLogic没有对XML的数据进行任何的过滤,导致可以构造XML数据,反序列化任意对象,从而RCE,这也就是CVE-2017-3506产生的原因。
这是我第一次对一个漏洞进行完整的复现和调试,其间遇到了许多问题,磕磕绊绊的终于完成了这篇博客,感谢很多师傅的帮助和博客。经过这次尝试,我对java反序列化和漏洞调试有了初步的认识,希望继续努力,早日完成下一篇博客!