perl -ne '…'を実行することのセキュリティへの影響*


27

どうやら、実行中:

perl -n -e 'some perl code' *

または

find . ... -exec perl -n -e '...' {} +

(の-p代わりに同じ-n

または

perl -e 'some code using <>' *

このサイトに投稿されたワンライナーでよく見られるものは、セキュリティに影響します。どうしたんだ?それを避ける方法は?

回答:


33

どうしたの

まず、多くのユーティリティと同様に、で始まるファイル名に問題があります-。にいる間:

sh -c 'inline sh script here' other args

他の引数はに渡されinline sh scriptます; perl同等の、

perl -e 'inline perl script here' other args

他の引数は、インラインスクリプトではなく、最初にperlに追加のオプションがスキャンされます。したがって、たとえば、-eBEGIN{do something evil}現在のディレクトリにファイルが呼び出されている場合、

perl -ne 'inline perl script here;' *

(の有無にかかわらず-n)は何か悪いことをします。

他のユーティリティと同様に、そのための回避策は、オプション終了マーカー(--)を使用することです。

perl -ne 'inline perl script here;' -- *

しかし、それでも、それはまだ危険であり、それは/ <>によって使用される演算子にかかっています。-n-p

この問題はperldoc perlopドキュメントで説明されています。

その特別な演算子は、入力の1行(1レコード、デフォルトでは行)を読み取るために使用されます。その入力は、各引数から順番に渡され@ARGVます。

に:

perl -pe '' a b

-pwhile (<>)コードのループを意味します(ここでは空です)。

<>最初に開きa、ファイルが使い果たされるまでレコードを一度に1行ずつ読み取り、次に開きbます...

問題は、ファイルを開くために、最初の安全でない形式を使用することですopen

open ARGV, "the file as provided"

その形式では、引数が

  • "> afile"afile書き込みモードで開き、
  • "cmd|"、実行cmdして出力を読み取ります。
  • "|cmd"、の入力への書き込み用に開いたストリームがありますcmd

したがって、たとえば:

perl -pe '' 'uname|'

呼び出されたファイルの内容uname|(完全に有効なファイル名btw)ではなく、unameコマンドの出力を出力します。

実行している場合:

perl -ne 'something' -- *

そして、誰かがrm -rf "$HOME"|現在のディレクトリに(再び完全に有効なファイル名)というファイルを作成しました(たとえば、そのディレクトリが他の人によってかつて書き込み可能であったか、危険なアーカイブを抽出したか、または危険なコマンドを実行したため、または他のソフトウェアの別の脆弱性が悪用された場合)、あなたは大きな問題に直面しています。その問題を認識することが重要な領域は、公共の場所でファイルを自動的に処理する/tmpツール(またはそのようなツールによって呼び出される可能性のあるツール)です。

呼ばれるファイルは> foofoo||foo問題です。しかし、それよりも少ない範囲< foofoo、先頭または末尾のASCII間隔文字(スペース、タブ、改行、cr ...を含む)と同様に、これらのファイルは処理されないか、間違ったファイルが処理されます。

また、一部のマルチバイト文字セットの一部の文字(ǖBIG5-HKSCSなど)がバイト0x7cで終わることに注意してください|

$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000  88  7c
        210   |
0000002

そのため、その文字セットを使用するロケールでは、

 perl -pe '' ./nǖ

実行しようとする./n\x88ようなコマンドをperl考えていないユーザーのロケールにそのファイル名を解釈しよう!

修正/回避方法

私の知る限り、perlシステム全体で一度だけの危険なデフォルト動作を変更するためにできることは何もありません。

まず、ファイル名の最初と最後の文字でのみ問題が発生します。だから、一方、perl -ne '' *またはperl -ne '' *.txt問題があり、

perl -ne 'some code' ./*.txt

すべての引数は、今で開始されていないため./、最後に.txt(そうではない-<>|...、スペース)。より一般的には、globの前にを付けることをお勧め./ます。また、他の多くのユーティリティ-で呼び出されるファイルや起動するファイルに関する問題も回避します-(ここでは、オプションの終わり(--)マーカーが不要になります)。

を使用-Tしてtaintモードをオンにすると、ある程度役立ちます。このような悪意のあるファイルが検出された場合、コマンドは中止されます(ただし>|場合のみ、not <またはwhitespace)。

そのようなコマンドをインタラクティブに使用する場合、何か危険なことが起こっていることを警告するので便利です。ただし、何らかの自動処理を行う場合は、ファイルを作成するだけで処理を失敗させることができるため、望ましくない場合があります。

名前に関係なく、すべてのファイルを処理する場合ARGV::readonly perl、CPAN のモジュールを使用できます(残念ながら、通常はデフォルトではインストールされません)。それは非常に短いモジュールです:

sub import{
   # Tom Christiansen in Message-ID: <24692.1217339882@chthon>
   # reccomends essentially the following:
   for (@ARGV){
       s/^(\s+)/.\/$1/;   # leading whitespace preserved
       s/^/< /;       # force open for input
       $_.=qq/\0/;    # trailing whitespace preserved & pipes forbidden
   };
};

基本的に、" foo|"例えばに変換することで@ARGVをサニタイズします"< ./ foo|\0"

コマンドのBEGINステートメントでも同じことができますperl -n/-p

perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*

ここで./は、使用されているという前提でそれを単純化します。

その(および副作用ARGV::readonly)かかわらずはということである$ARGVyour code here示すNUL文字を末尾のこと。

更新2015-06-03

perlv5.21.5 以降には、特別な処理を行わないことを除いて<<>>同様に動作する新しい演算子<>あります。引数はファイル名としてのみ考慮されます。したがって、これらのバージョンでは、次のように記述できます。

perl -e 'while(<<>>){ ...;}' -- *

(忘れてはいけない--、または使用./*、それはファイルを上書きしたり、予期しないコマンドを実行しているのを恐れずにいえ)。

-n/ -pそれでも危険な<>形式を使用します。また、シンボリックリンクが引き続き追跡されるため、信頼できないディレクトリで使用しても安全であるとは限りません。


2
あなたは一日中そのことに取り組んでいます、私は賭けます。よくやった。
mikeserv 14年

2
素敵なパールのアップデートが、それはperlの開発者は、(いくつかのスクリプトが安全でない動作に依存している可能性があるため、既存の-pを変更し、-nはできません)、それを利用するために-Pおよび-Nオプションを追加していないことを奇妙です
cas

9

@StéphaneChazelasの回答に加えて、-iコマンドラインオプションを使用する場合、この問題を心配する必要はありません。

$ perl -pe '' 'uname|'
Linux

$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.

-iオプションをperl使用する場合、statを使用してファイルのステータスをチェックしてから処理するためです。

$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40)                = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.

1
statチェックとその直後に行われる効果的なperl処理との間に競合状態はありませんか?
トーター

@Totor:いいえ。
クオンルム

約ではありませんstatファイルを所定の場所で-i編集するだけなので、実際のファイルパス以外の引数を受け入れることは意味をなさないので-i、特別な処理は行われません。
ステファンシャゼラス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.