この記事では、オリジナルのファジング作業と同じツールを使用して、 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年の記事から同じプログラムの最新バージョンを取りました。 一部のプログラムは使用できなくなったため、置き換えました。 すべての代替品の正当化:
-
cc1
:1995年の記事のCプリプロセッサに相当。 -
gdb
:1995デバッガーに相当。 -
ditroff
:ditroff
使用できなくなりました。 -
dtbl
:古いdtbl
ユーティリティのGNU Troffと同等です。 -
clisp
:lisp
の標準実装。 -
more
⇨less:少ないほど多い! -
prolog
swipl
:プロローグには、SWIプロローグとGNUプロローグの2つのオプションがあります。 SWI Prologは、より古く、より完全な実装であるため、望ましいです。 -
gawk
:awk
GNUバージョン。 -
gcc
:標準のCコンパイラ。 -
compress
:GZipは、古いUnixcompress
ユーティリティの概念上の子孫です。 -
splint
:GPLの下でlint
ました。 -
/bin/mail
mail⇨/usr/bin/mail
:別の方法での同等のユーティリティ。 -
fort77
:Fortan77コンパイラには、GNU FortranとFort77の2つのバリエーションがあります。 1つ目はFortran 90に推奨され、2つ目はFortran77サポートに推奨されます。f2c
プログラムf2c
積極的にサポートされており、その変更リストは1989年以来維持されています。
結果
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の同等のアプリケーションとの堅牢性を比較します 。 答えは予測できると思います。