findutilsマニュアルから:
たとえば、これら2つのコマンドの構造は次のとおりです。
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
非常に危険です。その理由は、「{}」がセミコロンやその他のシェル関連文字を含むことができるファイル名に拡張されるためです。たとえば、
/tmp/foo; rm -rf $HOME
上記の2つのコマンドは、誰かがファイルを作成した場合に誰かのホームディレクトリを削除できます。そのため、信頼できないデータ(ファイル名など)をさらに解釈するために、引数を解釈するコマンドに渡すコマンド(例: "sh")を実行しないでください。
シェルの場合、この問題に対するきちんとした解決策があります。
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
このアプローチはすべての問題を防ぐことを保証することはできませんが、攻撃者が選択したデータをシェルコマンドテキストに置き換えるよりもはるかに安全です。
find -exec sh -c "something {}" \;
代替項目が{}
引用されず、単一の文字列として扱われないのが問題の原因ですか?ソリューション
find -exec sh -c 'something "$@"' sh {} \;
ではが最初に
{}
置き換えられた{}
が引用されていないので、"$@"
元のコマンドと同じ問題がありますか?たとえば、これは、および?"$@"
に拡張されます。"/tmp/foo;"
"rm"
"-rf"
"$HOME"
なぜ
{}
脱出または引用しないのですか?
- 同じ種類の問題と解決策(
sh -c
該当する場合は含めるかどうか、find
必要ではない可能性があります)に適用され、最小限の例である他の例を提供できますか?注意を気を散らすことはできるだけ少ないのですか?バラより"bash -c"で実行されるコマンドに引数を与える方法
ありがとうございます。
答え1
これは実際には参照には関係なく、パラメータ処理に関連しています。
危険な例を考えてみましょう。
find -exec sh -c "something {}" \;
これはシェルによって解析され、6つの単語に分けられます:、、、、、(
find
もはや引用符なし)、拡張する内容はありません。シェルは、この6つの単語を引数として使用して実行されます。-exec
sh
-c
something {}
;
find
find
たとえば、処理する項目を見つけると、引数に置き換えられ、foo; rm -rf $HOME
実行されます。{}
foo; rm -rf $HOME
sh
sh
-c
something foo; rm -rf $HOME
sh
-c
これで結果が解析されたことがわかりますsomething foo; rm -rf $HOME
(オプションではない最初のパラメータ)結果を実行します。
今、より安全なバリエーションを考えてみましょう。
find -exec sh -c 'something "$@"' sh {} \;
シェルは、、、、、、、、パラメータ
find
で実行されます。find
-exec
sh
-c
something "$@"
sh
{}
;
これ
find
が見つかったら、foo; rm -rf $HOME
再び置き換えられ、{}
パラメータ、、、で実行されます。sh
sh
-c
something "$@"
sh
foo; rm -rf $HOME
sh
-c
、およびsomething "$@"
を実行するコマンドで処理し、sh
およびをfoo; rm -rf $HOME
位置引数として処理します(から始まる$0
)、"$@"
次に拡張foo; rm -rf $HOME
単一値として、something
単一の引数で実行しますfoo; rm -rf $HOME
。
を使用してこれを表示できますprintf
。新しいディレクトリを作成し、入力して実行します。
touch "hello; echo pwned"
次のように最初のバリアントを実行します。
find -exec sh -c "printf \"Argument: %s\n\" {}" \;
生産する
Argument: .
Argument: ./hello
pwned
2番目のバリアントは次のように実行されます。
find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;
生産する
Argument: .
Argument: ./hello; echo pwned
答え2
パート1:
find
テキスト置換を使用します。
はい、次のように引用符を追加しない場合は、次のようになります。
find . -type f -exec sh -c "echo {}" \;
; echo owned
攻撃者は次のファイルを生成する可能性があります。
sh -c "echo ; echo owned"
echo
これでシェルが実行されますecho owned
。
ただし、引用符を追加すると、攻撃者は引用符を終了して次のファイルを生成して、その後に悪意のあるコマンドを入力する可能性があります'; echo owned
。
find . -type f -exec sh -c "echo '{}'" \;
これによりシェルが実行されますecho ''
。echo owned
(二重引用符を一重引用符に置き換えると、攻撃者は他の種類の引用符を使用することもできます。)
パート2:
では、find -exec sh -c 'something "$@"' sh {} \;
最初{}
はシェルで解釈されずに直接実行されるため、execve
シェル引用符を追加しても役に立ちません。
find -exec sh -c 'something "$@"' sh "{}" \;
シェルが実行される前に二重引用符を削除するため、効果はありませんfind
。
find -exec sh -c 'something "$@"' sh "'{}'" \;
引用符の追加はシェルで特に処理されないため、ほとんどの場合、これはコマンドが必要に応じて実行されないことを意味します。
/tmp/foo;
, rm
, で拡張するのは のパラメータであり、その引数-rf
は実行されるコマンドとみなされないことがあるので問題になりません。$HOME
something
something
パート3:
信頼できない入力を受け入れ、それをコマンドとして実行するすべての項目(たとえば、およびの一部)に同様の考慮事項が適用されるとしxargs
ますparallel
。
答え3
find -exec sh -c "something {}" \;
1. 代替項目が{}
引用されず、単一の文字列として扱われないのが問題の原因である可能性がありますか?
ある意味ではそうだけどここでは引用符は役に立ちません。。置き換えるファイル名には、{}
次のものを含めることができます。どの恥、引用符を含める。どの形式の引用が使用されているかに関係なく、ファイル名に同じ参照を含めることができ、参照を「中断」することができます。
2. ...しかし
{}
引用されていないので、"$@"
元のコマンドと同じ問題がありますか?たとえば、"$@"
に拡張される予定ですか"/tmp/foo;", "rm", "-rf", and "$HOME"
?
いいえ。"$@"
別の単語として位置引数に展開されます。いいえ彼らはより分かれています。これは{}
それ自体の引数であり、find
現在find
のファイル名も別の引数に渡されますsh
。これはシェルコマンド自体として扱われず、シェルスクリプト内の変数として直接使用できます。
...
{}
エスケープしたり引用したらどうですか?
ほとんどのシェルではこれは必要ありません。を実行すると、空白行が印刷されるとfish
予想されます。fish -c 'echo {}'
しかし、引用しても構いません。シェルは引用符のみを削除します。
3.他の例を挙げることができますか?
ある種のコード(*)で処理される文字列からファイル名をそのまま (または制御されていない他の文字列) 拡張するたびに任意のコマンドを実行できます。
たとえば、これは$f
Perlコードを直接拡張するため、ファイル名に二重引用符が含まれていると問題が発生する可能性があります。ファイル名の引用符はPerlコードの引用符で終わり、ファイル名の残りの部分にPerlコードを含めることができます。
touch '"; print "HELLO";"'
for f in ./*; do
perl -le "print \"size: \" . -s \"$f\""
done
(Perlはコード全体を実行する前に事前に解析するため、ファイル名が少し奇妙になる可能性があります。解析エラーを避ける必要があります。)
パラメータを介して安全に渡すことができますが:
for f in ./*; do
perl -le 'print "size: " . -s $ARGV[0]' "$f"
done
(1つのシェルを別のシェルで直接実行することは意味がありませんが、そうすると同様の状況になりますfind -exec sh ...
)
(*一部のコードにはSQLが含まれているため、XKCDでなければなりません。https://xkcd.com/327/説明を追加: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables)
答え4
あなたの懸念は、GNU Parallelが入力を引用する理由です。
touch "hello; echo pwned"
find . -print0 | parallel -0 printf \"Argument: %s\\n\" {}
これは実行されませんecho pwned
。
シェルを実行するので、コマンドを拡張しても突然驚くことはありません。
# This _could_ be run without spawining a shell
parallel "grep -E 'a|bc' {}" ::: foo
# This cannot
parallel "grep -E 'a|bc' {} | wc" ::: foo
シェル問題のビルドの詳細については、次を参照してください。https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell