
authorized_key のローカル権限昇格ansible.posix.authorized_key Ansible モジュールにおける、ユーザーの ~/.ssh ディレクトリと authorized_keys ファイルに対するシンボリックリンク追従 chown(およびシンボリックリンク追従によるファイル作成)を介したローカル権限昇格。
これは、私が報告し、Red Hat Product Security(CNA)によって CVE-2026-11837 が割り当てられた脆弱性の writeup です。技術的な記録として公開されており、新規性を主張するものではありません。この問題は、以前の修正がカバーしなかったモジュールにおける CVE-2024-9902 の姉妹です。
Red Hat が公開した謝辞:「Red Hat はこの問題を報告してくれた Valentino Paulon に感謝します」
root として実行されるプレイブックが ansible.posix.authorized_key を使用してローカルユーザーの鍵を管理する場合、モジュールの keyfile() ヘルパーは:
~/.ssh ディレクトリと ~/.ssh/authorized_keys の所有権を、通常の os.chown(os.lchown ではない)で変更し、open(..., "w")(O_NOFOLLOW なし)で作成します。デフォルトの follow=False では、モジュールはシンボリックリンクを追って置き換えることも、拒否することもありません。パスに対して直接操作するため、カーネルは非特権の対象ユーザーが自身の ~/.ssh 内に事前に仕掛けたシンボリックリンクを追跡します。これにより、root が管理するパスの所有権を非特権ユーザーに移す 2 つのプリミティブが生じ、そのユーザーは root に昇格します。どちらもコレクションの HEAD に対してエンドツーエンドで再現しました。
keyfile()(plugins/modules/authorized_key.py)、デフォルトの manage_dir=True、follow=False の場合:
if manage_dir:
if not os.path.exists(sshdir): # os.path.exists FOLLOWS symlinks
try:
os.mkdir(sshdir, int('0700', 8))
...
os.chown(sshdir, uid, gid) # UNCONDITIONAL, plain chown, follows symlink
os.chmod(sshdir, int('0700', 8)) # follows symlink
if not os.path.exists(keysfile): # follows symlink
...
f = open(keysfile, "w") # plain open(), no O_NOFOLLOW, follows symlink
...
try:
os.chown(keysfile, uid, gid) # UNCONDITIONAL, plain chown, follows symlink
os.chmod(keysfile, int('0600', 8)) # follows symlink
except OSError:
pass
sshdir と keysfile は、対象ユーザーの passwd ホーム(pwd.getpwnam(user).pw_dir)から取得されます。これは、対象となる非特権ユーザーが所有・管理するディレクトリです。follow パラメータ(デフォルト False)は、パスに対して os.path.realpath() を実行するかどうかを選択するだけであり、いずれの場合も os.path.islink チェック、O_NOFOLLOW オープン、os.lchown、既存のシンボリックリンクの unlink-and-replace は行われません。特権の降格(seteuid/setfsuid)は実行されず、すべて root として実行されます。
2 つの os.chown 呼び出しは、if not os.path.exists(...) ガードの外側で無条件に実行されるため、ディレクトリやファイルが既に存在するかどうかに関係なく発動します。これが両方のプリミティブを確実なものにしている理由です。
両方とも、使い捨ての Linux VM 上で ansible.posix を HEAD にしてエンドツーエンドで確認しました。victim は非特権ローカルユーザー(uid 1000)です。モジュールは、オペレーターのプレイブックとまったく同様に root として実行されます。カナリアは機密性の高い root ターゲットの代役です。
ベクター 1: ディレクトリのシンボリックリンク(既存の root 所有ディレクトリの chown)
victim として:ln -s /root/AD_CANARY ~/.ssh(/root/AD_CANARY は既存の root 所有ディレクトリ)。victim に対して authorized_key タスクを実行する。/root/AD_CANARY の所有権が root:root から victim:victim に変更された。os.chown(sshdir, ...) 呼び出しが ~/.ssh シンボリックリンクを追従し、root 所有のディレクトリを非特権ユーザーに渡した。ベクター 2: ダングリングファイルのシンボリックリンク(新しい root パスファイルの作成と chown)
victim として(~/.ssh は通常のディレクトリ):ln -s /root/AF_CANARY ~/.ssh/authorized_keys(ターゲットは存在しない)。victim に対して authorized_key タスクを実行する。/root/AF_CANARY が作成され、所有権は victim:victim、モード 0600 になった。open(keysfile, "w") がシンボリックリンクを介してターゲットを作成し、os.chown(keysfile, ...) がそれを引き渡した。どちらの場合も、非特権ユーザーが最終的に root 管理下のパスを所有することになります。リンクを /etc 配下のディレクトリ(ベクター 1)や /etc/cron.d/ または /etc/sudoers.d/ 配下の新しいファイル(ベクター 2)に向けると、標準的な後続手順により root が得られます。ユーザーは所有者となったため、ターゲットを書き換えられます。
これは CVE-2024-9902(ansible-core の user モジュールの generate_ssh_key)の姉妹です。この CVE は同じシンボリックリンク追従クラスに対処したものです。authorized_key は別の ansible.posix コレクションにあり、その修正では変更されませんでした。シンボリックリンク追従の chown/作成パターンは依然として存在し、保護されていませんでした。これは、以前の修正が到達しなかったモジュールにおける同じ受理済みクラスとして報告されたものであり、新しい根本原因としてではありません。
ansible.posix.authorized_key タスクを実行することが条件(このモジュールの一般的な使用法)。ローカルユーザーは自身の ~/.ssh 内にシンボリックリンクを事前に仕込みます。プレイブックを実行するのはそのユーザーではありません。これはオペレータートリガーです。CVE-2024-9902 と同じトリガーモデルであり、Red Hat は「信頼できないアカウントに対して実行しない」という緩和策で受理しました。前提条件は自己トリガーとして評価されるのではなく、明示的に述べられています。os.open(keysfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) を使用し、ディスクリプタに対して操作する。sshdir または keysfile がシンボリックリンクの場合は拒否する(または follow=False の docstring 契約に従って unlink-and-replace する)。os.chown(...) を os.lchown に置き換えるか、安全に開いたディスクリプタ上の os.fchown に置き換える。同様に os.chmod の代わりに os.fchmod を使用し、所有権と権限がリンクを通じてリダイレクトされないようにする。chown/chmod を、モジュール自身が安全に作成したばかりのパスにのみ適用されるようにゲートし、既存の(シンボリックリンクの可能性のある)パスには適用しないようにする。| 日付 | 出来事 |
|---|---|
| 2026-06-08 | [email protected] に報告(CNA として Red Hat Product Security を CC に含む)、CVE-2024-9902 の姉妹として位置付け |
| 2026-06-10 | CVE-2026-11837 が割り当てられ公開。クレジットが受理された |
MIT。 LICENSE を参照。
| CVE | CVE-2026-11837 |
| コンポーネント | ansible.posix コレクション、ファイル plugins/modules/authorized_key.py、関数 keyfile() |
| タイプ | 権限昇格(ローカル)。 CWE-59 / CWE-61(リンク追従)と CWE-282(不適切な所有権管理) |
| CVSS | 7.3 (High) |
| 公開 | 2026-06-10 |
| 報告者 | Valentino Paulon |