Bash

inotifywait 的潛在解決方法不能產生 NUL 分隔的輸出

  • May 8, 2018

我目前正在編寫一個 bash 腳本,用於inotifywait對使用者提供的文件和目錄列表執行某些操作。

我注意到,與許多 shell 工具不同,inotifywait它無法使用\0. 這留下了使用專門製作但合法的文件名(包含換行符)進行注入攻擊的可能性。

我想解決這個問題,以確保我的腳本不會引入任何不必要的漏洞。我的方法如下:

  • 確保傳遞inotifywait給觀看的所有文件/路徑都刪除了尾隨反斜杠

  • 格式化inotifywait輸出--format "%e %w%f//"以產生如下輸出:

    • <EVENT LIST> <FILE PATH>//
  • 管道inotifywait輸出到 sed;任何//在行尾發現\0

  • 使用 bashwhile 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[]}數組只是從命令行參數複製而來,但這個數組可能會以其他方式建構,並且可能包含“有趣”的字元。

  1. 是否有任何惡意路徑可以破壞這一點?即,對於任何給定的事件,使$events$filepath變數的內容不正確?
  2. 如果這是防水的,有沒有更清潔的方法可以做到這一點?

注意我知道我可以很容易地編寫一個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 時,此版本預設不添加換行符。

引用自:https://unix.stackexchange.com/questions/442035