
CVE-2025-61686 का विस्तृत विश्लेषण, जो React Router की फ़ाइल सत्र भंडारण में एक पथ ट्रावर्सल भेद्यता है, जिसमें मूल कारण, हमले के परिदृश्य और कोड ऑडिट निष्कर्ष शामिल हैं।
CVE ID: CVE-2025-61686
प्रभावित संस्करण: @react-router/node 7.0.0 से 7.9.3
भेद्यता प्रकार: पथ ट्रैवर्सल (Path Traversal) / निर्देशिका क्रॉसिंग
यह भेद्यता packages/react-router-node/sessions/fileStorage.ts फ़ाइल में getFile() फ़ंक्शन और संबंधित फ़ाइल संचालन तर्क में मौजूद है।
packages/react-router/lib/server-runtime/sessions.ts की पंक्ति 267 में:
async getSession(cookieHeader, options) {
let id = cookieHeader && (await cookie.parse(cookieHeader, options));
let data = id && (await readData(id));
return createSession(data || {}, id || "");
}
Session ID कुकी से पार्स किया जाता है, cookie.parse() विधि के माध्यम से।
packages/react-router/lib/server-runtime/cookies.ts में decodeCookieValue() फ़ंक्शन:
async function decodeCookieValue(
value: string,
secrets: string[],
): Promise<any> {
if (secrets.length > 0) {
// यदि secrets कॉन्फ़िगर किए गए हैं, तो हस्ताक्षर सत्यापित करेंगे
for (let secret of secrets) {
let unsignedValue = await unsign(value, secret);
if (unsignedValue !== false) {
return decodeData(unsignedValue);
}
}
return null; // हस्ताक्षर सत्यापन विफल होने पर null लौटाएं
}
// यदि secrets कॉन्फ़िगर नहीं किए गए (अहस्ताक्षरित), तो सीधे डिकोडेड मान लौटाएं
return decodeData(value);
}
मुख्य समस्या: जब कुकी अहस्ताक्षरित होती है (secrets खाली सरणी या सेट नहीं है), तो decodeCookieValue सीधे डिकोडेड कुकी मान लौटाता है, हमलावर इस मान को पूरी तरह से नियंत्रित कर सकता है।
packages/react-router-node/sessions/fileStorage.ts में:
export function getFile(dir: string, id: string): string {
// Divide the session id up into a directory (first 2 bytes) and filename
// (remaining 6 bytes) to reduce the chance of having very large directories,
return path.join(dir, id.slice(0, 4), id.slice(4));
}
यह फ़ंक्शन session ID को दो भागों में विभाजित करता है:
id.slice(0, 4)id.slice(4)फिर path.join() का उपयोग करके पथ जोड़ता है।
हमला परिदृश्य: जब createFileSessionStorage() का उपयोग किया जाता है और कुकी अहस्ताक्षरित होती है:
../../etc/passwdgetFile() फ़ंक्शन प्रसंस्करण:
id.slice(0, 4) = ../.id.slice(4) = /etc/passwdpath.join(dir, ../., /etc/passwd)path.join() पथ को सामान्य करता है, लेकिन यदि dir पहले से ही सापेक्ष पथ है या प्रसंस्करण के बाद, फिर भी पथ ट्रैवर्सल संभव हो सकता हैअधिक सटीक दोहन विधि:
....//etc/passwd
id.slice(0, 4) = ....id.slice(4) = //etc/passwd/etc/passwd तक पहुँच हो सकती हैया:
../../../etc/passwd (16 वर्ण)
id.slice(0, 4) = ../.id.slice(4) = ./etc/passwdpath.join() के व्यवहार के साथ मिलकर, यह पथ ट्रैवर्सल का कारण बन सकता हैनिम्नलिखित फ़ाइल संचालन प्रभावित हो सकते हैं:
readData(id) - सत्र डेटा पढ़ते समय
async readData(id) {
try {
let file = getFile(dir, id);
let content = JSON.parse(await fsp.readFile(file, "utf-8"));
// ...
}
}
updateData(id, data, expires) - सत्र डेटा अपडेट करते समय
async updateData(id, data, expires) {
let content = JSON.stringify({ data, expires });
let file = getFile(dir, id);
await fsp.mkdir(path.dirname(file), { recursive: true });
await fsp.writeFile(file, content, "utf-8");
}
deleteData(id) - सत्र डेटा हटाते समय
async deleteData(id) {
try {
await fsp.unlink(getFile(dir, id));
}
}
निम्नलिखित सभी शर्तों को एक साथ पूरा करना होगा:
createFileSessionStorage() विधि का उपयोग करनाcookie कॉन्फ़िगरेशन में secrets सेट नहीं है या secrets खाली सरणी है)getSession(cookieHeader)
→ cookie.parse(cookieHeader)
→ decodeCookieValue(value, secrets) // अहस्ताक्षरित होने पर सीधे value लौटाता है
→ readData(id)
→ getFile(dir, id) // पथ जोड़ना, पथ ट्रैवर्सल जोखिम मौजूद है
→ fsp.readFile(file) / fsp.writeFile(file) / fsp.unlink(file)
getFile() फ़ंक्शन id पैरामीटर को सत्यापित या सामान्य नहीं करता हैpath.join() पथ को सामान्य करता है, लेकिन विशिष्ट परिस्थितियों में (जैसे जोड़ने से पहले पथ में पहले से .. शामिल है) फिर भी पथ ट्रैवर्सल की अनुमति दे सकता है