Looney Tunables ローカル権限昇格 (CVE-2023-4911) ワークショップ (教育目的のみ)
コンピューティングにおけるダイナミック リンカーは、ライブラリの内容を永続ストレージから RAM にコピーし、ジャンプ テーブルを埋め、ポインターを再配置することにより、実行可能ファイルの実行時に必要な共有ライブラリをロードしてリンクするオペレーティング システムの一部です。
たとえば、openssl ライブラリを使用して md5 ハッシュを計算するプログラムがあります。
$ head md5_hash.c
#include
#include
#include
ld.so はバイナリを解析し、
$ 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 に配置され、すべての参照がこのライブラリにリンクされます。
プログラムが開始されると、このローダーはまずプログラムを調べて、必要な共有ライブラリを決定します。次に、これらのライブラリを検索してメモリにロードし、実行時に実行可能ファイルにリンクします。その過程で、ダイナミック ローダーは関数や変数の参照などのシンボル参照を解決し、プログラムの実行に向けてすべてが設定されていることを確認します。ダイナミック ローダーは、その役割を考慮すると、ローカル ユーザーが set-user-ID プログラムまたは set-group-ID プログラムを起動するときに、昇格された特権でコードが実行されるため、セキュリティに非常に敏感です。
調整可能機能は、アプリケーション作成者や配布管理者がワークロードに合わせてランタイム ライブラリの動作を変更できるようにする 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 をダイナミック ローダーに渡して、すべての調整パラメータを最小値と最大値で出力します。
$ /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 行目)。 ):
// (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() の最初の引数 (tunestr) は、間もなくサニタイズされる GLIBC_TUNABLES のコピーを指し、2 番目の引数 (valstring) は、(スタック内の) 元の GLIBC_TUNABLES 環境変数を指します。 GLIBC_TUNABLES のコピー ( "tunable1= aaa:tunable2=bbb"
の形式である必要があります) をサニタイズするために、 parse_tunables() はすべての危険な調整パラメータ (SXID_ERASE 調整パラメータ) をtunestr から削除しますが、SXID_IGNORE および NONE 調整パラメータは保持します (行 221-) 235):