フォーマット文字列脆弱性
printfのフォーマット文字列を悪用して、任意のメモリを読み取り、任意のアドレスに書き込みます。
「フォーマット文字列脆弱性」はCoddyKit上の無料Cyber Security Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCyber Security Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Cyber Security Academyコースには全4レッスンが含まれています。
フォーマット文字列の脆弱性とは何ですか
フォーマット文字列の脆弱性は、ユーザー入力がprintf()などの関数のフォーマット文字列引数に直接渡されたときに発生します。攻撃者はメモリを読み取ったり、スタックアドレスをリークしたり、任意の値を書き込んだりできます。
脆弱なパターン
脆弱な例と安全な例:
// VULNERABLE - user controls the format string:
printf(user_input);
fprintf(stderr, user_input);
// SAFE - user input is a data argument:
printf("%s", user_input);%xと%pによるメモリの読み取り
対応する引数のないフォーマット指定子は、スタックから値を読み取ります:
# Input: %p.%p.%p.%p.%p
# Output: 0x7ffd12345678.0x400580.0x0.0x1.0x7f3a...
# Each %p prints the next value off the stack - leaking addresses!フォーマット文字列のリークによるASLRの回避
スタックには、libc、スタック、バイナリへのポインターが含まれていることがよくあります。%pや%lxの指定子でこれらをリークすると、攻撃者はベースアドレスを計算してASLRを突破できます。他の脆弱性がなくても可能です。
%sによる任意メモリの読み取り
%sはスタック上の値をポインターとして扱い、そのアドレスにある文字列を出力します。対象アドレスをスタック上の適切な位置に配置し、パラメータアクセス(%7$s)を使うことで、攻撃者は任意のメモリ位置を読み取れます。
# Direct parameter access:
printf("%7$s") # prints string at 7th stack parameter
# If 7th param is an address you control, reads that memory%nによるメモリへの書き込み
%nは、これまでに出力された文字数を、対応する引数が指すアドレスに書き込みます。これにより、任意のアドレスに制御された値を書き込めるため、関数ポインターやリターンアドレスを上書きできる可能性があります。
実践的な書き込み手法
%nを使って特定の値を書き込むには:
- 対象アドレスを指すポインターをスタック上で制御します
%Xc%N$nを使ってX文字を出力し、その文字数をN番目のパラメータ(対象アドレス)に書き込みます
pwntoolsなどのツールを使うと、フォーマット文字列エクスプロイトの構築を自動化できます。
フォーマット文字列バグの検出
フォーマット文字列の脆弱性をテストする方法:
# Test inputs:
%x.%x.%x
%p.%p.%p
%s%s%s
AAAA.%p.%p.%p.%p (looking for 0x41414141 in output)
# Automated testing with fuzzer:
python3 -c "print('%p.' * 20)" | ./vuln防御策: コードを修正する
修正方法は非常に簡単です。ユーザー入力は必ずデータ引数として渡し、フォーマット文字列として渡してはいけません。
// WRONG:
printf(buffer);
// RIGHT:
printf("%s", buffer);
// Compiler warning:
// gcc -Wformat-security will flag this他の言語におけるフォーマット文字列
フォーマット文字列の脆弱性は、多くの言語で発生します:
- C:
printf(input) - Python: 同じ問題ではありませんが、辞書入力を使った
%フォーマットで変数が公開される可能性があります - Java:
String.format(input)— より危険性は低く、メモリにアクセスすることもありません
実際の事例
フォーマット文字列バグは、次のソフトウェアに影響を与えました:
- ProFTPD — FTPコマンド経由のリモートコード実行を可能にしました
- WU-FTPD — フォーマット文字列を介したリモートrootエクスプロイトを可能にしました
- 2000年代以前のさまざまなLinuxデーモン
現代のコンパイラーやFORTIFY_SOURCEは有効ですが、この脆弱性クラスを完全に排除することはできません。
クイックチェック: フォーマット文字列
フォーマット文字列攻撃で任意のメモリアドレスに書き込むために使えるフォーマット指定子はどれですか?
レッスンのまとめ
フォーマット文字列の脆弱性は、ユーザー入力がprintfのフォーマット文字列として直接使われたときに発生します。%p/%xはスタックメモリをリークし、%sは任意のアドレスを読み取り、%nは任意のアドレスに書き込みます。修正方法は1文字の追加です: printf("%s", input)。コンパイラーフラグで-Wformat-securityを有効にすると、これらの問題を検出できます。
よくある質問
「フォーマット文字列脆弱性」レッスンは無料ですか?
はい。「フォーマット文字列脆弱性」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Cyber Security Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Cyber Security Academyコースには全4レッスンが含まれています。
「フォーマット文字列脆弱性」で何を学びますか?
printfのフォーマット文字列を悪用して、任意のメモリを読み取り、任意のアドレスに書き込みます。 ブラウザで直接実行するハンズオンコードでCyber Security Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Cyber Security Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのCyber Security Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「フォーマット文字列脆弱性」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このCyber Security Academyレッスンでコードを書いて実行できますか?
はい。すべてのCyber Security Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。