FFRIエンジニアブログ

FFRIエンジニアが執筆する技術者向けブログです

インターンシップ報告 1

ご挨拶

FFRI インターンシップ生の森です。この度インターンシップ参加の報告記事をエンジニアブログの記事として執筆することになりました。宜しくお願いします。この記事では、自分がインターンシップで行った作業や学んだことを報告していきます。

応募動機

FFRIにインターン応募しようと思った動機は、以前からFFRIのことがとても気になっていたからです。

自分は低レイヤセキュリティに関して興味が強く、将来それに関連するような職に就きたい思っていました。以前、日本の企業のリサーチを行ったときに、日本で自分が興味を持って働けそうな企業の候補として挙がったのが、ここFFRIでした。FFRIは今年、インターンシップ生を募集しており、業務内容も自分の興味のある分野であったため、早速応募しこのように今業務に取り組んでいる最中です。

事前課題

インターンシップに参加が決定し、しばらくすると事前課題が課されました。内容としてはVC++で、コマンドプロンプトから叩く一部のコマンドの再現などを行いました。難易度としては自分としては中々やりごたえが有り、一部非公開の関数なども使用するものもありました。普段あまりプログラミングをしないのにも関わらず、夜更かししてまで取り組みました。

一通り課題が終わると、課題の指摘が行われました。学校の課題程度では指摘されなかった細かい点まで指摘され、特にcallocなどの動的確保関数の戻り値チェックなどは大量に修正しました。普段戻り値のエラーチェックなどは行わないのですが、これが原因で上手く動いていない箇所もあったため非常に勉強になりました。

第一週

インターンシップは自分が本社から離れた場所に住んでいるため、Slackを利用したリモートで作業を行いました。

最初の一週間はAGLというLinuxディストリビューションのセットアップ及び、AGLのデフォルトのセキュリティ設定に関する調査を行いました。AGLについてはDebianやCentOSなどに比べると、あまり聞いたことがないかもしれませんが、車載OS向けのディストリビューションです。日本の大手自動車メーカーも開発に携わっています。AGLのセットアップではRaspberry Piというシングルボードコンピュータにインストールする作業と、クロスコンパイル環境構築作業を行いました。

AGLの調査では、以下のようなことを調べました。

  • どのようなプログラムが、起動して通信しているか
  • カーネルパラメータ
  • セキュリティ機構は有効か
  • 標準コマンドのセキュリティコンパイラオプションは有効か
  • SMACK

調査対象機器・OSは次のとおりです。

  • Raspberry Pi 3 Model B
  • AGL 5.1.0

調査結果

調査結果の一部を示します。

どのようなプログラムが、起動して通信しているか

netstat -antでオープンしているポートを調査し、lsof -i4TCP:<ポート番号>からPIDを調べ、ls -l /proc/<PID>/exeで実行ファイルのパスを調査しました。その結果、/usr/bin/afb-daemonというプログラムが11個ものポートも開放していることが分かりました。afb-daemonはBinderという、アプリケーションが必要とするサービスに接続するシステムのデーモンであり怪しいものではありません。

カーネルパラメータ・セキュリティ機構は有効か

/proc/sys/kernelの各値を調査しました。以下のような結果であれば、ASLRが有効化されているなどといったことをまとめていきました。

raspberrypi3:/proc/sys/kernel$ cat randomize_va_space
2

標準コマンドのセキュリティコンパイラオプションは有効か

checksec.shでコンパイラオプションを調査しました。調査対象は/bin , /usr/bin , /sbinです。

/usr/binのバイナリにSTACK CANARY無効やPIE無効なものがたくさんあると分かり、脆弱性が見つかった際、権限昇格を比較的簡単に起こせてしまうので、少し危険だと感じました。

SMACK

SMACKとはAGLで使用される、強制アクセス制御のシステムです。有名なものではSELinuxやAppArmorがあります。

SMACKの設定自体は、ラベル同士がどのようにアクセス権を持っているかをテキストファイルに書いて読み込ませるだけなので簡単です。SMACKのデフォルトの設定ファイルは/etc/smack/accesses.dに、作成した設定ファイルはsmackload < <ファイルパス>により有効化することが出来ます。例えば以下のような設定ファイルがあれば、PROCというラベルの付いたプロセスなどから、FILEというラベルのついたオブジェクトへの「読み書き実行」を許可します。

PROC FILE rwx

しかし標準では管理者権限であるrootがアクセス制御下に入っていないため、root権限で動作するプロセスから権限昇格があった時、その暴走を防げないというあまりセキュアでない面もあると分かりました。

第二週

脆弱性概要

二週目はConnManというネットワークマネージャの脆弱性に対するPoCを作成し、検証を行いました。

ConnManはネットワークの設定などに使用され、通常デーモンとして起動しています。しかし、バージョン1.4以前にはスタックバッファオーバーフロー脆弱性が存在します。これはConnManDoという名前付き脆弱性として、2017年にNRIセキュアテクノロジーズ株式会社から報告されています。

脆弱性が発生している箇所はget_name関数というDNSパケットの回答セクションのラベルを処理する部分にあります。DNSパケットはURLをラベルとして格納しています。具体的には「www.google.com」の問い合わせであれば、DNSパケットの問い合わせセクションには0x03www0x6google0x3com0x0といったように、ドットで区切られた文字とその文字数を先頭につけたものを格納しています。

回答セクションにも同様にラベルを格納できるのですが、同一のラベル名があるときは、DNSパケットからのオフセットを指定し、パケットの圧縮ができます。このパケットの圧縮を利用して大量のラベルを生み出すと、ConnManはそれを処理するget_name関数で、スタックバッファオーバーフローを起こしていました。

以下はパッチが当てられる前のget_name関数の一部です。if内が圧縮ラベルの処理で、elseがその他のラベル処理です。else内において、name配列に書き込む合計のラベルのバイト数を計算していないため、nameの要素数を越えた書き込みが行われる可能性があります。

while (*p) {
    if ((*p & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
        uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);

        if (offset >= max - pkt)
            return -ENOBUFS;

        if (!*end)
            *end = p + 2;

        return get_name(counter + 1, pkt, pkt + offset, max,
                output, output_max, output_len, end,
                name, name_len);
    } else {
        unsigned label_len = *p;

        if (pkt + label_len > max)
            return -ENOBUFS;

        if (*output_len > output_max)
            return -ENOBUFS;

        /*
            * We need the original name in order to check
            * if this answer is the correct one.
            */
        name[(*name_len)++] = label_len;
        memcpy(name + *name_len, p + 1,    label_len + 1);
        *name_len += label_len;

        /* We compress the result */
        output[0] = NS_CMPRSFLGS;
        output[1] = 0x0C;
        *output_len = 2;

        p += label_len + 1;

        if (!*end)
            *end = p;

        if (p >= max)
            return -ENOBUFS;
    }
}

つまりNRIセキュアテクノロジーズ株式会社の報告にもあるような、悪意のあるDNS応答を返すようなDNSサーバーに接続されると、DoSや権限昇格が起こりうるのです。

PoC

自分はCでスタックバッファオーバーフローさせるような応答を返す簡単なDNSサーバーを作成し、実際にバージョン1.33のConnManを落とせることを確認しました。同時に脆弱性が修正されたバージョン1.33-3+deb9u1に対しても同様のパケットを送り動作終了しないことを確認して、ConnManDoの脆弱性を突いていることを確認しました。

このPoCの作成自体はそこまで難しいものではありませんが、DNSパケットやネットワーク、systemdなど様々な知識を必要とするため、自分には少々難しく勉強になりました。

所感

まだインターン期間は続きますが、普段触れない技術や知識にふれる機会が有りとても刺激になっています。自分は IoT向け脆弱性攻撃検知・防御技術の研究開発というテーマで行っており、現在は攻撃まで行えましたので、検知・防御は残りのインターン期間の課題としていきたいと思います。インターン終了後も、ここで得た技術や知識を生かしていけるようなことを見つけていきたいと思います。

インターン終了時の追記

ConnManをDoSで落とせることを確認したため、次のステップとしてRCE(Remote Code Execution)に取り組みました。

セキュリティオプションの調査

前述したchecksec.shを使用して、ConnManのセキュリティオプションを調査すると以下のような結果になりました。

root@raspberrypi3:~/bin# ./checksec.sh --dir /usr/sbin|grep connman
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   /usr/sbin/connmand

主要なオプションの説明をします。また以下に挙げるオプション以外にも、ASLRが有効になっています。

FULL RELRO

GOTというセクションへの書き込みを禁止する機能です。GOTにはglibcで定義されるような関数のアドレスが保存されています。
ここを書き換えて、呼び出される関数を変更するGOT OVERWRITEという攻撃手法がありますが、使用できません。

Canary found

リターンアドレスの書き換えを検知する機能です。関数が呼ばれると、最初にスタックへ引数やリターンアドレスがpushされ、次にローカル変数が確保されます。
このような構造のため、ローカル変数がオーバーフローが可能な時、リターンアドレスの書き換えが可能です。
リターンアドレスの上書きを防ぐため、ローカル変数とリターンアドレスの間にCanaryと呼ばれるランダムな値をpushし、関数がリターンされるときに、その値が書き換わっていないか確認します。
単純にリターンアドレスの書き換えでプログラムカウンタを変更することが出来ません。

NX enabled

プログラムが実行できる箇所を制限する機能です。スタック上に不正なコードを配置し、実行すると言った攻撃方法ができません。

PIE enabled

プログラム全体のアドレスをランダム化します。ASLRではランダム化されない、textセクションまでもがランダム化されます。
ROPのような命令アドレスを直接使用する攻撃が、単純には行えなくなります。

ConnManの内部調査

上記のようにセキュリティが分厚いですが、まずはじめにリターンアドレスの位置を特定する作業からはじめました。作業内容としては、ConnManの逆アセンブルとコードを読みながら、GDBでデバッグしていきました。
調査した結果、GDBからCanaryをこちらから強制的に書き換えることで、リターンアドレスを変更することは出来ましたが、実際の攻撃ではCanaryのリークができなかったことと、パケットから攻撃する手法であるためリークさせた情報を読み取ることが難しく、Canaryを突破することはできませんでした。
escar Asia 2017ではConnManDoの脆弱性を突いたRCEのデモが公開されているため、何かしらセキュリティ機構を突破する手法が存在すると推測されますが、残念ながらインターンシップの期間中には見つけることができませんでした。