
# CVE-2023-25813 का प्रूफ-ऑफ-कॉन्सेप्ट और तकनीकी विश्लेषण Sequelize ORM के 6.19.1 से पहले के संस्करणों में SQL इंजेक्शन भेद्यता, जिसमें शोषण विवरण और पैच जानकारी शामिल है।
replacements विकल्प के माध्यम से पारित इनपुट मान उचित रूप से एस्केप नहीं किए जाते हैं, जिससे विशिष्ट क्वेरी कॉन्फ़िगरेशन के आधार पर SQL इंजेक्शन हो सकता है।Sequelize Node.js में उपयोग किया जाने वाला ORM (Object-Relational Mapping) लाइब्रेरी में से एक है। ORM एक तकनीक है जो ऑब्जेक्ट और रिलेशनल डेटाबेस के बीच स्वचालित रूपांतरण को स्वचालित करती है, जिससे SQL क्वेरी सीधे लिखे बिना ऑब्जेक्ट के माध्यम से डेटाबेस रिकॉर्ड को खोजा, बनाया, संशोधित और हटाया जा सकता है।
// SELECT * FROM users WHERE id = 1;
const user = await User.findByPk(1);
replacements Sequelize में रॉ SQL क्वेरी या ORM विधियों का उपयोग करते समय, उपयोगकर्ता इनपुट मानों को SQL में सुरक्षित रूप से सम्मिलित करने के लिए एक बाइंडिंग तंत्र है। इसका उपयोग SQL इंजेक्शन को रोकने के लिए एक प्रमुख साधन के रूप में किया जाता है।
// स्थिति-आधारित बाइंडिंग
await sequelize.query('SELECT * FROM projects WHERE status = ?', {
replacements: ['active'],
});
// कुंजी-आधारित बाइंडिंग
await sequelize.query(
'SELECT * FROM users WHERE name = :name AND age = :age',
{
replacements: {
name: 'Alice',
age: 25
}
}
);
Sequelize उपयोगकर्ता इनपुट मानों को SQL में सुरक्षित रूप से बाइंड करने के लिए replacements विकल्प प्रदान करता है।
हालाँकि, Sequelize 6.19.1 से पहले के संस्करणों में, ORM विधियों का उपयोग करते समय, replacements के माध्यम से बाइंड करने के बावजूद, आंतरिक SQL निर्माण और प्रतिस्थापन क्रम की समस्या के कारण SQL इंजेक्शन हो सकता है।
नीचे एक Sequelize कोड उदाहरण है जिसमें कमजोरी होती है। literal और replacements के संयोजन का उपयोग करने की स्थिति में SQL इंजेक्शन हो सकता है।
User.findAll({
where: or(
literal('soundex("firstName") = soundex(:firstName)'),
{ lastName: lastName },
),
replacements: { firstName },
})
हमलावर replacements में निम्नलिखित इनपुट मान इंजेक्ट करके क्वेरी संरचना को तोड़ सकता है।
{
"firstName": "OR true; DROP TABLE users;",
"lastName": ":firstName"
}
replacements कुंजी (:firstName) को मान में फिर से सम्मिलित करके, बाइंडिंग कुंजी को एक बार और जोड़ने की संरचना है।
SELECT * FROM users
WHERE soundex("firstName") = soundex(:firstName)
OR "lastName" = ':firstName'
अंतिम पूर्ण क्वेरी नीचे दी गई है। हालाँकि, यह क्वेरी संरचनात्मक रूप से स्पष्ट रूप से यह निर्धारित करना कठिन बनाती है कि इसे निष्पादित किया जाएगा या नहीं, और SQL पार्सर की व्याख्या और DB सेटिंग्स के आधार पर निष्पादन परिणाम भिन्न हो सकता है। कुछ वातावरणों में इसे एक सामान्य सिंटैक्स त्रुटि के रूप में माना जाता है, लेकिन सेटिंग्स के आधार पर, यह एक खतरनाक संरचना है जो अनपेक्षित SQL कमांड को निष्पादित कर सकती है।
SELECT * FROM users
WHERE soundex("firstName") = soundex('OR true; DROP TABLE users;')
OR "lastName" = ''OR true; DROP TABLE users;''
यदि SQL पार्सर मल्टी-क्वेरी की अनुमति देने वाले वातावरण में है, तो एक बाइंडिंग से तालिका को हटाया जा सकता है।

Sequelize literal फ़ंक्शन में डाले गए स्ट्रिंग को एक विश्वसनीय SQL खंड मानता है और इसे एस्केप नहीं करता है। इसलिए, वह स्ट्रिंग SQL क्वेरी में ज्यों की त्यों डाली जाती है, और यदि उसमें :param रहता है तो भी इसे बिना किसी प्रसंस्करण के छोड़ दिया जाता है।
जैसा कि नीचे दिखाया गया है, replacements एक ऐसा तरीका है जो SQL के पूरी तरह से स्ट्रिंग के रूप में इकट्ठा होने के बाद, निष्पादन से ठीक पहले बचे हुए :param टोकन को ढूंढकर बदलता है। इसलिए, पहले से निर्धारित संरचना वाले SQL के अंदर उपयोगकर्ता इनपुट सीधे डाला जाता है, जिससे पूरी क्वेरी की संरचना टूट जाती है और कमजोरी उत्पन्न होती है।
// sequelize-6.19.0/src/sequelize.js
if (options.replacements) {
if (Array.isArray(options.replacements)) {
sql = Utils.format([sql].concat(options.replacements), this.options.dialect);
} else {
sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect);
}
}
injectReplacements को परिभाषित करके, केवल सिंटैक्टिक रूप से सुरक्षित स्थानों पर बाइंडिंग की अनुमति देने के लिए बदल दिया गया है।
// sequelize-6.19.1/src/sequelize.js
if (options.replacements) {
if (Array.isArray(options.replacements)) {
sql = Utils.format([sql].concat(options.replacements), this.options.dialect);
} else {
sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect);
}
sql = injectReplacements(sql, this.dialect, options.replacements);
}