PHP2Shell, LiteLLM, GitLab: パッチ適用が必要なものを見つける
PHP2Shell, LiteLLM, GitLab: Find What You Need to Patch (cloudsleuth.io)
要約
2026年10月、PHP、Node.js、GitLab、LiteLLM、OpenCode、eMudhra認証局など、複数の主要なソフトウェアやサービスで深刻なセキュリティインシデントが発生しました。これらの脆弱性は、リモートコード実行の可能性や認証情報窃取など、広範囲に影響を及ぼすものでした。インシデント対応の迅速化には、影響を受ける資産の正確な把握と迅速なパッチ適用が不可欠ですが、多くの組織では資産管理の不備が対応を遅らせる要因となっています。
全文翻訳
PHP2Shell, LiteLLM, GitLab: パッチ適用が必要なものを見つける
2026年10月16日から23日の間に、PHP、Node.js、GitLab、LiteLLM、OpenCode、およびeMudhra認証局で深刻なセキュリティインシデントが発生しました。PHP、Node.js、GitLabは10月21日に修正パッチをリリースしましたが、これはLiteLLMの悪意のあるリリースが出回ったのと同じ日でした。すべてのアドバイザリは、「アップグレード、再構築、またはローテーション」といった何らかの対応を求めていました。
アップグレードを実行するのは簡単な部分です。時間がかかるのは、自身の環境で影響を受けるものがどこで実行されているか、誰がそれを所有しているか、そして何にアクセスできるかを把握することです。信頼できるインベントリがあれば、それは午後の作業で済みます。そうでなければ、Slackのスレッドを介した1週間の作業となり、今週は誰にも1週間を費やす余裕はありませんでした。AmazonのMadPotハニーポットは、バグが公開される4日前にPHP2Shellのエクスプロイト試行を記録していました。
[画像: "今週のインターネット"というタイトルのミーム。PHP: クリティカルなRCE、パッチは不完全な可能性あり; Node.js: 緊急RCEパッチ; GitLab: CVSS 10.0認証なしRCE、12年分のリリース; LiteLLM 1.107.0: 認証情報窃取マルウェア同梱; OpenCode Console: ユーザーのAPIキー漏洩; eMudhra CA: プライベートキーが文字通り4だった、とリストされている。その下に、スポンジボブがアームチェアから立ち上がり、「よし、もう行くぞ」というキャプションが付いている。]
今週のインターネット。魅力的ですが、それでもサーバーを見つける必要があります。
今週のインシデント
| インシデント | 発生内容 | 修正方法 | 確認が必要だったこと |
|---|---|---|---|
| PHP2Shell (CVE-2026-109618, CVE-2026-109619) | PHPにおける2つの認証前リモートコード実行バグ、それぞれ評価9.2。エクスプロイト試行は開示前に発生。 | PHP 8.5.12, 8.4.27, 8.3.36 または 8.2.35。一時的な設定ワークアラウンドではすべてのバリアントをカバーできないため、アップグレードのみが完全な修正となる。 | PHPを実行しているインスタンス、コンテナ、App Serviceアプリはどれか、そしてそれらは8.1以前のものか? |
| Node.js (CVE-2026-111497) | バンドルされているBrotliデコーダーにおける境界外書き込みにより、クラッシュ、メモリ開示、またはリモートコード実行につながる可能性があり、さらに9件の修正が含まれる。 | 22.23.4, 24.22.1 または 26.12.1。サポート終了のリリースラインには修正はない。 | Node.jsを実行しているLambda関数、コンテナ、VMはどれか、そしてそれらは20.x以前のものか? |
| GitLab (CVE-2026-110264) | GitLabの正規表現コンパイラにおける整数オーバーフローを介した、CVSS 10.0評価の認証なしリモートコード実行。2014年にリリースされたGitLab 7.0まで遡る。 | 10月21日のパッチリリース。セルフマネージドインスタンスに対するワークアラウンドはない。 | 誰も覚えていないセルフマネージドGitLabは存在するか、そしてインターネットからアクセス可能か? |
| LiteLLM 1.107.0 (Microsoftの分析) | PyPIおよび公式コンテナイメージ上の悪意のあるリリースが、Python起動時に認証情報スティラーを実行した。 | 検証済みのバージョンから再構築し、環境がアクセスできるすべての認証情報をローテーションする。 | どこで実行されたか、どのようなIDで実行されたか、そしてそのIDは何にアクセスできるか? |
| OpenCode Console (TechCrunch) | 侵入者がレガシーシステムに到達し、コンソールに保存されていたユーザーのAIプロバイダーキーが漏洩した可能性がある。一部のユーザーは不正請求を報告した。 | キーを失効させ、再発行する。 | そのキーは他にどこに保存されているか、そして失効時にどのワークロードが破損するか? |
| eMudhra (The Register) | 8つの中間CAプライベートキーが256未満の整数だった。1つは数字の4だった。 | ブラウザは中間証明書をブロックした。 | このチェーンから証明書を保持しているか、そして我々のイメージはまだそれを信頼しているか? |
リストにあるものはどれもマイナーなものではありません。2つはウェブの大部分が依存しているランタイムであり、1つは多くの企業が依然として自己ホストしているソースコード管理サーバー、そして1つはLLMトラフィックをルーティングするために多くのチームが急いで採用したAIゲートウェイです。AWSまたはAzureでそれなりの規模の環境を実行している場合、少なくとも1つは対応が必要だったはずです。
エクスプロイトウィンドウは短くなる一方
Mandiantは、攻撃者が脆弱性開示後にそれを悪用するのにかかる時間を追跡しています。そのエクスプロイトまでの時間分析では、平均が2018-2019年は63日、2020-2021年は44日、2021-2022年は32日でした。2023年には5日となり、Mandiantがその年に追跡した脆弱性の70%はゼロデイであり、パッチが存在する前に悪用されていました。
Hacker Newsのフロントページに10月23日に掲載されたDrew Silcock氏の投稿「Is this the new cyber normal?」は、LLMがこれをさらに圧縮し、悪意のあるRCEが公開されてからそれを標的とした自動攻撃までのギャップが、数週間ではなく数時間になったと主張しています。PHP2Shellがこれに該当します。バグはAIモデルの助けを借りて発見され、エンバーゴを巡る論争の後、パッチが適用されたバイナリがアドバイザリよりも前に登場し、ほとんどの人が修正をインストールできる前にエクスプロイト試行が開始されました。
あなたの露出ウィンドウは、影響を受けるものを特定する時間とそれを修正する時間の合計です。ほとんどのチームは、CI/CD、マネージドランタイム、自動パッチ適用により、後者の数値を縮小するために長年費やしてきました。前者の数値は依然としてスプレッドシート、Slackのスレッド、そして古いGitLabボックスがどのようにセットアップされたかを覚えている誰かです。
これが、フレームワークがインベントリを最優先する理由です。CIS Controlsは、エンタープライズ資産のインベントリとソフトウェアのインベントリをコントロール1と2としており、脆弱性管理はコントロール7まで登場しません。CISAのBOD 23-01は、米国連邦機関に対し、7日ごとに自動化された資産検出を実行し、14日ごとに脆弱性列挙を実行することを義務付けています。サーバーが存在することを知らなければ、パッチを適用できません。
ネイティブツールを使ったアドバイザリの処理
ここでは、AWSとAzureが標準で提供するものを使用して、今週のインシデントを例に、これらの質問にどのように答えるかを示します。以下のコマンドはすべて読み取り専用です。
1. どこで実行されているか
Node.jsから始めましょう。Lambdaでは、サポートされているマネージドランタイムはデフォルトでランタイムアップデートを自動的に取得するため、現在のNode.jsランタイムを使用しているほとんどの関数は再デプロイなしで修正を取得します。2つのグループは例外です。非推奨のnodejs20.xランタイム上の関数は、Node.js 20が2026年4月にサポート終了を迎えたため、セキュリティパッチを受け取らなくなりました。コンテナイメージとしてデプロイされた関数は、イメージに組み込まれたNode.jsバージョンを使用します。したがって、実際に必要なのは、古いランタイムを使用している、またはイメージとしてパッケージ化されている関数のリストです。
# すべての有効なリージョンにおけるNode.jsおよびコンテナイメージLambda関数
for region in $(aws ec2 describe-regions --query "Regions[].RegionName" --output text); do
echo "== $region"
aws lambda list-functions --region "$region" \
--query "Functions[?PackageType=='Image' || (Runtime && starts_with(Runtime, 'nodejs'))].[FunctionName, Runtime || PackageType]" \
--output text
done
Lambda APIは内部の情報を教えてくれないため、イメージベースの関数は上記の出力に含まれます。それらのためには、イメージスキャナーまたは再構築が必要です。
AzureでのPHP2Shellについては、App Serviceは各Linuxアプリのランタイムスタックを記録しており、Azure Resource Graphはサブスクリプション全体でそれを検索できます。
az graph query -q "
resources
| where type =~ 'microsoft.web/sites'
| mv-expand setting = properties.siteProperties.properties
| where setting.name =~ 'LinuxFxVersion' and tostring(setting.value) startswith 'PHP'
| project name, resourceGroup, subscriptionId, stack = tostring(setting.value)
" --first 1000 -o table
PHP 8.1以前のものは定期的なアップストリーム修正を受け取らないため、リストの最上位に配置してください。
VMやコンテナはより困難です。なぜなら、クラウドコントロールプレーンはそれらに何がインストールされているかを知らないからです。これはAWS Systems Manager Inventory、Amazon Inspector、Microsoft Defender for Cloudの役割であり、それぞれが有効になっているマシンしか認識しません。このカバレッジのギャップが最も痛いところで、後で触れます。
2. インターネットからアクセス可能か
GitLabはGitLab.com自体をパッチしました。セルフマネージドインスタンスはあなた次第であり、アップグレード以外に回避策はありません。私が懸念しているのは、プラットフォームチームが管理するGitLabではなく、あるチームが2019年にセットアップした、あるいは買収に伴って導入されたものの、まだ443で応答しているものです。
名前とタグで検索し、次に露出を確認します。
# GitLabのように見えるインスタンスで、パブリックIPとセキュリティグループを持つもの
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=*gitlab*,*GitLab*" \
--query "Reservations[].Instances[].[InstanceId, State.Name, PublicIpAddress, join(',', SecurityGroups[].GroupId)]" \
--output text
# 全てのインターネットに開いているインバウンドルールを持つセキュリティグループ
aws ec2 describe-security-groups \
--filters Name=ip-permission.cidr,Values=0.0.0.0/0 \
--query "SecurityGroups[].[GroupId, GroupName, VpcId]" \
--output text
Azureでは、az vm list -d --query "[?contains(name, 'gitlab')].{name:name, rg:resourceGroup, ips:publicIps}" -o tableで同様の最初のパスが得られます。
これが示すものには注意してください。名前フィルターは正直に名前が付けられたサーバーしか見つけられず、パブリックIPを持たないインスタンスでもパブリックロードバランサー経由で到達可能である可能性があります。どちらも、インシデントの最中にコンソールからこの質問に答えることを困難にします。インベントリが関係性を保持している場合は、はるかに簡単です。
3. 誰が所有しているか
サーバーを見つけたら、誰かがダウンタイムを承認する必要があり、理想的にはその人がどのようにインストールされたかを知っている必要があります。タグが最初の場所です。もしタグがない場合、CloudTrailはリソースを作成したユーザーを教えてくれます。
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=i-0abc123example \
--query "Events[].[EventTime, EventName, Username]" \
--output table
イベント履歴は90日間しか保持されず、Azureアクティビティログも同様です。2019年のサーバーについては、S3のログとAthenaクエリが必要であり、これはWho Created This AWS Resource?で説明されています。それでも、作成者はすでに退職している可能性があります。調査したケースの一つでは、89台のアイドル状態のAzure VMが買収によるもので、現在のチームの誰もその目的を説明できませんでした。そのようなリソースは、パッチが適用されていないGitLabが最終的に存在する場所なのです。
4. 何にアクセスできるか
LiteLLMは、パッケージの発見が始まりに過ぎない理由を示しています。Microsoftの分析によると、悪意のあるリリースはAWS、Azure、Google Cloudの認証情報キャッシュ、Kubernetesサービスアカウントファイル、Docker設定、およびマウントされたシークレットを標的にしました。また、AWSおよびGCPメタデータサービスから認証情報を取得しようとし、プロキシホストではLiteLLM自身のデータベースからプロバイダーキーを読み取りました。
悪意のあるパッケージはスキャナーが報告するものです。実際に対応する必要があるのは、ホストが実行されていたIDです。1.107.0をインストールしたすべての環境について、その役割と、その役割が許可されていることを確認してください。
# EC2インスタンスの背後にあるインスタンスプロファイル
aws ec2 describe-instances --instance-ids i-0abc123example \
--query "Reservations[].Instances[].IamInstanceProfile.Arn" --output text
# そのプロファイル内の役割、およびそれにアタッチされているポリシー
aws iam get-instance-profile --instance-profile-name litellm-proxy \
--query "InstanceProfile.Roles[].RoleName" --output text
aws iam list-attached-role-policies --role-name litellm-proxy-role
aws iam list-role-policies --role-name litellm-proxy-role
ECSではIDはタスク定義内のタスクロールです。EKSでは、Podのサービスアカウントにマッピングされたロールです。Microsoftのガイダンスは、クリーンなマシンから調査し、検証済みのアーティファクトから再構築し、アクセス可能なすべての認証情報をローテーションし、コンテナイメージをlatestのような浮動タグではなくダイジェストでピン留めすることです。クリーンマシンという部分を強調したいです。彼らの分析によると、ペイロードはPython起動時に実行されるため、疑わしい環境内でpipを実行してパッケージを確認すると、それがトリガーされる可能性があります。
5. シークレットまたは証明書が他にどこにあるか
OpenCodeはユーザーに、漏洩したキーを失効させ再発行するように指示しました。失効はクリック一つでできます。それを実行したときに何が壊れるかを把握することが本当の仕事です。なぜなら、AIプロバイダーキーはSecrets ManagerやKey Vault、LambdaやApp Serviceの設定、CI変数、開発者の.envファイルなどに配置されるからです。AWSでは、少なくともSecrets Managerは、各シークレットが最後にいつ読み取られ、最後にいつローテーションされたかを記録しており、CloudTrailのGetSecretValueイベントはどの役割がそれを読み取ったかを示します。
aws secretsmanager list-secrets \
--query "SecretList[].[Name, LastAccessedDate, LastRotatedDate]" \
--output table
eMudhraは証明書についても同様の問題を提起します。プライベートキーが非常に小さいため、誰でもそれを計算して、ブラウザが中間証明書をブロックするまで信頼していたであろう証明書に署名することができました。実行する価値のあるチェックが2つあります。まず、インポートした証明書の中からeMudhraまたはemSign発行者を探します。ACMはリージョンごとであり、CloudFrontで使用される証明書はus-east-1に保存されているため、使用するすべてのリージョン(us-east-1を含む)でこれを実行してください。
for arn in $(aws acm list-certificates \
--includes keyTypes=RSA_2048,RSA_3072,RSA_4096,EC_prime256v1,EC_secp384r1,EC_secp521r1 \
--query "CertificateSummaryList[].CertificateArn" --output text); do
aws acm describe-certificate --certificate-arn "$arn" \
--query "Certificate.[DomainName, Issuer, Type]" --output text
done | grep -i -E "emudhra|emsign"
Azureでは、Resource Graphはサブスクリプション全体でApp Service証明書をカバーします。
az graph query -q "
resources
| where type =~ 'microsoft.web/certificates'
| where tostring(properties.issuer) contains 'emSign' or tostring(properties.issuer) contains 'eMudhra'
| project name, resourceGroup, subscriptionId, issuer = tostring(properties.issuer), expires = properties.expirationDate
" -o table
第二に、独自のトラストストアを持つソフトウェアは、そのストアが更新されるまでCAを信頼し続けます。古いAMI、ベースイメージ、および独自のCAリストをバンドルするランタイムは、ブロックされたチェーンがブラウザがドロップした後も長く機能し続ける場所です。
スキャナーとコンソールが停止する場所
Amazon InspectorとDefender for Cloudは、特定のマシン上のパッケージバージョンが脆弱かどうかを教えてくれるのに役立ちます。AWSおよびAzureコンソールは、1つのリージョン内の1つのアカウントに何があるかを教えてくれるのに役立ちます。どちらも、アドバイザリが着弾した朝に必要なものではありません。
最初の問題はカバレッジです。スキャナーは、有効化されているアカウント、サブスクリプション、マシンを認識しますが、買収されたGitLabサーバーは通常、誰もオンボードしていないアカウントに存在します。2番目は所有権です。検出結果は共有キューに表示され、誰も他人のサーバーを再起動する権限がないため、そこに留まります。所有者が添付されていない検出結果は、単なるアラートの1つにすぎません。3番目はコンテキストです。スキャナーはパッケージが悪質であると述べています。ホストの役割がアカウント内のすべてのS3バケットを読み取れること、または3つの異なるチームのサービスがそれに呼び出しを行っていることは述べていません。LiteLLMの場合、そのコンテキストがインシデントの大部分でした。
これらのギャップを埋めるのは、最新の状態を維持し、何が存在するか、誰がそれを所有しているか、そしてそれが他のすべてとどのように接続されているかを記録するインベントリです。それが、Cloud Sleuthが目指しているものです。
Cloud Sleuthでの様子
Cloud Sleuthは、AWSおよびAzureのリソースをマッピングし、タグ付けされているかどうかにかかわらず、各リソースに所有者を割り当て、両方を最新の状態に保ちます。ここでは、Cloud Sleuthが回答するように設計されている方法でGitLabの質問をしています。
図解例 - コマンドと出力は最終版ではありません:
$ sleuth ask "gitlab servers the internet can reach, and who owns them"
ec2 scm-legacy-01 account acquired-prod · eu-west-1
image GitLab CE 13.12 AMI, launched 2021-06-02
exposure internet → alb-scm (public) → target group scm-https → :443
owner team-devex confidence 88%
created by role/terraform-ci (CloudTrail, 2021-06-02)
managed by github.com/acme/legacy-infra (terraform state)
role scm-legacy-instance → read on 4 S3 buckets, 2 secrets
findings no owner tag · public ingress
この1つの結果は、露出、所有権、到達可能性をカバーしています。サーバー、インターネットからそこへのパス、証拠とともに連絡すべきチーム、そしてサーバーの役割が何にアクセスできるかです。これがなければ、4つのコンソールとCloudTrail検索を行うことになり、アカウントごとに、買収されたアカウントも含めて、それを再度行うことになります。
インベントリ検索は、AWSとAzureのリソースを単一のスキーマに配置し、SQLまたはプレーンイングリッシュでクエリできるようにします。そのため、上記のリージョンごとのLambdaループは、両方のクラウドにわたって所有者が既に添付された単一のクエリになります。
図解例 - スキーマは最終版ではありません:
select cloud, account, type, name, runtime, owner
from resources
where runtime in ('nodejs18.x', 'nodejs20.x')
order by owner;
クラウドマップは、実際のアカウントからネットワークパス、ロードバランサーターゲット、アタッチメント、IAM関係を描画します。これにより、忘れられたサーバーの前に何があるか、そしてローテーションを開始する前にその役割が何にアクセスできるかを確認できます。所有権モデルは、CloudTrailとAzureアクティビティログ、IaC状態、名前付け、アカウント構造から所有者を推測し、信頼度スコアとそれを裏付ける証拠を示します。ガードレールは、所有者不明、タグなし、孤立したリソースをフラグ付けし、Cloud Sleuthでは、カスタムポリシー(例:「非推奨ランタイム上の関数なし」)を継続的に実行しながら、SlackまたはJiraの所有チームにルーティングできます。読み取り専用のMCPサーバーもあり、インシデント中にClaudeやCursorのようなエージェントに「Node.js 20を実行している関数はどれで、誰が所有しているか?」と質問できます。
今週は、顧客が保存していたキーをベンダーコンソールが漏洩し、人気のパッケージが認証情報スティラーを配布したことを考えると、信頼モデルは重要です。Cloud Sleuth CLIはデフォルトで読み取り専用であり、サーバーに何もインストールせず、マシンまたはVPCで実行されます。試してみたい場合は、早期アクセスリストに参加してください。
1時間のドリル
今週のアドバイザリの1つを選び、チームが以下のタスクを実行するのにかかる時間を計ってください。Node.jsとPHPが実行されているすべての場所(Lambdaランタイム、App Serviceスタック、コンテナイメージ、VMを含む)をリストアップします。セルフマネージドGitLabおよびその他のセルフホスト型開発者ツールをすべて見つけ、インターネットからアクセス可能かどうかを確認します。それぞれに所有者の名前を付けます。LiteLLM 1.107.0をインストールした環境については、それが実行されていた役割と、それらの役割がアクセスできたすべてをローテーションしたものをリストアップします。AIプロバイダーキーがどこに保存されており、どのワークロードがそれを使用しているかを書き留めます。eMudhraまたはemSign発行者を検索する証明書を検索します。
それが1時間以上かかった場合、次回のインシデントも同様にかかると予想してください。
よくある質問
AWS Lambda関数はNode.js Brotli脆弱性の影響を受けましたか?
node:zlibを使用して信頼できないソースからのBrotliデータを解凍するコードのみが影響を受けます。Lambdaは、サポートされているマネージドランタイムにデフォルトで自動的にランタイムアップデートをロールアウトします。関数のログのINIT_START行は、実行中のランタイムバージョンを示します。非推奨のnodejs20.xランタイム上の関数はもはやセキュリティパッチを受け取らず、コンテナイメージ関数はイメージに含まれるNode.jsバージョンを使用するため、それらをアップグレードまたは再構築してください。
GitLab.comはCVE-2026-110264の影響を受けますか?
GitLabはGitLab.comとGitLab Dedicated自体をパッチしており、リリースのカバレッジによると、どちらも既に修正済みバージョンを実行していました。セルフマネージドのGitLab CommunityおよびEnterprise Editionインスタンスは、パッチ適用済みリリースにアップグレードする必要があります。回避策はありません。
環境がLiteLLM 1.107.0をインストールした場合、どうすればよいですか?
侵害されたものとして扱ってください。クリーンなマシンから調査し、検証済みのバージョンから再構築し、Pythonライブラリパスからcook.pth、cook.py、sitecustomize.pyが削除されていることを確認し、環境がアクセスできたすべての認証情報(クラウドロール、CI/CDシークレット、SSHキー、モデルプロバイダーキー)をローテーションしてください。Microsoftの分析には、検索すべきインジケーターがリストされています。
新しい脆弱性が発表されたとき、Cloud Sleuthはどのように役立ちますか?
対応速度を決定する質問に答えます。AWSおよびAzureのリソースのうち、どのリソースが対象となるか、インターネットからアクセス可能なものはどれか、それぞれ誰が所有しているか、そしてそれぞれが何にアクセスできるかです。所有者にはその証拠が添付されているため、修正は実際に行えるチームに渡されます。CLIはマシン上で読み取り専用で実行され、早期アクセスリストに参加できます。