Java 文件名空字節截斷漏洞
空字節截斷漏洞漏洞在諸多編程語言中都存在,究其根本是Java在調用文件系統(C實現)讀寫文件時導致的漏洞,并不是Java本身的安全問題。不過好在高版本的JDK在處理文件時已經把空字節文件名進行了安全檢測處理。
文件名空字節漏洞歷史
2013年9月10日發布的Java SE 7 Update 40修復了空字節截斷這個歷史遺留問題。此次更新在java.io.File類中添加了一個isInvalid方法,專門檢測文件名中是否包含了空字節。
/**
* Check if the file has an invalid path. Currently, the inspection of
* a file path is very limited, and it only covers Nul character check.
* Returning true means the path is definitely invalid/garbage. But
* returning false does not guarantee that the path is valid.
*
* @return true if the file path is invalid.
*/
final boolean isInvalid() {
if (status == null) {
status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
: PathStatus.INVALID;
}
return status == PathStatus.INVALID;
}
修復的JDK版本所有跟文件名相關的操作都調用了isInvalid方法檢測,防止文件名空字節截斷。

修復前(Java SE 7 Update 25)和修復后(Java SE 7 Update 40)的對比會發現Java SE 7 Update 25中的java.io.File類中并未添加\u0000的檢測。

受空字節截斷影響的JDK版本范圍:JDK<1.7.40,單是JDK7于2011年07月28日發布至2013年09月10日發表Java SE 7 Update 40這兩年多期間受影響的就有16個版本,值得注意的是JDK1.6雖然JDK7修復之后發布了數十個版本,但是并沒有任何一個版本修復過這個問題,而JDK8發布時間在JDK7修復以后所以并不受此漏洞影響。
參考:
- JDK-8014846 : File and other classes in java.io do not handle embedded nulls properly。
- 維基百科-Java版本歷史
- Oracle Java 歷史版本下載
Java文件名空截斷測試
測試類FileNullBytes.java:
package com.anbai.sec.filesystem;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author yz
*/
public class FileNullBytes {
public static void main(String[] args) {
try {
String fileName = "/tmp/null-bytes.txt\u0000.jpg";
FileOutputStream fos = new FileOutputStream(new File(fileName));
fos.write("Test".getBytes());
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用JDK1.7.0.25測試成功截斷文件名:

使用JDK1.7.0.80測試寫文件截斷時拋出java.io.FileNotFoundException: Invalid file path異常:

空字節截斷利用場景
Java空字節截斷利用場景最常見的利用場景就是文件上傳時后端獲取文件名后使用了endWith、正則使用如:.(jpg|png|gif)$驗證文件名后綴合法性且文件名最終原樣保存,同理文件刪除(delete)、獲取文件路徑(getCanonicalPath)、創建文件(createNewFile)、文件重命名(renameTo)等方法也可適用。
空字節截斷修復方案
最簡單直接的方式就是升級JDK,如果擔心升級JDK出現兼容性問題可在文件操作時檢測下文件名中是否包含空字節,如JDK的修復方式:fileName.indexOf('\u0000')即可。
Java Web安全