FlawOpen

flawopen.com/Teardowns/cve-2026-76504-cisco-sdwan-api-authentication-bypass

● CVE-2026-76504 · CVSS 9.8[1] · 緊急
セキュリティ研究 · FlawOpen

CVE-2026-76504:Cisco SD-WAN Manager の認証バイパス

CVE-2026-76504 は、未認証のリモート攻撃者が Cisco Catalyst SD-WAN Manager の API 認証ルールを回避し、管理者権限で API にアクセスできる脆弱性です。Cisco は悪用を確認しています。該当ブランチの修正版へ更新し、安全と判断する前に Manager を調査してください。[1], [5]

CVSS ベクトルCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H[1]
💡 わかりやすい解説 (ELI5)

住所の表記を一種類しか認識しない入退室ゲートを想像してください。攻撃者が URI エンコードで表記を変えると、システムの一部は同じドアだと判断する一方、セキュリティルールは別の表記として扱います。Cisco によると、この食い違いによって認証を回避し、管理者として API にアクセスできました。修正では住所を一貫して処理し、同じアクセスルールを適用する必要があります。運用上の正式な対処は Cisco が提供するソフトウェア更新です。[1]

主要な概念と専門用語

URI エンコード
URL パス内の文字をパーセントエンコードしたバイト列で表す方法。すべての処理層で一貫してデコードし、比較する必要があります。[1], [7]
認証バイパス
システムが必要な方法で呼び出し元の身元を確認しないまま、リクエストが保護された機能へ到達することです。[1]
API セッションルール
API リクエストがエンドポイントに到達する前に、有効な認証済みセッションを持つか確認する Manager のルールです。[1]
CWE-177
URL エンコードの不適切な処理。エンコードされた URL データの処理が一貫しないと、セキュリティ判断を回避される可能性があります。Cisco がこの CVE に割り当てた弱点分類です。[1], [7]

フレームワーク対応

根本原因の分析 (Root Cause)

Cisco は CVE-2026-76504 の原因を、Catalyst SD-WAN Manager の API セッション認証における HTTP リクエストの URI エンコード処理の不備と説明しています。細工されたリクエストにより、特定の API エンドポイントを制限する認証ルールを回避し、管理者としてアクセスできる可能性があります。Cisco は CVSS 3.1 で 9.8(Critical)と評価し、システム構成にかかわらず製品が影響を受けるとし、PSIRT が 2026 年 9 月に実際の悪用を把握したと報告しています。アドバイザリは URI エンコードされた j_security_check リクエストを IOC の例として示し、URI エンコードされた任意の 1 文字が利用可能と明記しています。この例だけを完全な検知シグネチャと見なさないでください。Cisco は脆弱なソースコードやパッチ差分を公開していないため、以下のコード比較は認可設計の説明例であり、Cisco のコードや再現コードではありません。Cisco はブランチごとの修正版を示し、完全に解決する回避策はないとしています。ネットワーク制限と Live Protect シールドは一時的な緩和策で、シールドは正規ログインに影響する場合があり、更新の代わりにはなりません。[1], [5]

CVE-2026-76504 の修正バージョン(デプロイ形態別)

デプロイ / 系列修正バージョン必要な対応
20.9 より前のオンプレミス版修正済みリリースが公開されたサポート対象の系列へ移行する。移行し、古いサポート対象外の系列を使い続けない。
オンプレミス 20.9 系列20.9.10.1各 Manager をこのバージョン、またはサポート対象系列のそれ以降へ更新する。
オンプレミス 20.12 系列20.12.8.2各 Manager をこのバージョン、またはサポート対象系列のそれ以降へ更新する。
オンプレミス 20.15 系列20.15.6.1各 Manager をこのバージョン、またはサポート対象系列のそれ以降へ更新する。
オンプレミス 20.18 系列20.18.4.1各 Manager をこのバージョン、またはサポート対象系列のそれ以降へ更新する。
オンプレミス 26.1 系列26.1.2.1各 Manager をこのバージョン、またはサポート対象系列のそれ以降へ更新する。
オンプレミス 26.2 系列26.2.1各 Manager をこのバージョン、またはサポート対象系列のそれ以降へ更新する。
Cisco SD-WAN Cloud (Cisco Managed)20.15.605 以降。Cisco はホスト型サービスを修正済みと説明しています。マネージドサービスであることを確認する。この更新では顧客側の対応は不要です。

ステップ・バイ・ステップの攻撃フロー

ステップ 1

リモートリクエストが Manager API に到達する

アカウントやユーザー操作は不要です。インターネットに公開するとリスクが高まり、ベンダーは構成にかかわらず影響すると説明しています。[1]

ステップ 2

URI エンコードされたパスが認証ルールを回避する

Cisco は API セッション認証における URI エンコード処理の不一致を指摘しています。公開資料に脆弱な実装の詳細はありません。[1]

ステップ 3

API が管理者リクエストとして処理する

悪用に成功すると管理者権限を得ます。Cisco は悪用を確認していますが、アクセス後の完全な攻撃チェーンは公開していません。[1], [5]

ステップ 4

更新して調査する

admin-tech ファイルを収集してから、すべての Manager を適切な修正版へ更新し、その後 Cisco TAC に証拠の確認を依頼します。TAC の回答を待って更新を遅らせないでください。[1], [2]

ソースコード比較:脆弱 vs 堅牢化

Cisco は脆弱なソースコードやパッチ差分を公開していません。この Python は一般的な設計原則の例で、Cisco のコードではありません。ルーティングと認可には検証済みの同一の正規化リクエスト対象を使い、保護されたエンドポイントには認証済み主体を必須にします。router/framework が文書化する正規化仕様に従い、独自のデコードや二重デコードを追加しないでください。CVE-2026-76504 の正式な修正は、Cisco がブランチごとに示す修正版への更新です。[1], [7]

ソース抜粋: URI 正規化と認可境界の Python 説明例。Cisco のソースコードではありません

脆弱な実装
from urllib.parse import unquote, urlsplit


def dispatch(raw_target, session, routes):
    # あるレイヤーは、まだエンコードされたパスを検査する。
    path_for_auth = urlsplit(raw_target).path
    if path_for_auth.startswith("/api/") and session is None:
        raise PermissionError("authentication required")

    # 後段のレイヤーはルート検索前にデコードするため、2つの判定が食い違うことがある。
    route_path = unquote(path_for_auth)
    return routes[route_path]()
堅牢化されたセキュアパッチ
import re
from urllib.parse import unquote, urlsplit


def canonical_path(raw_target):
    raw_path = urlsplit(raw_target).path
    if re.search(r"%(?![0-9A-Fa-f]{2})", raw_path):
        raise ValueError("malformed URI encoding")

    path = unquote(raw_path, errors="strict")
    if "%" in path or "\\" in path:
        raise ValueError("ambiguous path encoding")
    if any(part in {".", ".."} for part in path.split("/")):
        raise ValueError("path traversal segment")
    return path


def dispatch(raw_target, session, routes):
    path = canonical_path(raw_target)  # Decode and validate exactly once.
    route = routes.get(path)
    if route is None:
        raise LookupError("route not found")

    requires_authentication, handler = route
    if requires_authentication and session is None:
        raise PermissionError("authentication required")
    return handler()

エンジニアリング&システム堅牢化チェックリスト

参考資料