
私はリンクされていましたUnix 嫌いのためのハンドブックそして偶然見つけた(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 で検索しても役に立ちません。メッセージの他の引用を検索しても、メッセージの他のコピーは見つかりましたが、説明はありませんでした。
答え1
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)
でsh.lex.c
これは、一致する履歴イベントを見つける関数のようです。 とstruct Hist
呼ばれるリンク リストを調べHistlist
ます。何も見つからない場合は、 を呼び出しますseterr2(cp, ": Event not found");
。noev()
ここcp
では、履歴で検索されている文字列のようです。
seterr2()
変数をerr
引数の連結として設定し、のいくつかの場所でのerr
ように使用されます。if (err) error(err);
process()
sh.c
最後に、error()
(sh.err.c
) には、典型的なフォーマット文字列の脆弱性が含まれています。if (s) printf(s, arg), printf(".\n");
他の場所では、error()
は のように引数とともに呼び出されるため、 の最初の引数は書式文字列である可能性があるerror("Unknown user: %s", gpath + 1);
という考え方であることは明らかです。error()
履歴置換関数を完全に理解したと言えば正直ではないでしょう。これは、C でのコメントなしの手動文字列処理とほぼ同じです。は履歴置換において特別な意味を持ちますが、最初の文字 ( のように) または が呼び出された後に%
特別に処理されるだけであると考えられます。!%
findev()