gawk -i inplace some-awk-code some-file
内部で(またはスクリプト@include "inplace"
から)awk
ファイル(または他の拡張子)を編集します。セキュリティホールです。
なぜ?
この問題をどのように解決できますか?
答え1
awk
GNUには、実行するコードを指定するという点で、標準に対するいくつかの拡張があります。
標準では、コードを読み取るファイルパスと見なされる1つ以上の最初の非オプション引数(gawkなど)でのみawk
コードを渡すことができます。 gawkにはより多くのオプションがあります。-f filepath
filepath
awk -- 'literal code here'
-e 'literal code'
(または--source 'literal code'
)のように、sed
コードを複数のパラメータに分割し、-f filepath
そのパラメータをパラメータ間で分散できます。-E filepath
(または--exec filepath
)、-f
1つしか存在できないこと、およびそれ以降のすべての項目は、オプションまたは変数の割り当てを考慮せずにファイルパス(または-
標準入力)のみを考慮することを除いて同じです。--file filepath
:エイリアス-f
。-i filepath
(または):--include filepath
動作に似ていますが、若干の変更があります。-f
マニュアルに記載されているように。
今質問はgawk
、ファイルパス上記のすべてが常にファイルパスと見なされるわけではありません。
- もしファイルパス存在しない場合は、
gawk
拡張子が追加された同じファイルを開こうとします.awk
。つまり、意図しないコードを解釈する可能性がありますが、実行しようとしているファイルが存在しないため、実際に問題になる可能性はほとんどありません。--traditional
orではそうしませんが、-W traditional
ほとんどのgawk拡張機能を使用することはできません。 - もしファイルパス
/
文字が含まれていない場合(およびそうでない場合-
)、awkプログラムは$AWKPATH
シェルと同様の方法で環境変数を検索するか、withおよびwithを含むすべての//(および説明されているように、またはwithでexecvp()
スラッシュのないコマンドを検索します。の拡張子が追加された場合)。$PATH
--posix
--traditional
-f
-i
-E
.awk
2番目のポイントはここで問題の中心です。
以下では、デフォルトのAWKPATHを見つけることができます。
$ (unset -v AWKPATH && gawk 'BEGIN{print ENVIRON["AWKPATH"]}')
.:/usr/share/awk
(メンションにはそんな変数がないのにENVIRON
!)
現在の作業ディレクトリから始めて、その後にいくつかの拡張機能または.
.NETに付属の他のawk
サードパーティモジュールを含むシステムの場所が続きますgawk
。このシステムでは:
$ls /usr/share/awk Assert.awk getlong.awk intdiv0.awk ord.awk rewind.awk bit2str.awk getopt.awk isnumeric.awk passwd.awk round.awk Cliff_rand.awk gettime.awk Join.awk processarray.awk shellquote.awk ctime.awk グループ.awk libintl.awk fastsort.awk strtonum.awk dpkg-awk.awk have_mpfr.awk nosign.awk 読み取り可能.awk walkarray.awk ftrans.awk 所定の位置に.awk ns_passwd.awk ファイルの読み取り.awk ゼロファイル.awk
つまり、-f
/の場合は現在の作業ディレクトリにロードし-E
たい場合に必要であり、現在の作業ディレクトリにない場合は他の場所からロードできます(または)。シェルで実行するには、現在の作業ディレクトリが必要なのと同じです(セキュリティ上の理由から通常は含まれておらず、上記のようにロードしようとしていることを除いて)。file
gawk -f ./file
gawk -f file
file
file.awk
file
./cmd
cmd
$PATH
.
gawk
file.awk
これは一般的なものに加えて適用されます-i
。-i
含むこの場合、ライブラリのgawk拡張するそれらがなければならないディレクトリにそれらを見つけることを期待し、する拡張を追加したいのですが.awk
(ライブラリの拡張には通常これらの拡張があるため)。
(またはシステムにインストールされている場所)を探しgawk -i inplace 'some code' some-file
たいが、ここで問題はデフォルトのAWKPATHです。gawk
/usr/share/awk/inplace.awk
inplace.awk
スタートand.
なので、gawk
andで最初に照会されます。./inplace
./inplace.awk
/tmp
書き込み可能であるか、すでに他の人が書き込んだり、通常は信頼できないディレクトリでこのファイルを実行すると、マルウェアが実行される可能性があります。
たとえば、次のようにします。
echo 'BEGIN{system("reboot")}' > /tmp/inplace
awk -i inplace
現在の作業ディレクトリで実行されているすべてのスクリプトが/tmp
システムを再起動することがわかります。
この問題を解決するには:
inplace
各システムまたはGawk展開に合わせてパスを調整する必要があるかもしれませんが、拡張awk -i /usr/share/awk/inplace.awk
パスをハードコーディングする代わりに使用してください。awk -i inplace
または、
.
すべての相対パスコンポーネントを削除します$AWKPATH
。export AWKPATH="$(LC_ALL=C gawk 'BEGIN { n = split(ENVIRON["AWKPATH"], dirs, ":") for (i = 1; i <= n; i++) if (substr(dirs[i], 1, 1) == "/") { newawkpath = newawkpath sep dirs[i] sep = ":" } if (newawkpath == "") newawkpath = "/dev/null" print newawkpath}')"
現在の作業ディレクトリでファイルを使用または
gawk -f ./file
ロードする必要があることに注意してください(上記の変更を加えずにすでにこれを行っている可能性があります)。また、4.1.2より前のgawkバージョンがレビュー中であることに注意してください。awk -E ./file
$AWKPATH
$AWKPATH
この方法は起動時に環境にすでに存在している必要があるため、
#! /usr/bin/gawk -E
使用するスクリプトでは使用できません。したがって、使用するスクリプトがある場合は、ユーザーに拡張パスを変更するか、上記のように拡張パスをハードコードするように指示する必要があります。@include
$AWKPATH
gawk
gawk
@include "some-extension"
$AWKPATH
または、何十年も
perl
使用されていた-i
内部編集オプションを使用して、すべての可能なタスクを実行し、awk
よりスマートな構文²とより少ない移植性の問題でより多くのタスクを実行できます。しかし--
inを忘れないでくださいperl -i -ne 'perl code' -- *.txt
。そうしないと、コード注入の脆弱性が発生する可能性があります(またはを使用してください./*.txt
。を参照)。perl -ne '...'実行のセキュリティリスク*)!
¹そうでない場合ファイルパス-
この場合、ほとんどの実装はawk
これを標準入力からコードを読み取ると解釈します。
sと同じであると考えられる²perl
オプションは、他の相対パスを含まず、含まないデフォルト検索パスを使用します(参照)またはでモジュールを検索する-M
gawk
-i
M
$PERL5LIB
$PERLLIB
(unset -v PERL5LIB PERLLIB && perl -le 'print for @INC'
.
答え2
まず、私が書いたすべてのフォーラムで、私が何年も言ってきたことを教えてくれた@StephaneChazelasに感謝sed -i
しますawk -i inplace
。
すでに述べた内容に加えて(これは私にとって新しい内容であり、思ったよりも悪いです):
「-所定の位置に」か。まさか!
sed -i
どちらもawk -i inplace
「所定の位置で」編集するふりをしますが、そうではありません。実際、それらは(隠された)一時ファイルを出力として生成し、最終的に移動して元のファイルを上書きします。デフォルトでは、POSIX検証バリアントを使用するのと同じですが、自動です。これは良い考えのように聞こえますが、「内部」の観点から見ると、所有権とファイルモードだけでなく、inode番号も保存したいと思います。そうではありません!実際に正しい前提条件が満たされると、3つのプロパティがすべて変更されます(たとえば、ユーザーはファイルに書き込むことができますが、ファイルとは異なるデフォルトグループ、固定ビットを含むディレクトリなど)。誤解しないでください。このようなことが起こるには何の問題もありません。プロセスが一時ファイルに書き込まれ、それ自体がコピーされても、同じ方法で発生します。しかし、この場合、私はこれに気づくそして変更後はファイルモードなどが修正されているか確認してください。このふりは効果があるから所定の位置にユーザーはこの効果を認識しない可能性が高いです。
存在しない一時ファイル
次の質問は次のとおりです。その過程でファイルが修正され、一時ファイルが生成された場合は予防措置を講じます。一時ファイルを保存するのに十分なスペースが必要です。その後、必ず一時ファイルを削除します。一時ファイルがどこに行くのかわからないため(マンページにこれに関する情報がなく、すべてが「その場で」発生すると仮定しています)、これを制御できず、スクリプトでシステムがクラッシュする場合(これが起こる)、ディスクスペースを占めるいくつかのアーティファクトを残したのかわかりません。
答え3
また、gawkには、環境で見つからない場合のデフォルト値を持つAWKLIBPATH変数があります。この変数は、@load "library"
ライブラリファイルが見つかる場所を制御します。
共有ライブラリのロード
.
デフォルトは(私がインストールしたバージョンの場合)ディレクトリを使用していないようですが、変更される可能性があると思います。
答え4
今@include
は私ですcppawk
。 Cプリプロセッサ、対応する#include
マクロ、およびすべてを使用できるようにするawkの周りの小さなシェルスクリプトラッパーです。
#include
現在のディレクトリは検索されません。 1つのより良い機能があります。ヘッダー名が二重引用符で囲まれている場合は、ディレクティブを含む#include
ファイルと同じディレクトリでその名前を見つけます。これにより、cppawk
複数のファイルを含むプログラムを簡単に作成できます。デフォルトファイルは、#include "..."
ディレクティブの相対パスを使用して他のファイルを簡単に見つけることができます。
cppawk
独自のライブラリヘッダーがいくつかありますが、内部ファイルを編集するためのソリューションを提供するものはありません。このユーティリティを使用すると、ソリューションを簡単に再利用できます。
これは品質の低いプロトタイプです。
$ cat file.bak
alpha
bravo
charlie
$ cp file.bak file
$ cppawk '
#include "inplace.h"
{ out(NR, $0) }
' file
$ cat file
1 alpha
2 bravo
3 charlie
コンテンツinplace.h
:
BEGIN {
__inplace_tmpfile = "xyz.tmp"
__inplace_origfile = ARGV[1]
}
END {
close(__inplace_tmpfile)
system("mv " __inplace_tmpfile " " __inplace_origfile)
}
#define out(...) print __VA_ARGS__ > __inplace_tmpfile
これには少なくとも以下が必要です。一時ファイルをインポートし、内容をシェルからエスケープしてコマンドにARGV[1]
安全に挿入するためのより良い方法です。mv
out
リダイレクトのない基本的な実装を持つことができます。その後、コードが含まれるときにコードを変更する必要がないように、プログラムの代わりにprint
それを使用する習慣があります。cppawk
inplace.h
-f
スクリプト資料を含めることができるため、前処理なしでこれらの目標の一部を達成できます。inplace.h
ヘッダーの代わりに、次の内容を含むファイルを準備しますinplace.awk
。
BEGIN {
inplace = "xyz.tmp"
__inplace_origfile = ARGV[1]
}
END {
close(inplace)
system("mv " inplace " " __inplace_origfile)
}
一時ファイルを保持する変数名の匿名化を解除しました。これはインターフェイスの一部です。
残念ながら、コマンドライン内のスクリプトエントリとインクルードを混在させるには、-f
GNU固有の-e
オプションが必要です。
$ mv file.bak file
$ awk -f inplace.awk -e '{ print NR, $0 > inplace }' file
$ cat file
1 alpha
2 bravo
3 charlie
引用方法に関する質問もありますinplace.awk
。それをどこに置き、どのように見つけることができますか?#include
そのような問題はありません。コードと一緒に送信すると、それ自体の横にあります。ライブラリヘッダーに入れても問題cppawk
はあり<inplace.h>
ません。また、cppawk --prepro-only
プリプロセッサなしで実行できる完全な「翻訳単位」キャプチャを使用するオプションもありますcpp
。