我目前正在编写一个 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 一直存在冲突。
答案1
有叉支持 NUL 分隔输出,您可以像这样使用它:
#!/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 时,此版本默认不添加换行符。
答案2
您需要清理watchlist
以将 any 替换//
为/
。考虑一个名为\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
获取数据集样式输出,用换行符双引号文件名,但更难解析,在我的例子中,核心转储在上面的示例中。
-c
和的输出示例touch t/$'new\nfile'
:
t/,CREATE,"new
file"