-
Notifications
You must be signed in to change notification settings - Fork 0
DNET_SCBOFCountermeasures
- 戻る(システム開発)
-
C/C++のバッファ・オーバーフロー(BOF)系の対策
-
言語仕様として、全体的にBOF系に弱い。
- 終端ナル(NULL)文字を忘れると、終端が認識されずBOFに繋がる。
- 後述のように、BOFを引き起こす可能性がある引数チェックされない関数が多い。
-
以下の2つがある
-
スタックベースのバッファ・オーバーフロー
- 基本こちら
- 呼び出し元に戻るアドレス部分を書き換え攻撃コードに誘導する。
-
ヒープベースのバッファ・オーバーフロー
スタックベースと比べ、攻撃が成功し難い。
-
-
データ領域からコードを実行することを防止する。
- Microsoft Windowsに搭載されているセキュリティ機能。
- ハードウェアDEP、ソフトウェアDEPがある。
-
DEP対策として、return-to-libc攻撃がある。
開発環境やコンパイラによってはサポートされないことがある。
-
スタックフレーム内のebpレジスタとローカル変数の間に
canary(カナリア・コード)を配置することで、スタック・オーバーフローの検知を行う。 -
元となった実装の名前からStackGuardやProPoliceとも呼ばれることがある。
- 各種領域をメモリ上にランダムに配置。
- コレにより、return-to-libc対策になる。
ASLRに加え、text領域のランダム化も行う(Full-ASLRとも言う)。
脆弱性の原因となるような関数をコンパイル時に
別の安全性の高い同じ機能を持つ関数へと置換する。
以下の様に、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をチェックするコードを読み込ませた後に
- 本来呼び出されるライブラリの内容を呼び出す。
- チェック関数挿入、コンパイル不要
- StackGuard、canary(カナリア・コード)
- メモリ配置のランダム化(ASLR、PIE)
-
BOFを起こす関数を適切に利用する。
gets、strcpy、strcat等を使用しない、_sを使用。 -
バッファ・コピー処理をLoopなどで書かない。
- バッファオーバーフローへの対策技術入門
https://www.saitolab.org/infra_kaisetsu/
-
スタックは、下から上に積み上がる
(下位アドレスが下に書いてある場合) -
上から順に、サブルーチンの
- ESP(先頭)
- バッファ
- ローカル変数
- canary
- EBP(底)
- リターン・アドレス
となる。
- text領域
プログラムおよび定数 - data領域
初期値をもつ静的変数 - bss領域
初期値をもたない静的変数
-
データ実行防止
https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%BC%E3%82%BF%E5%AE%9F%E8%A1%8C%E9%98%B2%E6%AD%A2 -
Return-to-libc攻撃
https://ja.wikipedia.org/wiki/Return-to-libc%E6%94%BB%E6%92%83
移行メモ
- 「言語使用として、全体的に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, セキュア・プログラミング
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。