ファジングスタイル1989

2019年の始まりから、過去を思い出して未来を考えるのは良いことです。 30年を振り返り、ファジングに関する最初の科学記事「UNIXユーティリティの信頼性に関する実証的研究」と、その後の同じ著者Barton Millerに よる 1995年の研究「Revision of Fuzzing」を振り返りましょう。



この記事では、オリジナルのファジング作業と同じツールを使用して Ubuntu Linuxの最新バージョンのバグを見つけようとします。 コンテキストだけでなく、理解のためにも元のドキュメントを読む必要があります。 彼らは、今後数十年間、脆弱性とエクスプロイトに関して非常に予言的であることが判明しました。 注意深い読者は、元の記事の発行日:1990年に気付くかもしれません。 さらに注意を払うと、ソースのコメントに著作権が表示されます:1989



短いレビュー



ドキュメントを読んでいない人のために(これは実際に行われるべきですが)、このセクションには簡単な要約といくつかの選択された引用が含まれています。



ファジングプログラムは、印刷された文字または印刷されていない文字のみを生成する機能を使用して、ランダムな文字ストリームを生成します。 特定の初期値(シード)を使用して、再現可能な結果を​​提供しますが、これは現代のファザーには欠けています。 テストされたプログラムで一連のスクリプトが実行され、基本的なダンプの存在が確認されます。 ハングは手動で検出されます。 アダプターは、対話型プログラム(1990年の記事)、ネットワークサービス(1995)、およびグラフィカルXアプリケーション(1995)にランダムな入力を提供します。



1990年の記事では、4つのプロセッサアーキテクチャ(i386、CVAX、Sparc、68020)と5つのオペレーティングシステム(4.3 BSD、SunOS、AIX、Xenix、Dynix)をテストしました。 1995年の記事で、プラットフォームの同様の選択。 最初の記事では、プラットフォームに応じて、ユーティリティの25〜33%が失敗します。 後続の記事では、これらの数値の範囲は9%から33%であり、GNU(SunOS上)およびLinuxがクラッシュの割合を最も低くしています。



1990年の記事では、1)プログラマは配列の境界やエラーコードをチェックしない、2)マクロはコードの読み取りとデバッグを困難にし、3)C言語は非常に安全ではないと結論付けました。 非常に安全でgets



ないgets



関数とC型システムが特に言及されており、テスト中に、著者は大量利用される何年も前にFormat Stringの脆弱性を発見しました。 この記事の最後に、ユーザーがバグを修正または報告する頻度についての調査を行います。 バグの報告は難しく、バグを修正することにほとんど関心がなかったことが判明しました。



1995年の記事では、オープンソースソフトウェアについて言及し、エラーが少ない理由について説明しています。 引用:



障害の原因を調査すると、不安な現象が現れました。1990年に報告されたバグの多く(約40%)は、1995年にそのままの形で残っています。 ...



ここで使用する方法はシンプルで、ほとんどが自動化されています。 開発者が信頼性を高めるためにこの簡単で無料のソースを使用しない理由を理解するのは困難です。


15〜20年後になって初めて、ファジング技術は大規模ベンダーの標準的なプラクティスになります。



また、この1990年の声明は将来の出来事を予見しているように思えます。



多くの場合、プログラミングCの簡潔なスタイルは極端なものであり、形式は正しい関数よりも優先されます。 入力バッファのオーバーフローの可能性は、 最近のインターネットワームが示したように、潜在的なセキュリティホールです


試験方法



30年後の幸いなことに、バートン博士は、彼の発見を再現するための完全なソースコード、スクリプト、およびデータを提供しています。これは、他の研究者が従うべき立派な例です。 スクリプトは問題なく機能し、ファジングツールはコンパイルと実行にわずかな変更のみを必要としました。



これらのテストでは、 テストされたアプリケーションの最新リストがあるため、スクリプトとfuzz-1995-basicリポジトリからの入力を使用しましたREADMEによると、元の調査と同じランダム入力があります。 以下の最新のLinuxの結果は、元の記事とまったく同じファジングコードと入力データで取得されています。 テスト用のユーティリティのリストのみが変更されています。



30年にわたるユーティリティの変更



明らかに、過去30年間にLinuxソフトウェアパッケージにいくつかの変更がありましたが、かなりの数の実績のあるユーティリティが数十年にわたってその血統を続けてきました。 可能な場合は、1995年の記事から同じプログラムの最新バージョンを取りました。 一部のプログラムは使用できなくなったため、置き換えました。 すべての代替品の正当化:





結果



1989年のファジング手法では、2018年にもエラーが見つかります。 しかし、いくつかの進歩があります。



進捗を測定するには、何らかの基礎が必要です。 幸いなことに、このようなフレームワークはLinuxユーティリティ用に存在します。 Linuxは1990年の元の記事の時点では存在していませんでしたが、1995年の2回目のテストでは、1995 Slackware 2.1.0ディストリビューションのユーティリティで同じファジングコードを起動しました。 関連する結果は、1995年の記事(p。7-9)の表3に記載されています 。 競合他社と比較して、GNU / Linuxは非常に見栄えがよくなります。



無料のLinuxバージョンのUNIXでのユーティリティエラーの割合は2番目に高く、9%でした。


それでは、1995年と2018年のLinuxユーティリティを1989年のファジングツールと比較しましょう。



Ubuntu 18.10(2018) Ubuntu 18.04(2018) Ubuntu 16.04(2016) Ubuntu 14.04(2014) Slackware 2.1.0(1995)
クラッシュ 1(f77) 1(f77) 2(f77、ul) 2(swipl、f77) 4(ul、flex、indent、gdb)
フリーズ 1(スペル) 1(スペル) 1(スペル) 2(スペル、ユニット) 1(ctags)
合計テスト済み 81 81 81 81 55
失敗/フリーズ、% 2% 2% 4% 5% 9%


驚くべきことに、Linuxのクラッシュとフリーズの数は、Ubuntuの最新バージョンであってもゼロよりも大きいままです。 そのため、 f77



はセグメンテーションエラーでf2c



プログラムを呼び出し、 spell



プログラムはテスト入力の2つのバージョンでハングします。



どんなバグ?



いくつかのバグの根本原因を手動で把握することができました。 glibcのエラーなどの一部の結果は予想外でしたが、固定バッファーを使用したsprintfなどの他の結果は予測可能でした。



Ulの障害



ulのバグは、実際にはglibcのバグです。 特に、2016年にここここul



見つかった別の人)で報告さました。 バグトラッカーによると、エラーはまだ修正されていません。 このバグはUbuntu 18.04以降では再現できないため、ディストリビューションレベルで修正されています。 バグトラッカーのコメントから判断すると、主な問題は非常に深刻な場合があります。



クラッシュf77



f77



プログラムは、Fort77パッケージに含まれています。これは、Fortran77からCへのソーストランスレーターであるf2c



シェルスクリプトです。f2cをデバッグすると、 errstr



関数が長すぎるエラーメッセージを出力するとエラーが発生します。 f2cソースコードは、sprintf関数を使用して可変長文字列を固定サイズバッファに書き込むことを示しています。



 errstr(const char *s, const char *t) #endif { char buff[100]; sprintf(buff, s, t); err(buff); }
      
      





このコードはf2c



の作成以降保持されているようです。 このプログラムは、少なくとも1989年以降の変更履歴を保持しています。 1995年に、再ファジングを行ったとき、Fortran77コンパイラーはテストされていませんでした。そうでなければ、問題は以前に発見されていたはずです。



フリーズスペル



古典的なデッドロックの素晴らしい例。 spell



ispell



、パイプを介しispell



委任します。 spell



は、行ispell



テキストspell



読み取り、 ispell



の行のサイズのブロッキングレコードを生成します。 ただし、 ispell



BUFSIZ/2



最大BUFSIZ/2



バイト(システムでは4096バイト)を読み取り、ブロッキングレコードを発行して、クライアントがこれまでに処理された検証データを受信したことを確認します。 2つの異なるテスト入力により、 spell



ispell



に4096文字を超える文字列を書き込むことを強制され、デッドロックが発生しました: spell



ispell



文字列全体ispell



読み取るのを待ち、 ispell



spell



が元のスペル修正を読み取ったことspell



確認するのを待ちます。



凍結ユニット



一見、無限ループ状態があるようです。 ハングは、 units



ではなくlibreadline



ようです。ただし、 units



新しいバージョンではこのエラーは発生しません。 変更ログは、この問題を誤って修正する可能性のある入力フィルタリングが追加されたことを示しています。 ただし、理由の徹底的な調査は、このブログの範囲外です。 おそらく、 libreadline



を掛けるlibreadline



まだそこにあります。



Swiplクラッシュ



完全をswipl



ために、 swipl



失敗について言及したいと思いますが、バグは長い間修正されており、かなり高品質であると思われるため、徹底的に調査しませんでした。 失敗は、実際には、文字が変換されるときに呼び出されるステートメント(つまり、決して発生してはならないこと)です。



[Thread 1] pl-fli.c:2495: codeToAtom: Assertion failed: chrcode >= 0

C-stack trace labeled "crash":

[0] __assert_fail+0x41

[1] PL_put_term+0x18e

[2] PL_unify_text+0x1c4









クラッシュは常に悪いものですが、少なくともここでは、プログラムはエラーを報告でき、早期に大声でクラッシュします。



おわりに



過去30年間、ファジングはバグを見つけるためのシンプルで信頼できる方法でした。 この分野は活発な研究が進行中ですが、30年前のファザーでさえ、最新のLinuxユーティリティのエラーを見つけることに成功しています。



元の記事の著者は、Cが今後数十年で引き起こすセキュリティ問題を予測しました。 彼は、安全でないコードはCで書くのは簡単すぎて、可能であれば避けるべきだと説得力をもって主張します。 特に、この記事は、最も単純な段階でさえバグが現れることを示しており、そのようなテストは標準的なソフトウェア開発のプラクティスに含まれるべきです。 残念ながら、このアドバイスは何十年も守​​られていません。



この30年間の回顧展をお楽しみください。 次の2000年のFuzzingの記事を待ちましょう 。ここでは、fuzzerでテストしたとき、Windows 10アプリケーションとWindows NT / 2000の同等のアプリケーションとの堅牢性を比較します 。 答えは予測できると思います。



All Articles