ふるつき

私は素直に思ったことを書いてるけど、上から目線だって言われる

Peachによるファジングをやる

以前の投稿で書きましたが、さる日、セキュリティミニキャンプ沖縄に参加しました。そこでの講義の一つがファジング実践でした。 それを改めてやったので記事にします。内容はミニキャンプでやったのほとんどそのままです。この記事で理解できたらすごいお得。沖縄まで飛ばなくて良いので。

やったこと。

Peachというファジングツールを使って、lighttpdというサーバアプリケーションを殴りました。v1.4.31にはある種のリクエストを食らうとハングアップ(?)する脆弱性(?)があって、そいつをファジングでみつけてやろうということです。  因みに、lighttpdの現在のバージョンは1.4.38で、今回の記事で発見する脆弱性は修正済みです。  ファジングで未知の脆弱性を見つけた時はブログに書く前に報告しましょう。IPAとかでいいのかしら。

ファジングとは

 ファジングとはソフトウェアテストの一種です。fuzzという適当な、エラーを起こしそうなデータを投げまくってソフトウェアが爆発しないかを調べます。おわり

peachとは

 フリーでクロスプラットフォームなファジングツールです。pitファイルというxmlを書いて上げるといろんなソフトウェアをテストできます。今回はサーバアプリケーションということでhttpリクエストを投げるようなpitファイルを書きましたが、画像を読ませたりいろいろデキるっぽいです。やってみたい。  安定して使える、そして情報があるのはv2.3系です。が、私は新しい物が好きですし今更2.3の記事書いてもしかたがないので3系を使いました。より正確には3.1.124.0です。

lighttpdとは

 軽量なサーバです。apacheとかの仲間です。詳しくは知りませんが軽量なのが良いと思います。

環境

 仮想マシンの中でやりました。VirtualBox仮想マシンを2つ立てて、その中でやった。もうちょっと詳しく書きます。

サーバ側 xubuntu

Linux x-ubuntu 3.19.0-42-generic #48~14.04.1-Ubuntu SMP Fri Dec 18 10:24:49 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

です。xubuntuの14.04落としてきてそのままインストールしました。lighttpd入れ程度でカスタマイズとかはしてないです。

lighttpdの導入

http://download.lighttpd.net/lighttpd/releases-1.4.x/から、1.4.31のアーカイブを落としてきます。そして伸張(圧縮の対義語として試験的に使ってます)してconfigure make make installで終わり。依存とか結構あるので、一度aptから最新のをインストールして依存を潰したあと、lighttpdだけアンインストールするのもおすすめです。

[Optional]lighttpdの設定

 lighttpdの設定が気に食わなかったので設定ファイルを少し編集します。/etc/lighttpd/lighttpd.confの適当な場所に

mimetype.assign = (".html" => "text/html")

と書いておきましょう

lighttpdの起動。

lighttpd -f /etc/lighttpd/lighttpd.confをどうぞ。sudoいるかも。

peach側 kali linux2

Linux kali 4.0.0-kali1-686-pae #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) i686 GNU/Linux

 です。iso落としてインストールしただけだと思うなぁ。

peachの導入

http://community.peachfuzzer.com/v3/Installation.html あたりから適当にダウンロードして伸張して終わり。linuxだったのでapt install mono-completeだけしました。

virtualboxの設定

 仮想マシン同士は、Host-Only networkでつなぎました。それぞれ中で静的にIPを取得してる(???)。

pitファイルを書く。

 peachでファジングするときはどういうデータを投げるのかということを教えないといけないのでpitファイルを書きます。最終的には↓になりました。

<?xml version="1.0" encoding="utf-8"?>
<Peach xmlns="http://peachfuzzer.com/2012/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://peachfuzzer.com/2012/Peach /peach/peach.xsd">
    <DataModel name="HeaderField">
        <String name="FieldName" />
        <String value=": " mutable="false" />
        <String name="FieldValue" />
        <String value="\r\n" mutable="false" />
    </DataModel>
    <DataModel name="HttpRequest">
        <Block name="RequestLine">
            <String name="Method" value="GET" />
            <String value=" " mutable="false"/>
            <String name="RequestUri" value="/" />
            <String value=" " mutable="false"/>
            <String name="HttpVersion" value="HTTP/1.1" />
            <String value="\r\n" mutable="false" />
        </Block>
        <Block name="HeaderHost" ref="HeaderField">
            <String name="FieldName" value="Host" mutable="false" />
            <String name="FieldValue" value="192.168.1.1" />
        </Block>
        <Block name="UserAgent" ref="HeaderField">
            <String name="FieldName" value="User-Agent" mutable="false" />
            <String name="FieldValue" value="Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.4.0" />
        </Block>
        <Block name="Accept" ref="HeaderField">
            <String name="FieldName" value="Accept" mutable="false" />
            <String name="FieldValue" value="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" />
        </Block>
        <Block name="AcceptLanguage" ref="HeaderField">
            <String name="FieldName" value="Accept-Language" mutable="false" />
            <String name="FieldValue" value="en-US,en;q=0.5" />
        </Block>
        <Block name="AcceptEncoding" ref="HeaderField">
            <String name="FieldName" value="Accept-Encoding" mutable="false" />
            <String name="FieldValue" value="gzip, deflate" />
        </Block>
        <Block name="Connection" ref="HeaderField">
            <String name="FieldName" value="Connection" mutable="false" />
            <String name="FieldValue" value="keep-alive" />
        </Block>
        <Block name="ContentLEngth" ref="HeaderField">
            <String name="FieldName" value="Content-Length" mutable="false" />
            <String name="FieldValue">
                <Relation type="size" of="Content" />
            </String>
        </Block>
        <String value="\r\n" mutable="false" />

        <Blob name="Content" value="" />
    </DataModel>
    
    <StateModel name="HttpRequestStateModel" initialState="HttpRequestState">
        <State name="HttpRequestState">
            <Action type="output">
                <DataModel ref="HttpRequest" />
            </Action>
        </State>
    </StateModel>

    <Test name="Default">
        <StateModel ref="HttpRequestStateModel" />
        <Publisher class="TcpClient">
            <Param name="Port" value="80" />
            <Param name="Host" value="192.168.168.10" />
        </Publisher>
    </Test>

</Peach>

 もうちょっと順を追って、ハンズオン的に作っていきます。記事長くなりそうで嫌だ。

一番外側

<?xml version="1.0" encoding="utf-8"?>
<Peach xmlns="http://peachfuzzer.com/2012/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://peachfuzzer.com/2012/Peach /peach/peach.xsd">
</Peach>

おまじないです。サンプルからコピーしてきただけです。encodingだけ環境に応じて変えましょう。

DataModelを作る。

 DataModelというのは、データ構造のモデルです。うわぁそのままだ。どういう規則にしたがってデータが並んでいるかとかそういうのを書きます。詳しいことは書きながら理解。

まずは汎用のモデルを作ります。

 <DataModel name="HeaderField">
        <String name="FieldName" />
        <String value=": " mutable="false" />
        <String name="FieldValue" />
        <String value="\r\n" mutable="false" />
    </DataModel>

httpのヘッダって、先頭行を覗いて、HOGEHOGE: PIYOPIYO\r\nとなってますよね。それを表しています。Stringと言うのは文字列データを表します。数値ならNumberになりますね。mutable="false"は結構大事で、Peachに「こいつは変更するなよ」といってます。これを指定しないとPeachが気を利かせて、適当なfuzzに置き換えてくれたりします。

↑の雛形を使って実際のhttpヘッダになるDataModelを作ります。

<DataModel name="HttpRequest">
        <Block name="RequestLine">
            <String name="Method" value="GET" />
            <String value=" " mutable="false"/>
            <String name="RequestUri" value="/" />
            <String value=" " mutable="false"/>
            <String name="HttpVersion" value="HTTP/1.1" />
            <String value="\r\n" mutable="false" />
        </Block>
        <Block name="HeaderHost" ref="HeaderField">
            <String name="FieldName" value="Host" mutable="false" />
            <String name="FieldValue" value="192.168.1.1" />
        </Block>
        <String value="\r\n" mutable="false" />
</DataModel>

BlockはDataModelの子要素でいわゆる

です。最初の行は先程の雛形にあわないのでベタ書きしました。それに対してHeaderHost(Host: hogehoge)は雛形に合うので、雛形を使います。その指定がrefですね。そして、雛形のnameに合わせて値を設定します。192.168.1.1は適当です。

 こうやってHTTPリクエストにあるヘッダを作っていきます。とりあえずこの2つと最後の改行だけあればHTTP/1.1のリクエストになるので、他のは勝手に作ってもらって、次に行きます。

StateModelを作る

 正直StateModelあんまりわかってないです。どうやって対象にfuzzデータを送るかみたいなところだと思います。

 <StateModel name="HttpRequestStateModel" initialState="HttpRequestState">
        <State name="HttpRequestState">
            <Action type="output">
                <DataModel ref="HttpRequest" />
            </Action>
        </State>
    </StateModel>

StateModelのinitialStateはStateタグのnameと合わせて下さい。Actionにはoutput以外にもinputとかあります。inputどうやって使うんだろうって感じです。この辺もなんかほらあれです、あれ。

Testタグかく

 実際にfuzz送るところです。

 <Test name="Default">
        <StateModel ref="HttpRequestStateModel" />
        <Publisher class="TcpClient">
            <Param name="Port" value="80" />
            <Param name="Host" value="192.168.168.10" />
        </Publisher>
    </Test>

  使うStateModelを指定します。それから、どうやってデータを送るかを指定します。今回はTcpでサーバに送るのでTcpClientです。HttpというのもあったのですがデキるだけHttpの制約に縛られずに殴りたかったのでTcpです。  Testタグの名前をDefaultにしておくとデフォルトでこのテストが実行されます。

Blobについて

 blobはよくわからないけれど、適当なデータ突っ込んでくれる気がしてます。

Relationについて

 他のタグに依存した関数程度に思っていれば

やってみる

./peach <hoge>.xmlをやりましょう。だーっとログが流れてファジングしてるのが分かります。WireSharkとかで見てると楽しいです。

 で、ある種のpitファイルを書いていると、急にログの流れが遅くなったりします。これはデータが、「刺さった」状態で、サーバが苦しんでいます。C-cでpeachとめて、サーバ側を見てみましょう。私のところだとこんな感じです。

 CPUの使用率が100%になっています。この時はlighttpdさんは応答してくれません。とりあえずlighttpd止めておきましょう。

 Peach側に戻って、どのあたりで応答が遅くなったか見ます。番号を振ってくれているので見分けられますね。なんどか試行してどのデータが刺さったのかを突き止めます。大事なのは「どのへんで遅くなったか」です。それから、ログの一番最初にいる、RandomSeedをおぼえておきましょう。

 peachでは、--range N,Mというオプションで、N~M-1の範囲のテストデータを投げられます。--seed NでランダムシードをNに指定してfuzzを生成できます。これら2つを使って、ささったデータを探して下さい。私のところではこんな感じでした。

GET / HTTP/1.1

User-Agent: Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.4.0

Host: 192.168.1.1

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Connection: ;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,;,

Content-Length: 0

Content-Length: 0

Content-Length: 0

 ...

Content-Lengthは何行もあったので省略しました。この感じだと怪しいのはConnectionとContent-Lengthですよね。これらをそれぞれ試してみましょう。こういう時はcurlの-Hオプションが便利です(好きなヘッダをつけられる)。

 curl -H "Connection: ;,;,;,;;,;,;;,;" 192.168.168.10とかします。あるいは-H "Content-Length: 0" -H "Content-Length: 0" ...とかします。すると、刺さるのは前者のほうだと分かります。

 更にこいつを詰めていって、どういう条件で刺さるのかを調べます。冗長なので省いていろいろやったらConnection: ,としたら刺さることを報告しておきます。

 ということでlighttpdの1.4.31ではConnectionに,があると刺さるなあとか分かりました。

 最後の方駆け足で適当で申し訳ないです

追記

 本当はここからlighttpdのソースのどこが悪くてこう治す、ってところまでやるべきですけどそこまでの力がなかった。だいたいrequest.cが怪しいかな~くらいしか見てないです