Skip to content

DNET_SCBOFCountermeasures

nishi_74322014 edited this page Sep 11, 2026 · 1 revision

SC:システム開発 - プログラミングによるBOF対策

概要

  • C/C++のバッファ・オーバーフロー(BOF)系の対策

  • 言語仕様として、全体的にBOF系に弱い。

    • 終端ナル(NULL)文字を忘れると、終端が認識されずBOFに繋がる。
    • 後述のように、BOFを引き起こす可能性がある引数チェックされない関数が多い。
  • 以下の2つがある

    • スタックベースのバッファ・オーバーフロー

      • 基本こちら
      • 呼び出し元に戻るアドレス部分を書き換え攻撃コードに誘導する。
    • ヒープベースのバッファ・オーバーフロー
      スタックベースと比べ、攻撃が成功し難い。

詳細

データ実行防止機能(DEP)

  • データ領域からコードを実行することを防止する。

    • Microsoft Windowsに搭載されているセキュリティ機能。
    • ハードウェアDEP、ソフトウェアDEPがある。
  • DEP対策として、return-to-libc攻撃がある。

コンパイラに依る対策

開発環境やコンパイラによってはサポートされないことがある。

SSP(Stack Smashing Protection)

  • スタックフレーム内のebpレジスタとローカル変数の間に
    canary(カナリア・コード)を配置することで、スタック・オーバーフローの検知を行う。

  • 元となった実装の名前からStackGuardやProPoliceとも呼ばれることがある。

ASLR(Address Space Layout Randomization)

  • 各種領域をメモリ上にランダムに配置。
  • コレにより、return-to-libc対策になる。

PIE(Position Independent Executable)

ASLRに加え、text領域のランダム化も行う(Full-ASLRとも言う)。

AF(Automatic Fortification)

脆弱性の原因となるような関数をコンパイル時に
別の安全性の高い同じ機能を持つ関数へと置換する。

BOFを起こす関数

以下の様に、BOFを引き起こす可能性がある引数チェックされない関数が多い。

関数一覧

# 関数 代替案/対策 用法 備考
1 char *gets(char *s)
標準入力から一行を取り込む。
char *fgets(char *s, int n, FILE *stream); fgets(dst , sizeof(dst) , stdin); gets と異なり
・引数に最大読み込み文字数を指定する。
・改行文字も 1文字として数える。
・実際読むのはn-1で最後は終端ナル(NULL)文字になる。
 ・終端記号は、末尾の改行を置換ではなく改行の後ろに付与
 ・最大文字数を超えた場合でも終端ナル(NULL)文字を保証
2 char *strcpy(char *dst, const char *src);
src文字列をdst文字列バッファに'\0' までコピー
void *memset(void *buf, int ch, size_t n);
char *strncpy(char *dst, const char *src, size_t n);
memset(dst, 0 , sizeof(dst));
-> strncpy(dst, src, sizeof(dst) - 1);
・コピーされる文字の長さは、src長とnのいずれか小さい方。
・src長がnより短い場合、終端ナル(NULL)文字を保証
3 char *strcat(char *dst, const char *src);
src文字列をdst文字列バッファに'\0' まで連結
char *strncat(char *dst, const char *src, size_t n); strncat(dst, src, sizeof(dst) - strlen(dst) - 1) ・文字列連結される文字の長さは、src長とnのいずれか小さい方。
・src長がnより短い場合、終端ナル(NULL)文字を保証
4 int sprintf(char *dst, const char *format, ...);
可変個引数をstring.Format()的に処理しdst文字列バッファに格納。
sprintf(dst, ”%.{B}s”, data) {B} = 書式指定子の精度(最大文字数)
= dstの要素数 - 書式指定子以外の文字数 - 1(終端ナル(NULL)文字)
5 int sprintf(char *dst, const char *format, ...); int snprintf(char *dst, size_t n, const char *format, ...); snprintf(dst, sizeof(dst), ”%s”, data) 動作としては、fgets()に近く、実際読むのはn-1で最後は終端ナル(NULL)文字になる。
6 int scanf(const char *format, ...);
標準入力から空白、タブ、改行で分割された文字列を読みstring.Format()的に処理し可変個引数に格納。
scanf(”%.{B}s”, dst) {B} = 書式指定子の精度(最大文字数)
= dstの要素数 - 書式指定子以外の文字数 - 1(終端ナル(NULL)文字)
7 int sscanf(char *src, const char *format, ...);
src文字列をstring.Format()的に処理し可変個引数に格納。
sscanf(src, ”%.{B}s”, dst) {B} = 書式指定子の精度(最大文字数)
= dstの要素数 - 書式指定子以外の文字数 - 1(終端ナル(NULL)文字)
8 int fscanf(FILE *fp, const char *format, ...);
ストリームをstring.Format()的に処理し可変個引数に格納。
fscanf(fp, ”%.{B}s”, dst) {B} = 書式指定子の精度(最大文字数)
= dstの要素数 - 書式指定子以外の文字数 - 1(終端ナル(NULL)文字)
9 getchar();, fgetc(fp);, getc(fp);
入力から一文字を取り込む。
使用する際にバッファ境界チェックを行う。

パターン

  • 何れもdstのサイズを調査して長さを指定する。

    • 基本的には、n = dstのサイズ - 1(終端ナル(NULL)文字用)
    • 指定先は、size_tか、書式指定子の精度(書式指定子の場合、更に書式指定子以外の文字列数を引く)になる。
    • 関数によっては(fgets、snprintf)、入力を自動的にn-1するものもあり、「- 1」が不要。
  • va_listを使用する関数も基本的対応は同じ。

    • vscanf → scanf
    • vsscanf → sscanf
    • vfscanf → fscanf
    • vsprintf → sprintf

パラメータ検証

  • CRTセキュリティ強化(VC++バージョンアップの該当節を参照)

  • Libsafe

    • BOFをチェックするコードを読み込ませた後に
    • 本来呼び出されるライブラリの内容を呼び出す。
    • チェック関数挿入、コンパイル不要

まとめ

OS

データ実行防止機能(DEP)

コンパイラに依る対策(まとめ)

プログラミング

  • BOFを起こす関数を適切に利用する。
    gets、strcpy、strcat等を使用しない、_sを使用。

  • バッファ・コピー処理をLoopなどで書かない。

参考

各種領域

スタック領域

ヒープ領域

プログラム領域

  • スタックは、下から上に積み上がる
    (下位アドレスが下に書いてある場合)

  • 上から順に、サブルーチンの

    • ESP(先頭)
    • バッファ
    • ローカル変数
    • canary
    • EBP(底)
    • リターン・アドレス

となる。

その他

  • text領域
    プログラムおよび定数
  • data領域
    初期値をもつ静的変数
  • bss領域
    初期値をもたない静的変数

Wikipedia

移行メモ

  • 「言語使用として、全体的にBOF系に弱い。」→「言語仕様として」に正した。
  • 概要の「以下の様に、BOFを引き起こす可能性がある…関数が多い。」は、 直後に一覧が続かず「BOFを起こす関数」の節にあるため「後述のように」とした。
  • 関数一覧の scanf / sscanf / fscanf の説明にあった 「処理し格納可変個引数に格納」→「処理し可変個引数に格納」に正した。
  • 元 Wiki の結合セル(~)を含む表は、GitHub Wiki では再現できないため、 セルの内容を展開して平坦な表にした(5 行目の sprintf)。
  • 「まとめ」配下の見出し「コンパイラに依る対策」は、 同名の見出しが既にあり GitHub Wiki でアンカが衝突するため 「コンパイラに依る対策(まとめ)」とした。
  • 元 Wiki で見出しそのものが他ページ・同ページ内へのリンクになっていた箇所 (「コンパイラに依る対策」「スタック領域」「ヒープ領域」)は、 GitHub Wiki では見出しからアンカが生成されるため、 見出しをプレーン・テキストとし、リンクは直下の本文に置いた。
  • マイクロソフト系技術情報 Wiki(techinfoofmicrosofttech.osscons.jp)への URL リンクは、移行済みの VC++バージョンアップ / スレッドのスタック / ヒープ に張り替えた。
  • PukiWiki のページ内アンカ(#xxxxxxxx)は GitHub Wiki では再現できないため、 同一ページ内のアンカは見出しから生成されるアンカに張り替え、 他ページのアンカを指すリンクは「〜(ページ名 の該当節を参照)」の形に置き換えた。

Tags: 移行, 資格, SC, システム開発, バッファ・オーバーフロー, BOF, C/C++, DEP, ASLR, PIE, canary, StackGuard, セキュア・プログラミング

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally