web-dev-qa-db-ja.com

安価な国、ヒープオブジェクトの関数ポインタを保護する

ヒープベースのバッファーオーバーランに対してシステムを強化するために、以前に次の方法を提案、評価、または展開した人がいるのではないかと思います。スタック。

次のような構造体を考えます

struct whatever {
    int blah;
    char buf[256];
    void (*fp)(); // a function pointer
}

bufフィールドの終わりを超えて書き込むオーバーランがある場合、関数ポインターフィールドfpを上書きできることに注意してください。

コンパイラは、バッファと関数ポインタの間に保存されたカナリア(秘密のランダムな値)を導入することで、これを正当に防御できます。基本的に、コンパイラーは構造のレイアウトを次のように変換します。

struct whatever {
    int blah;
    char buf[256];
    unsigned int canary; // inserted by compiler; not exposed to source code
    void (*fp)(); // a function pointer
}

たとえば、コンパイラは、プログラムがcanaryに書き込むときはいつでも、グローバルシークレット値でfpフィールドを書き込むように調整し、canaryの値が残っていることを確認できます。プログラムがfpから読み取るときは常に変更されません。

これは基本的にスタックカナリアの類似物ですが、スタックの戻りアドレスではなく、ヒープの関数ポインタの保護に焦点を当てています。それは自然な考えのようです。

誰かがこれを以前に提案したことがありますか?誰かがそれを試作したり、このようなことをすることのパフォーマンスコストを評価したりしましたか? (スタックカナリアのように、コンパイラの変更が必要であるという事実を超えて)展開への明らかでない障壁はありますか?


私が行った研究:ヒープ内のオブジェクト間にガードページを挿入するという考えは知っていますが、それは異なります(単一のオブジェクトの境界を超えるヒープオーバーフローから保護しますが、単一のヒープオブジェクトの領域内にとどまるヒープオーバーフローから保護します)。私は CruiserContraPolice に精通していますが、これはヒープ内のオブジェクト間にカナリアを配置しますが、これもオブジェクト内オーバーフローではなくオブジェクト間オーバーフローに焦点を当てています。また、mallocのメタデータを保護するためのスタックカナリアまたはポインター暗号化の使用にも精通していますが、これはオブジェクト内オーバーフローを保護せず、関数ポインターではなくmallocのメタデータを保護することを目的としています。

4
D.W.

オブジェクト内のカナリアは、1つの実用的な問題にぶつかります。それは、これらのオブジェクトのメモリ内レイアウトを変更します。このレイアウトは、たとえばプログラムとライブラリの間で渡されるときに一貫している必要があります。タイプstruct whatever *のポインターを、コンパイルされたプログラムからwithこの計測器に、ライブラリーにコンパイルされたwithoutこの計測器に渡した場合(ただし、同じ宣言struct whatever)の場合、一部のフィールドが異なるオフセットにあるため、問題が発生します。その結果、理論的な研究以外の目的で内部カナリアを使用すると、既存のほとんどすべてのコードとライブラリとの互換性が失われます。

2
Mark

同様のアプローチは、mallocメタデータの保護に使用されるものと同様の関数ポインター暗号化です。これは、「オーバーフロー攻撃に対する保護」の セクション2.4 で提案されています。

本の分析は好意的です。この手法については 古い論文 もあります(そして 別の論文 )。ただし、私の知る限り、この手法は積極的に使用されていません。どうしてそうなのか全くわからない。おそらくそれはパフォーマンスと互換性の問題です。またはおそらくNSAはそれを提案した人々を沈黙させた.

1
paj28