禁止JVM執行外部命令Runtime.exec–由ApacheCommonsCollections漏洞引發的思考
該文章來自阿里巴巴技術協會(ATA)精選集
Apache Commons Collections遠端程式碼執行漏洞
最近出來一個比較嚴重的漏洞,在使用了Apache Commons Collections的Java應用,可以遠端程式碼執行。包括最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些大名鼎鼎的Java應用。
這個漏洞的嚴重的地方在於,即使你的程式碼裡沒有使用到Apache Commons Collections裡的類,只要Java應用的Classpath裡有Apache Commons Collections的jar包,並且開放了允許Java序列化協議的埠,都可以遠端程式碼執行。
參考:
這個漏洞的演示很簡單,只要在maven依賴裡增加
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
再執行下面的java程式碼:
Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class },
new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class },
new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "calc" }) };
Transformer transformedChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);
Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foobar");
這個漏洞的根本問題並不是Java序列化的問題,而是Apache Commons Collections允許鏈式的任意的類函式反射呼叫。攻擊者通過允許Java序列化協議的埠,把攻擊程式碼上傳到伺服器上,再由Apache Commons Collections裡的TransformedMap來執行。
這裡不對這個漏洞多做展開,可以看上面的參考文章。
如何簡單的防止Java程式呼叫外部命令?
從這個漏洞,引發了很久之前的一個念頭:**如何簡單的防止Java程式呼叫外部命令?**
java相對來說安全性問題比較少。出現的一些問題大部分是利用反射,最終用Runtime.exec(String cmd)函式來執行外部命令的。**如果可以禁止JVM執行外部命令,未知漏洞的危害性會大大降低,可以大大提高JVM的安全性。**
換而言之,就是如何禁止Java執行Runtime.exec(String cmd)之類的函式。
在Java裡有一套Security Policy,但是實際上用的人比較少。因為配置起來太麻煩了。
參考:
- http://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.html
- http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
- http://docs.gigaspaces.com/xap102sec/java-security-policy-file.html 詳細的許可權列表可以參考這個文件
從文件裡可以知道,Java裡並沒有直接禁止Rumtine.exec 函式執行的許可權。
禁止檔案執行的許可權在java.io.FilePermission裡。如果想禁止所有外部檔案執行,可以在下面的配置檔案中把execute
刪除:
grant {
permission java.io.FilePermission "<<ALL FILES>>", "read, write, delete, execute";
};
但是**Java許可權機制是白名單的**,還有一大堆的許可權要配置上去,非常複雜。
從tomcat的配置就知道了。http://tomcat.apache.org/tomcat-7.0-doc/security-manager-howto.html
所以Tomcat預設是沒有啟用Security Policy的,可以通過在命令加上-security引數來啟用。
catalina.sh start -security
那麼有沒有簡單的辦法可以在程式碼裡禁止Java執行外部命令?
研究了下,通過擴充套件SecurityManager可以簡單實現:
SecurityManager originalSecurityManager = System.getSecurityManager();
if (originalSecurityManager == null) {
// 建立自己的SecurityManager
SecurityManager sm = new SecurityManager() {
private void check(Permission perm) {
// 禁止exec
if (perm instanceof java.io.FilePermission) {
String actions = perm.getActions();
if (actions != null && actions.contains("execute")) {
throw new SecurityException("execute denied!");
}
}
// 禁止設定新的SecurityManager,保護自己
if (perm instanceof java.lang.RuntimePermission) {
String name = perm.getName();
if (name != null && name.contains("setSecurityManager")) {
throw new SecurityException("System.setSecurityManager denied!");
}
}
}
@Override
public void checkPermission(Permission perm) {
check(perm);
}
@Override
public void checkPermission(Permission perm, Object context) {
check(perm);
}
};
System.setSecurityManager(sm);
}
只要在Java程式碼裡簡單加上面那一段,就可以禁止執行外部程式了。
Java序列化的本身的蛋疼之處
其實Java自身的序列化機制就比較蛋疼,可以參考Effective Java裡的。
http://allenlsy.com/NOTES-of-Effective-Java-10/
並非禁止外部程式執行,Java程式就安全了
要注意的是,如果可以任意執行Java程式碼,還可以做很多事情,比如寫入ssh金鑰,從而可以遠端登陸,參考最近的Redis未授權訪問漏洞:https://www.sebug.net/vuldb/ssvid-89715
總結
禁止JVM執行外部命令,是一個簡單有效的提高JVM安全性的辦法。但是以前沒有見到有相關的內容,有點奇怪。
可以考慮在程式碼安全掃描時,加強對Runtime.exec相關程式碼的檢測。在JVM層面對所有Runtime.exec打日誌。
有些開源庫喜歡用Runtime.exec來執行命令獲取網路卡mac等操作,個人表示相當的蛋疼,不會使用這樣子的程式碼。
相關文章
- 由吃飯引發的思考
- 由一個emoji引發的思考
- 由春節回家難引發的思考
- 由京東發貨引發的思考和分析
- 由mv命令引發的對inode的思考
- 【高併發】由InterruptedException異常引發的思考Exception
- 由Siri和Alexa結婚引發的思考
- 由屎色自行車棚引發的思考
- 由sap一沖銷方法引發的思考
- 由整合ARouter引發的一些思考
- 由“自動播放”事件引發的幾點思考事件
- 一條Python命令引發的漏洞思考Python
- 檢視JVM執行時引數JVM
- Runtime.exec執行dos命令不正確
- 由“阿里巴巴入股星辰急便”引發的思考阿里
- 由Android 65K方法數限制引發的思考Android
- 由select for update鎖等待問題引發的深入思考
- 由 LG P4309 引發的擴充思考
- 【漏洞預警】WordPress檔案刪除功能引發程式碼執行漏洞
- 一個由line-height引發的血案與思考
- 由《暗黑地牢》這款「地主模擬器」引發的思考
- 由ASP.NET Core讀取Response.Body引發的思考ASP.NET
- 由一把手槍引發的設計思考
- iOS開發基礎149-由UUIDString引發的思考iOSUI
- 由一把手槍的設計引發的思考
- 由SQL語句執行過程觸發對Oracle體系結構的思考SQLOracle
- 由農行取消免費的訊息服務,開始收費引發的思考和分析
- 由script標籤引發了我對setTimeout非同步的思考非同步
- 由作業題引發對C++引用的一些思考C++
- JS學習筆記之由定時器引發的深入思考JS筆記定時器
- 由一份auto_ptr原始碼所引發的思考 (轉)原始碼
- 一場由React引發的前後端分離架構的思考React後端架構
- perl中如何執行外部命令
- for...in引發的思考
- 口碑割裂,內憂外患 由《最後生還者2》引發的思考
- 由Object.prototype.toString.call( )引發關於toString( )方法的思考Object
- 一道“史上最難”java面試題引發的執行緒安全思考Java面試題執行緒
- 由重構react元件引發的函數語言程式設計的思考React元件函數程式設計