如何安全地使用 gawk 的 -i 选项或 @include 指令?

Sté*_*las 24 security awk

使用gawk -i inplace some-awk-code some-file(或在脚本@include "inplace"awk)就地编辑文件(或任何其他扩展名)是一个安全漏洞

为什么?

我该如何解决这个问题?

Sté*_*las 26

awk在指定要运行的代码方面,GNU对标准有一些扩展。

\n

在 standard 中awk,您只能将代码作为一个或多个传递-f filepath,其中filepath被视为从\xc2\xb9 读取代码的文件路径,或者作为第一个非选项参数(如awk -- \'literal code here\'),gawk 有一个还有几个选项:

\n
    \n
  • -e \'literal code\'(或--source \'literal code\') 与 中一样sed,您可以将代码拆分为多个参数,并且可以将-f filepath这些参数散布其中。
  • \n
  • -E filepath(或--exec filepath),与除了只能有一个之外相同-f,并且其后的任何内容不考虑选项或变量分配,仅考虑文件路径(或-标准输入)。
  • \n
  • --file filepath: 的别名-f
  • \n
  • -i filepath(或--include filepath):类似于手册中所述的-f行为,但有一些变化。
  • \n
\n

现在的问题是gawk,上述所有内容中的filepath并不总是被视为文件路径:

\n
    \n
  1. 如果文件路径不存在,gawk将尝试打开添加了扩展名的同一文件.awk。这意味着它最终可能会解释您不希望的代码,但这在实践中不太可能成为问题,因为您希望它运行的文件并不存在,因此会发生这种情况。它不会用--traditionalor来做到这一点-W traditional,但是你不能用它来使用大多数 gawk 的扩展。
  2. \n
  3. 如果filepath不包含/字符(并且不是-),则在环境变量中查找 awk 程序,$AWKPATH其方式与 shell 或execvp()在 中查找无斜杠命令类似$PATH,其中包括 with--posix和 with --traditional,对于-f//的全部-i-E以及不带或带.awk如上所述添加的扩展名的情况)。
  4. \n
\n

第二点是这里问题的核心。

\n

您可以通过以下方式找到默认的 AWKPATH:

\n
$ (unset -v AWKPATH && gawk \'BEGIN{print ENVIRON["AWKPATH"]}\')\n.:/usr/share/awk\n
Run Code Online (Sandbox Code Playgroud)\n

(尽管文章中没有这样的变量ENVIRON!)

\n

它以.当前工作目录 开头,后面跟着一个系统位置,其中包含一些随 .NET 一起提供的扩展awk或其他第三方模块gawk。在这个系统上:

\n
\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.awkfile./cmdcmd$PATH.gawkfile.awk

\n

这也适用于-i,但通常用于-i包含中的 gawk 扩展,在这种情况下,您确实希望在要找到它们的目录中查找这些扩展,并且您确实希望.awk添加扩展(因为那些库扩展通常有这样的扩展名)。

\n

在 中gawk -i inplace \'some code\' some-file,您确实想要gawk查找/usr/share/awk/inplace.awk(或inplace.awk系统上安装的任何位置),但这里的问题是默认的 AWKPATH开头.,因此将首先在和gawk中查找它。./inplace./inplace.awk

\n

如果您在/tmp任何可写或已被其他人可写或通常不可信的目录中运行该文件,您最终可能会运行恶意代码。

\n

例如,运行:

\n
echo \'BEGIN{system("reboot")}\' > /tmp/inplace\n
Run Code Online (Sandbox Code Playgroud)\n

您会发现任何awk -i inplace在当前工作目录下执行的脚本都会/tmp重新启动系统!

\n

要解决这个问题:

\n
    \n
  • inplace使用awk -i /usr/share/awk/inplace.awk而不是硬编码扩展的路径awk -i inplace,尽管您可能需要使路径适应每个系统或 gawk 部署。

    \n
  • \n
  • .从中删除所有相对路径组件$AWKPATH

    \n
    $ (unset -v AWKPATH && gawk \'BEGIN{print ENVIRON["AWKPATH"]}\')\n.:/usr/share/awk\n
    Run Code Online (Sandbox Code Playgroud)\n

    请记住,您将需要使用gawk -f ./fileawk -E ./file加载当前工作目录中的文件(即使没有$AWKPATH如上所示的更改,您也可能已经这样做了)。另请注意,4.1.2 之前的 gawk 版本在查看$AWKPATH.

    \n

    该方法不能在#! /usr/bin/gawk -E使用@include尽管的脚本中使用,因为在启动$AWKPATH时必须已经在环境中。gawk因此,如果您有一个gawk使用的脚本,@include "some-extension"您需要告诉您的用户更改其$AWKPATH扩展程序的路径或按照上面的方式对扩展程序的路径进行硬编码。

    \n
  • \n
  • 或者使用perl它,它已经有-i几十年的就地编辑选项,并且可以做任何awk可以做的事情,并且以更明智的语法\xc2\xb2 和更少的可移植性问题来做更多的事情。但不要忘记--in perl -i -ne \'perl code\' -- *.txt,否则也会引入代码注入漏洞(或使用./*.txt; 请参阅运行 perl -ne '...' * 的安全隐患)!

    \n
  • \n
\n
\n

\xc2\xb9 ,但该文件路径除外,在-这种情况下,大多数awk实现将其解释为从标准输入读取代码。

\n

\xc2\xb2 perl\ 的-M选项,可以看作是gawk\的-i等价物,使用不包含也不包含任何其他相对路径的默认搜索路径(请参阅)M在 或 中$PERL5LIB查找模块$PERLLIB(unset -v PERL5LIB PERLLIB && perl -le \'print for @INC\'.

\n


小智 13

首先,感谢@StephaneChazelas,因为他说了我多年来在我写的每个论坛上一直说的话:放开sed -iawk -i inplace

除了你已经说过的内容(这对我来说是新的,这比我想象的更糟糕):

  1. “-到位”?并不真地!

    sed -i两者awk -i inplace都假装“就地”编辑,但事实并非如此。事实上,他们创建一个(隐藏的)临时文件作为输出,并最终移动它,覆盖原始文件。基本上与使用 POSIX 确认变体所做的事情相同,但自动如此。这听起来是个好主意,但从“就地”来看,我希望保留索引节点号以及所有权和文件模式。不是这种情况!事实上,在满足正确的先决条件的情况下,所有三个属性都会发生更改(即,允许用户写入文件,但具有与文件不同的主要组,具有粘滞位的目录,...)。

    现在,不要误会我的意思:发生这种情况没有问题,如果我的进程写入临时文件然后自行复制,也会以同样的方式发生。但在这种情况下,我会意识到这一点,并确保文件模式等在更改后得到纠正。由于这假装就地工作,用户很可能没有意识到这种效果。

  2. 不存在的临时文件

    下一个问题是:如果修改文件并在此过程中创建临时文件,我将采取预防措施:必须有足够的空间来保存临时文件,之后我将确保删除临时文件等。因为我不这样做不知道临时文件去了哪里(手册页中没有任何关于它的信息,据称一切都发生“就地”)我无法控制它,如果系统在脚本中崩溃(这些事情发生)我有不知道我什至留下了一些文物来占用磁盘空间。

  • 确实,perl/sed 的 `-i` 或 `gawk` 的 `inplace.awk` 不会就地修改文件,破坏硬链接和符号链接,并且可能会丢失一些元数据,但这里没有完美的解决方案。保留原始文件的替代方法也有其自身的问题,例如文件处理一半的风险,它不是原子的,并且在另一个进程正在使用文件时更改文件可能会产生令人讨厌的副作用。一切都是“perl -i”方法(GNU“sed”或“awk”模拟)是最不坏的方法之一,而且可能是最好的通用方法。 (2认同)