使用gawk -i inplace some-awk-code some-file
(或在脚本@include "inplace"
内awk
)就地编辑文件(或任何其他扩展名)是一个安全漏洞。
为什么?
我该如何解决这个问题?
Sté*_*las 26
awk
在指定要运行的代码方面,GNU对标准有一些扩展。
在 standard 中awk
,您只能将代码作为一个或多个传递-f filepath
,其中filepath
被视为从\xc2\xb9 读取代码的文件路径,或者作为第一个非选项参数(如awk -- \'literal code here\'
),gawk 有一个还有几个选项:
-e \'literal code\'
(或--source \'literal code\'
) 与 中一样sed
,您可以将代码拆分为多个参数,并且可以将-f filepath
这些参数散布其中。-E filepath
(或--exec filepath
),与除了只能有一个之外相同-f
,并且其后的任何内容不考虑选项或变量分配,仅考虑文件路径(或-
标准输入)。--file filepath
: 的别名-f
。-i filepath
(或--include filepath
):类似于手册中所述的-f
行为,但有一些变化。现在的问题是gawk
,上述所有内容中的filepath并不总是被视为文件路径:
gawk
将尝试打开添加了扩展名的同一文件.awk
。这意味着它最终可能会解释您不希望的代码,但这在实践中不太可能成为问题,因为您希望它运行的文件并不存在,因此会发生这种情况。它不会用--traditional
or来做到这一点-W traditional
,但是你不能用它来使用大多数 gawk 的扩展。/
字符(并且不是-
),则在环境变量中查找 awk 程序,$AWKPATH
其方式与 shell 或execvp()
在 中查找无斜杠命令类似$PATH
,其中包括 with--posix
和 with --traditional
,对于-f
//的全部-i
(-E
以及不带或带.awk
如上所述添加的扩展名的情况)。第二点是这里问题的核心。
\n您可以通过以下方式找到默认的 AWKPATH:
\n$ (unset -v AWKPATH && gawk \'BEGIN{print ENVIRON["AWKPATH"]}\')\n.:/usr/share/awk\n
Run Code Online (Sandbox Code Playgroud)\n(尽管文章中没有这样的变量ENVIRON
!)
它以.
当前工作目录 开头,后面跟着一个系统位置,其中包含一些随 .NET 一起提供的扩展awk
或其他第三方模块gawk
。在这个系统上:
\n$ ls /usr/share/awk\nassert.awk getlong.awk intdiv0.awk ord.awk rewind.awk\nbits2str.awk getopt.awk isnumeric.awk passwd.awk round.awk\ncliff_rand.awk gettime.awk join .awk processarray.awk shellquote.awk\nctime.awk group.awk libintl.awk Quicksort.awk strtonum.awk\ndpkg-awk.awk have_mpfr.awk noassign.awk 可读.awk walkarray.awk\nftrans.awk inplace.awk ns_passwd .awk readfile.awk 零文件.awk\n\n
这意味着对于-f
/ -E
,如果您希望file
加载当前工作目录中的 ,则需要gawk -f ./file
,而不是如果当前工作目录中没有 ,gawk -f file
则可以从其他地方加载 a file
(或) 。就像您需要在 shell 中运行当前工作目录一样(除了出于安全原因通常不包含,并且它将尝试加载,如上所示)。file.awk
file
./cmd
cmd
$PATH
.
gawk
file.awk
这也适用于-i
,但通常用于-i
包含库中的 gawk 扩展,在这种情况下,您确实希望在要找到它们的目录中查找这些扩展,并且您确实希望.awk
添加扩展(因为那些库扩展通常有这样的扩展名)。
在 中gawk -i inplace \'some code\' some-file
,您确实想要gawk
查找/usr/share/awk/inplace.awk
(或inplace.awk
系统上安装的任何位置),但这里的问题是默认的 AWKPATH以开头.
,因此将首先在和gawk
中查找它。./inplace
./inplace.awk
如果您在/tmp
任何可写或已被其他人可写或通常不可信的目录中运行该文件,您最终可能会运行恶意代码。
例如,运行:
\necho \'BEGIN{system("reboot")}\' > /tmp/inplace\n
Run Code Online (Sandbox Code Playgroud)\n您会发现任何awk -i inplace
在当前工作目录下执行的脚本都会/tmp
重新启动系统!
要解决这个问题:
\ninplace
使用awk -i /usr/share/awk/inplace.awk
而不是硬编码扩展的路径,尽管您可能需要使路径适应每个系统或 gawk 部署。awk -i inplace
或.
从中删除所有相对路径组件$AWKPATH
:
$ (unset -v AWKPATH && gawk \'BEGIN{print ENVIRON["AWKPATH"]}\')\n.:/usr/share/awk\n
Run Code Online (Sandbox Code Playgroud)\n请记住,您将需要使用gawk -f ./file
或awk -E ./file
加载当前工作目录中的文件(即使没有$AWKPATH
如上所示的更改,您也可能已经这样做了)。另请注意,4.1.2 之前的 gawk 版本在查看$AWKPATH
.
该方法不能在#! /usr/bin/gawk -E
使用@include
尽管的脚本中使用,因为在启动$AWKPATH
时必须已经在环境中。gawk
因此,如果您有一个gawk
使用的脚本,@include "some-extension"
您需要告诉您的用户更改其$AWKPATH
扩展程序的路径或按照上面的方式对扩展程序的路径进行硬编码。
或者使用perl
它,它已经有-i
几十年的就地编辑选项,并且可以做任何awk
可以做的事情,并且以更明智的语法\xc2\xb2 和更少的可移植性问题来做更多的事情。但不要忘记--
in perl -i -ne \'perl code\' -- *.txt
,否则也会引入代码注入漏洞(或使用./*.txt
; 请参阅运行 perl -ne '...' * 的安全隐患)!
\xc2\xb9 ,但该文件路径除外,在-
这种情况下,大多数awk
实现将其解释为从标准输入读取代码。
\xc2\xb2 perl
\ 的-M
选项,可以看作是gawk
\的-i
等价物,使用不包含也不包含任何其他相对路径的默认搜索路径(请参阅)M
在 或 中$PERL5LIB
查找模块$PERLLIB
(unset -v PERL5LIB PERLLIB && perl -le \'print for @INC\'
.
小智 13
首先,感谢@StephaneChazelas,因为他说了我多年来在我写的每个论坛上一直说的话:放开sed -i
和awk -i inplace
!
除了你已经说过的内容(这对我来说是新的,这比我想象的更糟糕):
“-到位”?并不真地!
sed -i
两者awk -i inplace
都假装“就地”编辑,但事实并非如此。事实上,他们创建一个(隐藏的)临时文件作为输出,并最终移动它,覆盖原始文件。基本上与使用 POSIX 确认变体所做的事情相同,但自动如此。这听起来是个好主意,但从“就地”来看,我希望保留索引节点号以及所有权和文件模式。不是这种情况!事实上,在满足正确的先决条件的情况下,所有三个属性都会发生更改(即,允许用户写入文件,但具有与文件不同的主要组,具有粘滞位的目录,...)。
现在,不要误会我的意思:发生这种情况没有问题,如果我的进程写入临时文件然后自行复制,也会以同样的方式发生。但在这种情况下,我会意识到这一点,并确保文件模式等在更改后得到纠正。由于这假装就地工作,用户很可能没有意识到这种效果。
不存在的临时文件
下一个问题是:如果修改文件并在此过程中创建临时文件,我将采取预防措施:必须有足够的空间来保存临时文件,之后我将确保删除临时文件等。因为我不这样做不知道临时文件去了哪里(手册页中没有任何关于它的信息,据称一切都发生“就地”)我无法控制它,如果系统在脚本中崩溃(这些事情发生)我有不知道我什至留下了一些文物来占用磁盘空间。