FlawOpen

flawopen.com/Teardowns/cve-2023-6246-glibc-syslog-heap-buffer-overflow

● CVE-2023-6246 · CVSS 7.8 · 高
セキュリティ研究 · FlawOpen

CVE-2023-6246:glibc syslogのヒープバッファーオーバーフロー

CVE-2023-6246は長いログヘッダーで発生するglibc syslogのヒープオーバーフローです。ディストリビューションの修正済みglibcを適用してください。

💡 わかりやすい解説 (ELI5)

短い定規で荷物と送り状を測る担当者を想像してください。送り状が長すぎて定規に収まらず、必要な大きさが計算に記録されません。担当者は小さな箱を注文し、送り状と中身をすべて印刷しようとします。glibcでは、長いsyslogヘッダーによって必要サイズが記録されず、ログ処理がヒープ上に小さすぎる領域を確保する可能性がありました。

主要な概念と専門用語

glibc
GNU C Libraryは、多くのLinuxプログラムが使うCランタイムの基本機能を提供します。
__vsyslog_internal()
syslog() と vsyslog() がメッセージと SYSLOG_HEADER を整形し、システムロガーへ送るために使うglibc内部実装です。
SYSLOG_HEADER
syslogメッセージの前に整形される優先度と識別情報のプレフィックスです。プログラム名や openlog() で渡す識別子を含む場合があります。
Heap-based buffer overflow (CWE-122)
ヒープに確保したバッファーの境界を超える書き込みです。ここでは長い名前を含むヘッダーが初期固定バッファーに収まらないとサイズ計算が失敗しました。

フレームワーク対応

  • このガイドで説明する弱点の CWE 参照: CWE-122

根本原因の分析 (Root Cause)

glibcのアドバイザリによると、長いプログラム名を含む SYSLOG_HEADER を整形する際、__vsyslog_internal() が必要なバッファーサイズを更新していませんでした。脆弱なコードではヘッダー長 l が1,024バイトのスタックバッファー以上になり、本文を整形して bufsize を設定する分岐が実行されません。その後のヒープ確保では未設定またはゼロのサイズが使われ、小さすぎる領域にヘッダーとメッセージを書き込みます。Qualysによると、長い名前は呼び出し側が制御する argv[0] のbasename、または1,024バイトを超える openlog() 識別子から来る場合があります。この経路でログを記録する脆弱なsetuidプログラムでは、セキュリティ上の影響が生じ得ます。QualysはFedora 38の su を通じたローカル権限昇格を実証しました。リスクはローカルアクセス、設定、呼び出し経路に依存し、一般的なリモートsyslog攻撃ではありません。

ステップ・バイ・ステップの攻撃フロー

ステップ 1

ローカル条件

ローカルプロセスが、影響を受けるglibcを使うプログラムを通じて syslog() または vsyslog() を呼び出す必要があります。セキュリティ影響には特権のある呼び出し元と、プログラム名プレフィックスを安全で長さ制限された識別子に置き換えない経路が必要です。

ステップ 2

長いヘッダー

ヘッダーが初期バッファーを超える|SYSLOG_HEADER には長いプログラム名(argv[0] のbasenameに由来する __progname)または長い openlog() 識別子が含まれます。Qualysによると、トリガー値は1,024バイトを超える必要があります。

ステップ 3

計算の欠落

サイズ計算の分岐が省略される|ヘッダーが1,024バイトのスタックバッファーに収まらない場合、脆弱なコードは bufsize を計算する分岐を省略します。後続のヒープ確保ではヘッダーと整形済みメッセージの領域が不足します。

ステップ 4

メモリーへの影響

書き込みが確保領域を超える|ログ処理が小さすぎるバッファーにヘッダーとメッセージを書き込み、ヒープを破損させます。QualysはFedora 38の su を介したローカル権限昇格を実証しましたが、すべてのアプリやディストリビューションに同じ影響があるとは限りません。

ソースコード比較:脆弱 vs 堅牢化

ヘッダーがスタックバッファーに収まらない場合、旧分岐は bufsize をゼロのままにし、後続のヒープ確保が不足する可能性がありました。修正ではスタックバッファーを使用不可とし、本文サイズの測定に使って l + vl を記録し、必要サイズで既存の確保経路に進みます。これはupstreamの要点を示す抜粋で、単独で完結する関数ではありません。

ソース抜粋: misc/syslog.c

脆弱な実装
/* commit 6bd0e4efcc78 前の misc/syslog.c の該当箇所。 */
char bufs[1024];
char *buf = NULL;
size_t bufsize = 0;
...
if (0 <= l && l < sizeof bufs)
  {
    va_list apc;
    va_copy (apc, ap);
    int vl = __vsnprintf_internal (bufs + l, sizeof bufs - l, fmt, apc,
                                   mode_flags);
    if (0 <= vl && vl < sizeof bufs - l)
      buf = bufs;
    bufsize = l + vl;
    va_end (apc);
  }
/* 長いヘッダーは上にあるサイズ設定の分岐を通らない。 */
堅牢化されたセキュアパッチ
/* commit 6bd0e4efcc78 後の misc/syslog.c の該当箇所。 */
char bufs[1024];
char *buf = bufs;
size_t bufsize;
...
if (0 <= l && l < sizeof bufs)
  {
    pos = bufs + l;
    len = sizeof (bufs) - l;
  }
else
  {
    buf = NULL;
    pos = bufs;
    len = sizeof (bufs);
  }
vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
if (!(0 <= vl && vl < len))
  buf = NULL;
bufsize = l + vl;

エンジニアリング&システム堅牢化チェックリスト

参考資料