inotifywait 的潛在解決方法不能產生 NUL 分隔的輸出
我目前正在編寫一個 bash 腳本,用於
inotifywait
對使用者提供的文件和目錄列表執行某些操作。我注意到,與許多 shell 工具不同,
inotifywait
它無法使用\0
. 這留下了使用專門製作但合法的文件名(包含換行符)進行注入攻擊的可能性。我想解決這個問題,以確保我的腳本不會引入任何不必要的漏洞。我的方法如下:
確保傳遞
inotifywait
給觀看的所有文件/路徑都刪除了尾隨反斜杠格式化
inotifywait
輸出--format "%e %w%f//"
以產生如下輸出:
<EVENT LIST> <FILE PATH>//
管道
inotifywait
輸出到 sed;任何//
在行尾發現\0
使用 bash
while read
循環讀取 -\0
分隔的記錄這意味著在第一條記錄之後,所有後續記錄都將有一個額外的前導換行符。這是剝離
然後可以在第一個空格處拆分每條記錄 - 在空格之前是事件列表(按照 inotifywait 逗號分隔) - 在空格之後是與事件關聯的完整路徑名
#!/bin/bash shopt -s extglob watchlist=("${@}") # Remove trailing slashes from any watchlist elements watchlist=("${watchlist[@]%%+(/)}") # Reduce multiple consecutive slashes to singles as per @meuh watchlist=("${watchlist[@]//+(\/)/\/}") printf -vnewline "\n" inotifywait -qrm "${watchlist[@]}" --format "%e %w%f//" | \ sed -u 's%//$%\x00%' | \ while IFS= read -r -d '' line; do line="${line#${newline}}" events="${line%% *}" filepath="${line#* }" printf "events=%s\nfilepath=%q\n" "$events" "$filepath" done
據我所知,它處理包含有趣字元的文件/路徑名 - 空格、換行符、引號等。但這似乎是一個相當不雅的組合。
出於這個問題的目的,該
${watchlist[]}
數組只是從命令行參數複製而來,但這個數組可能會以其他方式建構,並且可能包含“有趣”的字元。
- 是否有任何惡意路徑可以破壞這一點?即,對於任何給定的事件,使
$events
和$filepath
變數的內容不正確?- 如果這是防水的,有沒有更清潔的方法可以做到這一點?
注意我知道我可以很容易地編寫一個c程序來呼叫
inotify_add_watch()
和朋友來解決這個問題。但現在由於其他依賴關係,我正在使用 bash。我一直對是否在此處發布此內容或 codereview.SE 甚至主要的 so.SE 存在衝突。
您需要進行消毒
watchlist
才能將任何內容//
替換為/
. 考慮一個名為\nabc
(\n
換行符在哪裡)的目錄:$ mkdir t $ mkdir t/$'\nabc' $ touch t/$'\nabc'/x
如果通過了目錄,您將在行尾
t//$'\nabc'
看到帶有虛假的輸出://
$ inotifywait -m -r t//$'\nabc' --format "%e %w%f//" Setting up watches. Beware: since -r was given, this may take a while! Watches established. OPEN t// abc/x// ATTRIB t// abc/x// CLOSE_WRITE,CLOSE t// abc/x//
請注意,您也可以使用
-c
而不是--format
獲取csv樣式的輸出,它用換行符對文件名進行雙引號,但它更難解析,在我的例子中,核心轉儲在上面的範例中。
-c
和的範例輸出touch t/$'new\nfile'
:t/,CREATE,"new file"
有支持 NUL 分隔輸出的fork,您可以像這樣使用它:
#!/bin/bash shopt -s extglob watchlist=("${@}") # Remove trailing slashes from any watchlist elements watchlist=("${watchlist[@]%%+(/)}") printf -vnewline "\n" inotifywait -qrm "${watchlist[@]}" --format "%e %w%f%0" | \ while IFS= read -r -d '' line; do events="${line%% *}" filepath="${line#* }" printf "events=%s\nfilepath=%q\n" "$events" "$filepath" done
請注意,使用 –format 時,此版本預設不添加換行符。