GNU/Linux >> Linux の 問題 >  >> Linux

Bash / posixシェルで変数を引用するのを忘れることのセキュリティへの影響?

unix.stackexchange.comをしばらくフォローしている場合は、
リストコンテキストで変数を引用符で囲まずに
残すことを
知っているはずです( echo $ var のように) )Bourne / POSIXでは
シェル(zshは例外)には非常に特別な意味があり、
非常に正当な理由がない限り、実行しないでください。

ここでは、いくつかのQ&Aで詳細に説明されています(例:シェルスクリプトが空白やその他の特殊文字でチョークするのはなぜですか?、二重引用符が必要なのはいつですか?、シェル変数の拡張とglobの効果とその分割、引用符vs引用符で囲まれていない文字列展開

これは、70年代後半のボーンシェルの最初のリリース以来のケースであり、Kornシェルによって変更されていません
(David Kornの最大の後悔の1つ
(質問#7 ))または bash これは主に
Kornシェルをコピーしたものであり、それがPOSIX/Unixによって指定された方法です。

現在、ここにはまだ多くの回答があり、
変数が引用されていない
公開されているシェルコードもあります。あなたは人々が今までに
学んだだろうと思っていたでしょう。

私の経験では、主に3つのタイプの人々が
変数の引用を省略しています:

  • 初心者。確かに、それは
    完全に直感的でない構文であるため、これらは許されます。そして、このサイトでの私たちの役割は、
    彼らを教育することです。

  • 忘れられた人々。

  • 何度も叩いても確信が持てない人、
    ボーンシェルの作者が
    すべての変数を引用するつもりはなかったと思う


この種の行動に関連するリスクを明らかにすれば、彼らを納得させることができるかもしれません。


変数の引用を忘れた場合に発生する可能性があるよりも、最悪の事態は何ですか。本当に 悪いですか?

ここで話しているのはどのような脆弱性ですか?

どのような状況で問題になる可能性がありますか?

承認された回答:

前文

まず、問題に対処するのは正しい方法ではないと思います。
これは、「人を殺してはいけません。
そうしないと刑務所に行くからです
」と言っているようなものです。 「。

同様に、変数を引用しないでください。そうしないと、
セキュリティの脆弱性が発生することになります。
変数を引用するのは間違っているためです(ただし、刑務所への恐怖が役立つ場合は、そうしないでください)。

電車に飛び乗ったばかりの人のための簡単な要約。

ほとんどのシェルでは、変数展開を引用符で囲まずに残します(ただし、
それ(およびこの回答の残りの部分)はコマンド
置換にも適用されます( `...` または$(...) )および算術展開( $((...)) または$[...] ))非常に特別な
意味があります。それを説明する最良の方法は、
ある種の暗黙的なsplit + globを呼び出すようなものです。 オペレーター¹。

cmd $var

別の言語では、次のように記述されます:

cmd(glob(split($var)))

$ var 最初に、 $ IFSを含む複雑な
ルールに従って単語のリストに分割されます 特別なパラメータ(分割 一部)
そして、その分割の結果として生じる各単語は、
パターンと見なされます。 これは、それに一致するファイルのリストに展開されます
glob 一部)。

例として、 $ varの場合 *。txt、/ var/*。xmlが含まれています および$IFS が含まれています 、 cmd いくつかの引数を使用して呼び出されます。
最初の引数はcmdです。 次はtxtです 現在のディレクトリとxml内のファイル / var内のファイル 。

cmdを呼び出したい場合 2つのリテラル引数cmdだけで および*。txt、/ var/*。xml 、あなたは書くでしょう:

cmd "$var"

これは、他のより身近な言語になります:

cmd($var)

シェルの脆弱性とはどういう意味ですか ?

結局のところ、シェルスクリプトはセキュリティに敏感なコンテキストで使用されるべきではないことが昔から知られていました。
確かに、変数を引用符で囲まないままにしておくことはバグですが、それはできません。
それだけの害はありますか?

まあ、誰もがシェルスクリプトをWeb CGIに使用してはいけないと言うだろうという事実にもかかわらず、またはありがたいことに
現在、ほとんどのシステムはsetuid/setgidシェルスクリプトを許可していません。
1つshellshock(2014年9月に話題になったリモートで悪用可能なbashバグ
)が明らかにしたことは、シェルがおそらく使用されるべきではない場所で
まだ広く使用されていることです:
CGI、DHCPクライアントsudoersコマンドのフックスクリプト
によって呼び出されます ( asでない場合 )setuidコマンド…

時々無意識のうちに。たとえば、 system('cmd $ PATH_INFO') phpで / perl / python CGIスクリプトは、シェルを呼び出してそのコマンドラインを解釈します(
cmdという事実は言うまでもありません。 それ自体がシェルスクリプトである可能性があり、その
作成者は、CGIから呼び出されることを予期していなかった可能性があります。

特権のエスカレーションへのパスがある場合、つまり誰か(彼を攻撃者と呼びましょう)の場合に脆弱性があります )
彼が意図していないことをすることができます。

常にそれは攻撃者を意味します データを提供すると、そのデータは
特権ユーザー/プロセスによって処理され、
バグが原因で、ほとんどの場合、
実行してはいけないことを
実行します。

基本的に、バグのあるコードが攻撃者の制御下でデータを処理するときに問題が発生します。

さて、そのデータがどこにあるかは必ずしも明らかではありません
から来る可能性があり、コードが信頼できないデータを処理するようになるかどうかを判断するのは難しいことがよくあります。

変数に関する限り、CGIスクリプトの場合、
非常に明白ですが、データはCGI GET / POSTパラメータであり、
Cookie、パス、ホスト…パラメータなどです。

>

setuidスクリプト(
別のユーザーによって呼び出されたときに1人のユーザーとして実行される)の場合、それは引数または環境変数です。

もう1つの非常に一般的なベクトルは、ファイル名です。ディレクトリから
ファイルリストを取得している場合は、攻撃者によって
ファイルがそこに植えられている可能性があります

その点で、対話型シェルのプロンプトでも、
脆弱になる可能性があります( / tmp 内のファイルを処理する場合)。 または〜/ tmp たとえば)。

〜/ .bashrcでさえ 脆弱である可能性があります(たとえば、 bash
sshを介して呼び出されたときに解釈します ForcedCommandを実行するには gitのように
クライアントの制御下にあるいくつかの変数を使用したサーバー展開)。

現在、信頼できないデータを処理するためにスクリプトを直接呼び出すことはできませんが、
別のコマンドで呼び出すことができます。または、
間違ったコードが、スクリプトにコピーアンドペーストされる可能性があります(3
年後または同僚の1人によって)。
特に重要の1つの場所
コードのコピーがどこにあるかわからないため、Q&Aサイトに回答があります。

ビジネスに至るまで。どれくらい悪いですか?

変数(またはコマンド置換)を引用符で囲まないままにしておくことは、シェルコードに関連する
セキュリティの脆弱性の最大の原因です。これらのバグは
脆弱性につながることが多いためですが、引用符で囲まれていない変数がよく見られるためです。

関連:cpとmkdirではなくinstallを使用するのはなぜですか?

実際、シェルコードの脆弱性を探すとき、
最初に行うことは、引用符で囲まれていない変数を探すことです。
簡単に見つけることができ、多くの場合、適切な候補であり、一般的に
攻撃者が制御するデータにさかのぼることができます。

引用符で囲まれていない変数が
脆弱性に変わる可能性のある方法は無数にあります。ここでは、いくつかの一般的な傾向について説明します。

情報開示

split が原因で、ほとんどの人は引用符で囲まれていない
変数に関連するバグにぶつかります。 一部(たとえば、最近ではファイルの名前にスペースが含まれるのが一般的であり、スペースはIFSのデフォルト値になっています)。多くの人がグロブを見落とします 部。 グロブ 一部は少なくとも分割と同じくらい危険です 一部。

消毒されていない外部入力で行われるグロブは、
攻撃者
を意味します 任意のディレクトリのコンテンツを読み取らせることができます。

で:

echo You entered: $unsanitised_external_input

$ unsanitised_external_inputの場合 / *が含まれています 、つまり
攻撃者
/の内容を見ることができます 。大きな問題ではない。
/ home / * を使用すると、さらに面白くなります これにより、マシン上の
ユーザー名のリスト/ tmp / *が表示されます。 、 /home/*/.forward
他の危険な慣行のヒントについては、 / etc / rc * / * 有効な
サービスの場合…個別に名前を付ける必要はありません。 / * / * / * / * / * / * ...の値 ファイルシステム全体が一覧表示されます。

サービス拒否の脆弱性。

前のケースを少しやりすぎて、DoSがあります。

実際、サニタイズされていない
入力を持つリストコンテキスト内の引用符で囲まれていない変数は、少なくとも DoSの脆弱性。

熟練したシェルスクリプターでさえ、一般的に次のようなものを引用するのを忘れます:

#! /bin/sh -
: ${QUERYSTRING=$1}

no-opコマンドです。何がうまくいかない可能性がありますか?

これは、 $ 1を割り当てることを意味します $ QUERYSTRING $ QUERYSTRINGの場合 設定されていませんでした。これは、CGIスクリプトを
コマンドラインからも呼び出し可能にする簡単な方法です。

その$QUERYSTRING ただし、まだ拡張されており、
引用されていないため、 split + glob 演算子が呼び出されます。

現在、
拡張するのに特に費用がかかるグロブがいくつかあります。 /* / * / * / * 1つは、最大4レベル下のディレクトリを
リストすることを意味するので十分に悪いです。ディスクとCPUのアクティビティに加えて、
これは、数万のファイルパスを保存することを意味します
(ここでは、最小のサーバーVMで40k、そのうちの10kはディレクトリです)。

/* / * / * / * / .. / .. / .. / .. / * / * / * / * 40kx10kおよび/* / * / * / * / .. / .. / .. / .. / * / * / * / * / .. / .. / .. / ../*を意味します/ * / * / *
最も強力なマシンでさえもひざまずくには十分です。

自分で試してみてください(ただし、マシンが
クラッシュまたはハングする準備をしてください):

a='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*' sh -c ': ${a=foo}'

もちろん、コードが次の場合:

echo $QUERYSTRING > /some/file

次に、ディスクをいっぱいにすることができます。

シェルでGoogle検索を実行するだけです
cgiまたはbash
cgiまたはksh
cgi
シェルでCGIを作成する方法を示すページがいくつかあります。

パラメータを処理するものの半分が脆弱であることに注意してください。

DavidKornの
自分の
ものでさえ脆弱です(Cookieの処理を見てください)。

任意のコード実行の脆弱性まで

攻撃者の場合、任意のコードの実行は最悪のタイプの脆弱性です。
任意のコマンドを実行できます。
彼が実行できることには制限がありません。

これは通常、分割 それらにつながる部分。その
分割により、1つだけが予想される場合に、コマンドに渡されるいくつかの引数が
発生します。それらの最初のものは予想されるコンテキストで
使用されますが、他のものは異なるコンテキストで使用されるため
、異なる解釈が行われる可能性があります。例を挙げてみましょう:

awk -v foo=$external_input '$2 == foo'

ここでの目的は、 $ external_inputのコンテンツを割り当てることでした。 fooへのシェル変数 awk 変数。

今:

$ external_input='x BEGIN{system("uname")}'
$ awk -v foo=$external_input '$2 == foo'
Linux

$ external_inputの分割の結果として生じる2番目の単語 fooに割り当てられていません ただし、 awkと見なされます コード(ここでは
任意のコマンドを実行します: uname

これは、他の
コマンド( awk )を実行できるコマンドでは特に問題になります。 、 env sed (GNU one)、 perl find …)特に
GNUバリアント(引数の後にオプションを受け入れる)を使用します。
場合によっては、
kshのような他のコマンドを実行できるとは思わないでしょう。 、 bash またはzsh[ またはprintf

for file in *; do
  [ -f $file ] || continue
  something-that-would-be-dangerous-if-$file-were-a-directory
done

x -o yesというディレクトリを作成すると 、その後、テストは
陽性になります。これは、私たちが評価している
条件式とはまったく異なるためです。

さらに悪いことに、 x -a a [0 $(uname>&2)] -gt 1というファイルを作成すると 、
少なくともすべてのksh実装( sh を含む) uname を実行するほとんどの商用Unicesおよび一部のBSDの) これらのシェルは、[
数値比較演算子で算術評価を実行するためです。 コマンド。

$ touch x 'x -a a[0$(uname>&2)] -gt 1'
$ ksh -c 'for f in *; do [ -f $f ]; done'
Linux

bashと同じ x -a -v a [0 $(uname>&2)]のようなファイル名の場合 。

もちろん、恣意的に実行できない場合は、攻撃者
より少ないダメージで解決するかもしれません(これは恣意的な実行を得るのに役立つかもしれません
)。ファイルを書き込んだり、権限や所有権を変更したり、主効果や副作用をもたらしたりする可能性のあるコマンドはすべて悪用される可能性があります。

あらゆる種類のことがファイル名で実行できます。

$ touch -- '-R ..'
$ for file in *; do [ -f "$file" ] && chmod +w $file; done

そして、あなたは ..を作ることになります 書き込み可能(GNU chmodで再帰的に 。

/ tmpのような公的に書き込み可能な領域でファイルの自動処理を行うスクリプト 非常に注意深く書かれるべきです。

[$#-gt 1]はどうですか

それは私が腹立たしいと思うものです。一部の人々は、
特定の拡張が、引用符を省略できるかどうかを判断するのに
問題があるのではないかと考えるのに苦労します。

言っているようなものです。 ねえ、 $#のようです
split+glob演算子の対象にはなりません。シェルにsplit+globを実行するように依頼しましょう

またはバグが発生する可能性が低いという理由だけで、間違ったコードを記述しましょう

今、それはどれほどありそうもないですか? OK、 $# (または $! $? または
算術置換)には数字(または-)のみを含めることができます
some²)ので、 glob 一部が出ています。 分割の場合
何かをする部分ですが、必要なのは $ IFSだけです。 数字を含める(または-

一部のシェルでは、 $ IFS 環境から継承される可能性がありますが、
環境が安全でない場合は、とにかくゲームオーバーです。

関連:グロブされたファイル名へのリダイレクトは失敗しますか?

ここで、次のような関数を作成すると、次のようになります。

my_function() {
  [ $# -eq 2 ] || return
  ...
}

つまり、関数の動作は、呼び出されるコンテキストに
依存するということです。つまり、 $ IFS それへの入力の1つになります。厳密に言えば、
関数のAPIドキュメントを作成するときは、
次のようになります。

# my_function
#   inputs:
#     $1: source directory
#     $2: destination directory
#   $IFS: used to split $#, expected not to contain digits...

また、関数を呼び出すコードでは、 $ IFSを確認する必要があります。
数字は含まれていません。
これらの2つの二重引用符の文字を入力する気がなかったからです。

さて、その [$#-eq 2] バグが脆弱になるため、
$ IFSの値にはどういうわけか必要です
攻撃者の管理下に置かれる 。おそらく、攻撃者が しない限り、通常は
発生しません。 なんとか別のバグを悪用することができました。

しかし、それは前代未聞ではありません。一般的なケースは、
算術式で使用する前にデータをサニタイズするのを忘れた場合です。

一部のシェルで任意のコードが実行される可能性があることはすでに説明しましたが、すべてのシェルで攻撃者が可能になります。 任意の変数に整数値を与えるため。

例:

n=$(($1 + 1))
if [ $# -gt 2 ]; then
  echo >&2 "Too many arguments"
  exit 1
fi

そして$1 (IFS =-1234567890) 、その算術評価
には、IFSと次の[の設定の副作用があります コマンドが失敗します。これは、引数が多すぎるのチェックを意味します
バイパスされます。

split + glob の場合はどうですか? 演算子は呼び出されませんか?

変数やその他の展開の前後に引用符が必要な別のケースがあります。それがパターンとして使用される場合です。

[[ $a = $b ]]   # a `ksh` construct also supported by `bash`
case $a in ($b) ...; esac

$ aかどうかをテストしないでください および$b 同じです( zshを除く )ただし、 $ aの場合 $ bのパターンと一致します 。そして、 $ bを引用する必要があります 文字列として比較する場合( "$ {a#$ b}"でも同じです または"${a%$ b}" または"${a ## * $ b *}" ここで、 $ b パターンと見なされない場合は、引用する必要があります。

つまり、 [[$ a =$ b]] $ aの場合にtrueを返す場合があります $ bとは異なります (たとえば、 $ aの場合 何でもです および$b *です )または、それらが同一である場合(たとえば、両方の $ a の場合)はfalseを返す場合があります および$b [a]です 。

それはセキュリティの脆弱性につながる可能性がありますか?はい、他のバグと同じように。ここで、攻撃者 スクリプトの論理コードフローを変更したり、スクリプトが行っている仮定を破ったりする可能性があります。たとえば、次のようなコードを使用します:

if [[ $1 = $2 ]]; then
   echo >&2 '$1 and $2 cannot be the same or damage will incur'
   exit 1
fi

攻撃者 '[a]''[a]'を渡すことでチェックをバイパスできます 。

さて、そのパターンマッチングも split + glob も、 オペレーターが適用します。変数を引用符で囲まずに残すことの危険性は何ですか?

私は自分が書いていることを認めなければなりません:

a=$b
case $a in...

そこでは、引用は害にはなりませんが、厳密には必要ではありません。

ただし、このような場合(たとえば、Q&Aの回答で)引用符を省略することの副作用の1つは、初心者に間違ったメッセージを送信する可能性があることです。変数を引用符で囲まなくても大丈夫かもしれません

たとえば、 a =$ b の場合、彼らは考え始めるかもしれません。 OKの場合、 export a =$ b 同様になります( export の引数にあるため、多くのシェルにはありません。 リストコンテキストでコマンドを実行)または env a =$ b

zshはどうですか ?

zsh それらの設計の厄介さのほとんどを修正しました。 zshで (少なくともsh / kshエミュレーションモードでない場合)分割が必要な場合 、または globbing 、またはパターンマッチング 、明示的にリクエストする必要があります: $ =var 分割し、 $〜var グロブまたは変数のコンテンツをパターンとして処理します。

ただし、分割(グロブではない)は、引用符で囲まれていないコマンド置換時に暗黙的に実行されます( echo $(cmd)のように)。 。

また、変数を引用しないことによる望ましくない副作用は、空の削除です。 。 zsh 動作は、グロビングを完全に無効にすることで他のシェルで達成できることと似ています( set -f を使用) )および分割( IFS ='' を使用) )。それでも、:

cmd $var

split + globはありません 、ただし $ varの場合 1つの空の引数cmdを受け取る代わりに、は空です 引数はまったくありません。

それはバグを引き起こす可能性があります(明らかな [-n $ var] のように )。これにより、スクリプトの期待や仮定が破られ、脆弱性が発生する可能性があります。

空の変数により、引数が削除される可能性があるため 、つまり、次の引数が間違ったコンテキストで解釈される可能性があることを意味します。

例として、

printf '[%d] <%s>n' 1 $attacker_supplied1 2 $attacker_supplied2

$ Attacker_supplied1の場合 空の場合、 $ Attacker_supplied2 算術式として解釈されます(%d の場合) )文字列の代わりに(%s の場合 )および算術式で使用されるサニタイズされていないデータは、zshなどのKornのようなシェルでのコマンドインジェクションの脆弱性です。

$ attacker_supplied1='x y' attacker_supplied2='*'
$ printf '[%d] <%s>n' 1 $attacker_supplied1 2 $attacker_supplied2
[1] <x y>
[2] <*>

結構ですが:

$ attacker_supplied1='' attacker_supplied2='psvar[$(uname>&2)0]'
$ printf '[%d] <%s>n' 1 $attacker_supplied1 2 $attacker_supplied2
Linux
[1] <2>
[0] <>

uname 任意のコマンド 実行されました。

行うときはどうですか split + globが必要です オペレーター?

はい、それは通常、変数を引用符で囲まないままにしておきたい場合です。ただし、分割を確実に調整する必要があります。 およびglob それを使用する前に正しく演算子。 分割のみが必要な場合 グロブではなく一部 一部(ほとんどの場合)、グロビングを無効にする必要があります( set -o noglob / set -f )そして $ IFSを修正します 。そうしないと、脆弱性も発生します(前述のDavid KornのCGIの例のように)。

結論

要するに、シェルで変数(またはコマンド置換または
算術展開)を引用符で囲まずに残すことは非常に危険です
特に間違ったコンテキストで行われた場合、
どれがそれらの間違ったコンテキスト。

これが、悪い習慣と見なされる理由の1つです。 。

これまで読んでくれてありがとう。頭を越えても
心配しないでください。誰もが
コードを書く方法でコードを書くことのすべての意味を理解することを期待することはできません。そのため、グッドプラクティスの推奨事項があります 、したがって、
必ずしも理由を理解していなくてもフォローできます。

関連:別の列からの情報を考慮して、列の値の平均を計算するにはどうすればよいですか?

(それがまだ明らかでない場合は、
セキュリティに敏感なコードをシェルで記述しないでください)。

そして、このサイトの回答に変数を引用してください!


Linux
  1. どのBashが実行されていますか?

  2. シェル - 変数の内容をファイルに書き込む

  3. スクリプト内の bash シェル スクリプトおよび関数の変数スコープ

  1. Bash動的(変数)変数名?

  2. シェル変数の関数?

  3. [ :シェル プログラミングの予期しない演算子

  1. bash の変数を持つエイリアス

  2. 方法:無制限の Bash/シェル履歴?

  3. PATH環境変数で相対パスを使用することのセキュリティへの影響は?