web-dev-qa-db-ja.com

「!xxx%s%s%s%s%s%s%s%s」の何が特別なのですか?

私は nix-Haters Handbook にリンクされていて、つまずいた(149ページ):

件名:関連するUnixのバグ

1991年10月11日

W4115xの仲間の学生—

アクティベーションレコード、引数の受け渡し、呼び出し規約について説明しているときに、次のように入力することをご存知でしたか。

!xxx%s%s%s%s%s%s%s%s

cシェルに接続すると、すぐにクラッシュしますか?なぜなのかご存知ですか?

考えるべき質問:

  • !xxxと入力すると、シェルは何をしますか?
  • !xxx%s%s%s%s%s%s%s%sと入力すると、入力に対して何をする必要がありますか?
  • なぜこれがシェルをクラッシュさせるのですか?
  • この問題が発生しないように、シェルの問題のある部分を(かなり簡単に)どのように書き直すことができますか?

純粋に好奇心から、誰かが問題が何であったかを説明できますか?当然のことながら、Googleで文字列を検索しても役に立ちません。メッセージから他の引用を検索すると、メッセージの他のコピーが表示されただけで、説明はありませんでした。

4
Alexey Romanov

25年前の貝殻の出所を掘り下げる気はしませんが

フォーマット文字列の脆弱性 である可能性があります。

シェルに次のようなコードが含まれている場合

printf(str);

ここで、strはユーザーの入力から取得した文字列であり、文字列の内容はprintfが使用するフォーマット文字列になります。 %sは、引数が指す文字列を出力するようにprintfに指示します。引数が指定されていない場合(上記のように、フォーマット文字列のみがあります)、関数はスタックから他のデータを読み取り、それらをポインターとして追跡します。おそらく、マップされていないメモリにアクセスし、プロセスをクラッシュさせます。

ある意味、メッセージの言い回しもこのような解決策を示唆していると思います。 !xxxと入力すると、シェルが目に見える形で!xxx: event not foundのようなエラーメッセージを出力します。そこから、!xxx%s%s%s%s%s%s%s%s: event not foundも印刷しようとすることは大きな飛躍ではなく、フォーマット文字列の脆弱性が関係しています。


持ってはいけませんが、ソースを覗いてみました ここ4.3BSD-Tahoe/usr/src/bin/csh、日付は1988年のものです)。

findev(cp, anyarg) in sh.Lex.c 一致する履歴イベントを見つける関数である可能性があります:Histlistと呼ばれるstruct Histのリンクリストをウォークスルーします。何も見つからない場合は、seterr2(cp, ": Event not found");からnoev()を呼び出します。ここでのcpは、履歴で検索されている文字列のようです。

seterr2()は、変数errを引数の連結として設定し、errは、 sh.cif (err) error(err);のいくつかの場所でprocess()として使用されます。最後に、error()sh.err.c )には、古典的なフォーマット文字列の脆弱性が含まれています:if (s) printf(s, arg), printf(".\n");

他のいくつかの場所では、error()error("Unknown user: %s", gpath + 1);のような引数で呼び出されるため、error()の最初の引数はフォーマット文字列である可能性があることは明らかです。

履歴置換機能を完全に理解したと言ったら正直ではありません。 Cでのコメントなしの手動文字列処理です。%は履歴置換で特別な意味を持ちますが、最初の文字として(!%のように)、またはfindev()が呼び出された後にのみ特別に処理されることがわかります。

7
ilkkachu