
2019年1月、当時の Ubuntu Linux の現行バージョンが snapd API のバグによりローカル権限昇格の脆弱性の影響を受けることが判明しました。このリポジトリには、研究および教育用に公開されているオリジナルのエクスプロイト POC が含まれています。脆弱性とエクスプロイトの詳細なウォークスルーについては、 こちらのブログ投稿を参照してください。
Ubuntu には snapd がデフォルトで含まれていますが、このパッケージがインストールされているディストリビューションであれば、いずれもエクスプロイト可能です。システムが脆弱かどうかは簡単に確認できます。以下のコマンドを実行してください。snapd が 2.37.1 以降であれば安全です。
$ snap version
...
snapd 2.37.1
...
一部のシステムでは、このコマンドを実行すると上記の例のようなアップストリーム版ではなく、ディストリビューションのパッケージ版の snapd バージョンが返されることに注意してください。snapd のバージョンに Ubuntu のバージョン番号のようなものが付加されている場合(例: 2.34.2ubuntu0.1 や 2.35.5+18.10.1)、このリンクを参照して、パッチ適用済みバージョンを実行しているかどうかを確認してください。
このエクスプロイトは、アクセス制御チェックをバイパスして、ローカルの snapd サービスの制限付き API 関数 (POST /v2/create-user) を使用します。これは、指定されたメールアドレスのユーザー名と公開 SSH キーを Ubuntu SSO に問い合わせ、これらの値に基づいてローカルユーザーを作成します。
このバージョンのエクスプロイトを成功させるには、アウトバウンドのインターネット接続と、localhost からアクセス可能な SSH サービスが必要です。
エクスプロイトするには、まず Ubuntu SSO でアカウントを作成します。確認後、プロフィールを編集して SSH 公開キーをアップロードします。次に、以下のようにエクスプロイトを実行します(アップロードした公開キーに対応する SSH 秘密キーを使用):
python3 ./dirty_sockv1.py -u "[email protected]" -k "id_rsa"
[+] Slipped dirty sock on random socket file: /tmp/ktgolhtvdk;uid=0;
[+] Binding to socket file...
[+] Connecting to snapd API...
[+] Sending payload...
[+] Success! Enjoy your new account with sudo rights!
[Script will automatically ssh to localhost with the SSH key here]
このエクスプロイトは、アクセス制御チェックをバイパスして、ローカルの snapd サービスの制限付き API 関数 (POST /v2/snaps) を使用します。これにより、任意の snap をインストールできます。「devmode」の snap はサンドボックスをバイパスし、インストール時に root のコンテキストで実行される「インストールフック」を含めることができます。
dirty_sockv2 はこの脆弱性を利用して、ローカルシステムに新しいユーザーを追加するフックを含む空の「devmode」snap をインストールします。このユーザーは sudo コマンドを実行する権限を持ちます。
バージョン 1 とは異なり、これは SSH サービスの稼働を必要としません。また、インターネット接続がまったくない新しいバージョンの Ubuntu でも動作するため、変更に対して耐性があり、制限された環境でも効果的です。
明確にするための注意: このバージョンのエクスプロイトは悪意のある snap 内に隠れるのではありません。代わりに、ユーザー作成ペイロードの配信メカニズムとして悪意のある snap を使用します。これは、バージョン 1 と同じ uid=0 のバグが原因で可能になっています
このエクスプロイトは、snapd をインストールしているが、互換性のない Linux シェル構文のため「create-user」API をサポートしていない非 Ubuntu システムでも効果的です。
一部の古い Ubuntu システム (16.04 など) には、サイドローディングに必要な snapd コンポーネントがインストールされていない場合があります。その場合、このバージョンのエクスプロイトがこれらの依存関係のインストールを引き起こす可能性があります。そのインストール中に、snapd が脆弱性のないバージョンに自己アップグレードする可能性があります。テストによると、このシナリオでもエクスプロイトは依然として成功します。詳細については、トラブルシューティングのセクションを参照してください。
エクスプロイトするには、脆弱なシステム上で引数なしでスクリプトを実行するだけです。
python3 ./dirty_sockv2.py
[+] Slipped dirty sock on random socket file: /tmp/gytwczalgx;uid=0;
[+] Binding to socket file...
[+] Connecting to snapd API...
[+] Deleting trojan snap (and sleeping 5 seconds)...
[+] Installing the trojan snap (and sleeping 8 seconds)...
[+] Deleting trojan snap (and sleeping 5 seconds)...
********************
Success! You can now `su` to the following account and use sudo:
username: dirty_sock
password: dirty_sock
********************
バージョン 2 を使用していて、エクスプロイトが完了したのに新しいアカウントが表示されない場合、バックグラウンドの snap 更新が原因である可能性があります。これらは snap changes を実行し、次に snap change # を実行して、dirty_sock snap のインストールを示す行を参照することで確認できます。最終的にこれらの処理が完了すれば、アカウントを使用できるようになります。
環境が対応している場合 (SSH サービスが稼働し、localhost からアクセス可能な場合)、バージョン 1 が最も簡単で最速のようです。
脆弱性のないシステムでは、以下のような出力が表示されます:
[!] System may not be vulnerable, here is the API reply:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Date: Mon, 18 Feb 2019 07:07:12 GMT
Content-Length: 119
{"type":"error","status-code":401,"status":"Unauthorized",
"result":{"message":"access denied","kind":"login-required"}}
何か異常があれば issue を開いてください。
この問題は、Ubuntu のバグトラッカーを通じて snapd チームに直接報告されました。完全なスレッドはこちらで読むことができます。
この問題に対する Canonical の対応には非常に感銘を受けました。チームは協力的で素晴らしく、全体的にこの経験は、私自身が Ubuntu ユーザーであることに大きな満足感を与えてくれます。
公開アドバイザリリンク:
注記: 私はこの GitHub リポジトリ、私のブログ (initblog.com)、および私のチームのブログ (shenaniganslabs.io) にのみ情報を投稿しています。公式ソースを装ったサイトについては、残念ながら私の管理外です。