一、目錄遍歷漏洞
1、原理介紹
通過用戶輸入,后端接收到參數直接拼接到指定路徑下讀取用戶的文件名,看似正常,但是用戶輸入的參數不可控制,黑客將非法的特殊字符作為文件名的一部分,操作到其他路徑下,甚至是跳轉到服務器敏感目錄,讀取敏感的配置文件,例如服務器的密碼文件,程序數據庫,redis等核心配置文件,因此具有一定的風險;
2、 人工代碼審計
關鍵字
1 )new FileInputStream( path
2 )new FileOutputStream( path
3 )new File( path
4 )RandomAccessFile fp = new RandomAccessFile(fname,"r");
5)mkdirs
6 )getOriginalFilename
7 )entry.getName(
...
類和函數
1)sun.nio.ch.FileChannelImpl
2)java.io.File.list/listFiles
3)java.io.FileInputStream
4)java.io.FileOutputStream
5)java.io.FileSystem/Win32FileSystem/WinNTFileSystem/UnixFileSystem
6)sun.nio.fs.UnixFileSystemProvider/WindowsFileSystemProvider
7)java.io.RandomAccessFile
8)sun.nio.fs.CopyFile
9)sun.nio.fs.UnixChannelFactory
10)sun.nio.fs.WindowsChannelFactory
11)java.nio.channels.AsynchronousFileChannel
12)FileUtil/IOUtil
13)filePath/download/deleteFile/move/getFile
在使用這些函數,關鍵字,類時,對用戶傳遞來的文件對象/文件名/文件路徑,是否進行了正確的處理:
- 是否限制了可操作文件的路徑,文件類型,文件所有者;
- 是否將敏感文件進行了排除
- 查找getPath(),getAbsolutePath(),查看是否有錯誤的路徑判斷;
- 在排查程序的安全策略配置文件,全局搜索permission,Java.io.FilePermission,grant的字樣,是否給程序的某部分路徑賦予了讀寫權限;
3、問題代碼示例
1)文件獲取響應文件路徑或者創建響應的目錄時,對讀入的文件路徑沒有進行過濾與限制,用戶可控;
//創建讀取 要拷貝的文件
InputStream inStream = new FileInputStream(file1);
//創建 要復制到的文件,filename未經校驗
OutputStream inStream = new FileOutputStream(new File(file2+"\\"+filename));
2)直接獲取文件名稱,未進行校驗,創建文件;
String orgName = mf.getOriginalFilename();//獲取文件名 然后
File file = new File(orgName);
file.mkdirs();//創建文件根目錄
3)路徑操作,壓縮項覆蓋,應用接收惡意zip壓縮包,會造成受保護的文件或目錄被覆蓋等危險;
//開始解壓
Enumeration entries = zipFile.entries();
//遍歷entries 獲得entry
while(entries.hasMoreElements()){
ZipEntry entry = (ZipEntry)entries.nextElement();
...
File targetFile = new File(entry.getName());
...
targetFile.getParentFile().mkdirs();
}
4、滲透測試
路徑遍歷漏洞一般隱藏在文件讀取或者展示圖片功能塊這樣的通過參數提交上來的文件名稱;
例如:http://www.test.com/my.jsp?file=abc.html,
攻擊者就可以假定my.jsp能夠從文件系統中獲取文件并構造如下惡意URL:
http://www.test.com/my.jsp?file=.../.../Windows/system.ini
http://www.test.com/my.jsp?file=%2e./...%2fWindows/system.ini
http://192.168.32.163/view.php?page=%20../../../../../../../../../../etc/passwd
1)目錄跳轉符可以是.../,也可以是.../的ASCII編碼或者unicode編碼等,或者~/ .%2E /%2F 空格%20 換行符%0a;
在Unix的系統中也可以使用Url編碼的換行符;
例如:.../.../etc/passwd%0a.jpg
如果文件系統在獲取含有換行符的文件名,會截斷為文件名,也可以嘗試%20;
例如:../../../index.jsp%20
2)Java%c0%ae 安全模式繞過漏洞:
Apache Tomcat UTF-8目錄遍歷漏洞,漏洞CVE編號為CVE-2008-2938,Tomcat處理請求中的編碼時存在漏洞,如果在context.xml中將allowLinking設置為true且連接配置為URLEncoding=UTF-8的話,則向Tomcat提交惡意請求就可以通過目錄遍歷攻擊讀取服務器上的任意文件,在Java端“%c0%ae”解析為“\uC0AE”,最后轉義為ASCII低字符-“-”,通過這個方法可以繞過目錄保護讀取包配置文件信息,將目錄跳轉符里的點編碼%c0%ae,如果服務器使用的受該漏洞影響的Tomcat版本,則可能攻擊成功;
http://www.target.com/%c0%ae%c0%ae/%c0%ae%c0%ae/foo/bar
3)繞過文件類型白名單過濾,可以借助"%00"截斷符在進行嘗試;
/WeBid/loader.php?js=…/…/…/…/…/WINDOWS/SchedLgU.txt%00.js
[
](https://www.cnblogs.com/macter/p/16181588.html)
5、審計案例1
用戶通過form表單輸入文件名稱,通過源碼逐層對文件進行非法字符的過濾與處理,導致任意文件讀取(目錄遍歷),漏洞的產生;
1)正常的用戶行為是輸入要讀取的合法文件

2)黑客利用未過濾文件名或目錄的漏洞讀取了其他目錄下的資源文件,通過遍歷該目錄或者上級路徑下的其他文件,例如

6、審計案例2
1)當輸入文件名為../1.txt(上級目錄需要存在),不存在的話,我們就一直../../../直到存在為止;

2)提交之后發現成功下載到當前目錄外的目錄文件

3)源碼分析
通過用戶輸入文件名之后,后端直接接收了文件名拼接到需要讀取的路徑下,進而讀取了文件,未對來自前端用戶輸入的文件名稱做非法過濾;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/file1/directroyTranersal02" method="get">
<input type="text" name="filename"/>
<input type="submit" value="uploaddown"/>
</form>
</body>
</html>
//目錄遍歷2
@GetMapping("/directroyTranersal02")
public void directoryTraversal02(HttpServletRequest request, HttpServletResponse response) throws IOException {
//獲取項目部署絕對路徑下的upload文件夾路徑,下載upload目錄下面的文件
String root = request.getServletContext().getRealPath("/upload");
//獲取文件名
String filename = request.getParameter("filename");
File file = new File(root + "/" + filename);
//根據文件路徑創建輸入流
FileInputStream fis = new FileInputStream(file);
//設置響應頭,彈出下載框
response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes()));
response.addHeader("Content-Length", "" + file.length());
byte[] b = new byte[fis.available()];
fis.read(b);
response.getOutputStream().write(b);
}
通過源碼分析,獲取到文件中含有../,當執行下載邏輯的時候,file類底層將會訪問該路徑讀取資源,如同人工通過瀏覽器讀取文件E:\java_project\FileUpload\src\main\webapp\upload..\1.txt

這里通過debug可以清楚看出來過程;
7、目錄遍歷+文件上傳(ofcms審計)
1)我們在idea使用Ctrl+Shift+R,進行全局搜索關鍵字filename;

2)發現 TemplateController類的save方法中還存在任意文件上傳漏洞;

可以看到文件名,文件內容都是可控的,且對用戶輸入的文件名沒有做../的處理,我們可以往服務器上寫任意文件;
3)定位漏洞 后臺->模板設置->模板文件->模板目錄->修改404.html

就愛注釋寫得好的程序員!!!!

4)抓包進行文件名修改或者添加../跳轉目錄在static靜態資源目錄下寫js馬子,對文件內容進行修改寫入惡意的jsp文件;

5)修改file_content為冰蝎馬,這里要進過url編碼;

6)修改file_name = …/…/…/static/shell.jsp

7)我們在Tomcat的war中,static目錄下發現了我們上傳的shell.jsp;

8)嘗試連接

成功連接!!!!
二、修復方案以及修復代碼
1、 全局過濾關鍵字;
private String fileNameValidate(String str) {
String fileNameListStr ="../|./|/.."; //這里為請求體中不能攜帶的關鍵字
if(null!=fileNameListStr && !"".equals(fileNameListStr))
{
str = str.toLowerCase();// 統一轉為小寫
log.info("sqlFilter===========================>>路徑遍歷過濾規則:"+ fileNameListStr);
String[] badStrs = fileNameListStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
return badStrs[i];
}
}
}
return null;
}
2、單個字符串過濾關鍵字;
public class FlieNamefilter {
//private static Pattern FilePattern = Pattern.compile("[\\\\/:*?\"<>|]");
private static Pattern FilePattern = Pattern.compile("[\\s\\.:?<>|]"); //過濾規則
public static String filenameFilter(String str) {
return str==null?null:FilePattern.matcher(str).replaceAll("");
}
public static void main(String[] args) {
String str="home/.. <>|logs/../:edata?";
//String filenameFilter = filenameFilter(str);
String filenameFilter = fileNameValidate(str);
System.out.println(filenameFilter);
}
}
private static String fileNameValidate(String str) {
String strInjectListStr ="../|./|/..| |<|>|:|?";
if(null!=strInjectListStr && !"".equals(strInjectListStr))
{
str = str.toLowerCase();
String[] badStrs = strInjectListStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
str= str.replace(badStrs[i], "");
}
}
}
return str;
}
3、代碼修復:
private static Pattern FilePattern = Pattern.compile("[\\\\/:*?\"<>|]");
/**
* 路徑遍歷 漏洞修復
* @param str
* @return
*/
public static String filenameFilter(String str) {
return str==null?null:FilePattern.matcher(str).replaceAll("");
}
建議在所有涉及到文件名的地方使用fileNameFilter過濾一下;
4、使用嚴格符合規范的可接受輸入白名單
- 使用正則表達式驗證文件路徑和文件名,如下:
- 推薦正則代碼案例:寫一個過濾函數,白名單形式的,
- 根據業務需求,只允許包含字母、數字、. 、/、-, 且不允許 . 和 / ,或者 . 和 \ 連在一起,直接完全防止 ../ ..\ 等情況的發生
public class CommUtils{
private static final String patternString = "^[a-zA-Z\\d-/.]+$";
private static final String patternString1 = "./|/.";
public static String filePathFilter(String filepath){
final String[] split = patternString1 .split("\\|");
for(String s : split){
filepath = filepath.replace(s,"");
}
if(filepath.matches(patternString)){
return filepath;
}
return null;
}
}
- 對文件路徑,后綴進行白名單控制,對包含惡意的符號或者空字節進行拒絕,如下,解析可接受字符的白名單輸入:從輸入中拒絕路徑中不需要的任何字符,它可以被刪除或者替換;
1 public class CleanPath {
2
3 public static String cleanString(String aString) {
4 if (aString == null) return null;
5 String cleanString = "";
6 for (int i = 0; i < aString.length(); ++i) {
7 cleanString += cleanChar(aString.charAt(i));
8 }
9 return cleanString;
10 }
11
12 private static char cleanChar(char aChar) {
13
14 // 0 ‐ 9
15 for (int i = 48; i < 58; ++i) {
16 if (aChar == i) return (char) i;
17 }
1
19 // 'A' ‐ 'Z'
20 for (int i = 65; i < 91; ++i) {
21 if (aChar == i) return (char) i;
22 }
23
24 // 'a' ‐ 'z'
25 for (int i = 97; i < 123; ++i) {
26 if (aChar == i) return (char) i;
27 }
28
29 // other valid characters
30 switch (aChar) {
31 case '/':
32 return '/';
33 case '.':
34 return '.';
35 case '‐':
36 return '‐';
37 case '_':
38 return '_';
39 case ' ':
40 return ' ';
41 }
42 return '%';
43 }
44 }
2 public static void main(String[] args) {
3 File file=new File(args[0]);
4 if (!isInSecureDir(file)) {
5 throw new IllegalArgumentException();
6 }
7 String canonicalPath = file.getCanonicalPath();
8 if (!canonicalPath.equals("/img/java/file1.txt") &&
9 !canonicalPath.equals("/img/java/file2.txt")) {
10 // Invalid file; handle error
11 }
12
13 FileInputStream fis = new FileInputStream(f);
14 } }
5、嚴格進行輸入驗證
- 也就是拒絕任何不嚴格符合規范的輸入,獲將其轉換為符合要求的輸入,即程序對非受信任的用戶輸入數據凈化,對網站用戶提交過來的文件名進行硬編碼或者統一編碼,過濾非法字符,黑名單過濾非法字符包括;
/ \ " : ~ | * ? < > % ../ ..\
public String filter(String data){
Pattern pattern = Pattern.complie("[\\s\\\\/:\\*\\?\\\"<>\\|]");
Matcher matcher = pattern.matcher(data);
data = matcher.replaceAll("");
return data;
}
如果黑名單過濾“ ..”,攻擊者可替換為"...."過濾”..“后,還剩下一個"..",從而實現繞過,黑名單的話,很容易過濾不嚴格,被各種方式繞過,仍然會有風險,建議盡量采用白名單的形式;
6、合理配置web服務器的目錄權限
(禁止目錄瀏覽,分配好目錄權限等)部署新的業務系統或者安裝新的軟件或應用后應通過web掃描工具積極查找系統是否存在目錄遍歷漏洞,盡可能不要在服務器上安裝與業務不相關的第三方軟件以避免引入目錄遍歷漏洞。
除此之外,還應該合理配置web服務器(禁止目錄瀏覽,分配好目錄權限等)并積極關注所使用的各種軟件和應用的版本發布情況,及時升級新的軟件版本。
不同web服務器禁止目錄瀏覽方法有所不同。對IIS而言,如果不需要可執行的CGI,可以刪除可執行虛擬目錄或直接關閉目錄瀏覽;如果確實需要可執行的虛擬目錄,建議將可執行的虛擬目錄單獨放在一個分區。
對于Apache而言,管理員需要修改配置文件,禁止瀏覽列出目錄和文件列表,如可通過修改conf目錄下的httpd.conf文件來禁止使用目錄索引。以Apache 2.2.25版本為例,打開httpd.conf文件將“Options Indexes FollowSymLinks”中的“Indexes”刪除,這樣web目錄下的所有目錄都不再生成索引。
三、參考鏈接:
https://blog.csdn.net/qq_41085151/article/details/113525348
https://www.cnblogs.com/zhangruifeng/p/16077916.html
https://www.cnblogs.com/jayus/p/11423769.html
https://www.cnblogs.com/macter/p/16181588.html#8-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99%E5%88%A0%E9%99%A4%E5%A4%8D%E5%88%B6%E7%A7%BB%E5%8A%A8%E9%81%8D%E5%8E%86
ofcms環境搭建:https://forum.butian.net/share/1229