
HackTheBox "Nexus" privilege escalation के लिए PoC और write-up: root द्वारा चलाई जाने वाली Gitea template-sync सेवा, जो forged Git objects के माध्यम से path traversal के प्रति vulnerable है। केवल शैक्षिक उपयोग के लिए।
नकली Git ऑब्जेक्ट्स के माध्यम से Gitea template-sync पथ ट्रैवर्सल → root
HackTheBox मशीन Nexus के लिए एक proof-of-concept विशेषाधिकार-उन्नयन एक्सप्लॉइट।
एक root-स्वामित्व वाली systemd सेवा Gitea template रिपॉज़िटरीज़ को डिस्क पर
सिंक करती है, बिना एंट्री नामों को सैनिटाइज़ किए, जिससे एक प्रमाणित निम्न-विशेषाधिकार
उपयोगकर्ता डायरेक्टरी ट्रैवर्सल के माध्यम से root के रूप में मनमानी फ़ाइलें लिख सकता है —
यहाँ इसका उपयोग /root/.ssh/authorized_keys में एक SSH कुंजी प्रत्यारोपित करने के लिए किया गया है।
यह कोड केवल शैक्षिक उद्देश्यों और अधिकृत सुरक्षा परीक्षण के लिए प्रदान किया गया है — अर्थात् HackTheBox जैसे लैब वातावरण, या वे सिस्टम जिनके आप स्वामी हैं या जिनका परीक्षण करने के लिए आपके पास स्पष्ट लिखित अनुमति है। उन सिस्टमों के विरुद्ध अनधिकृत उपयोग जिन्हें आप नियंत्रित नहीं करते, अवैध है। दुरुपयोग के लिए लेखक कोई दायित्व नहीं लेता।
यदि Nexus मशीन HackTheBox पर अभी भी सक्रिय है, तो इस एक्सप्लॉइट को प्रकाशित या साझा न करें — HTB नियम सक्रिय मशीनों के समाधान वितरित करने से मना करते हैं।
| वर्ग | पथ ट्रैवर्सल (CWE-22) → स्थानीय विशेषाधिकार उन्नयन |
| घटक | gitea-template-sync.service (कस्टम /etc/gitea/template-sync.py) |
| इस रूप में चलता है | root, हर ~60s में gitea-template-sync.timer द्वारा ट्रिगर होता है |
| मूल कारण | Git tree ऑब्जेक्ट्स से लिए गए असैनिटाइज़्ड नामों पर os.path.join() |
| प्रभाव | प्रमाणित निम्न-विशेषाधिकार उपयोगकर्ता → root |
सिंक्रोनाइज़ेशन स्क्रिप्ट Gitea में template के रूप में चिह्नित रिपॉज़िटरीज़ से फ़ाइलें पढ़ती है और उन्हें स्थानीय फ़ाइलसिस्टम पर लिखती है। गंतव्य पथ लगभग इस प्रकार बनाया जाता है:
dest = os.path.join(base_dir, name) # name comes straight from the Git tree
open(dest, "w").write(content)
os.path.join() .. अनुक्रमों को निष्क्रिय नहीं करता। इसलिए एक tree एंट्री जिसका नाम
एक ट्रैवर्सल पथ है (जैसे ../../../../../root/.ssh/authorized_keys), विशेषाधिकार प्राप्त प्रक्रिया को
इच्छित डायरेक्टरी के बाहर लिखने पर मजबूर कर देती है।
Git सामग्री और नामों को अलग-अलग संग्रहीत करता है:
name → object को मैप करता है (यहीं फ़ाइलनाम रहता है);एक tree एंट्री में फ़ाइलनाम केवल टेक्स्ट है, इसलिए यह .. हो सकता है — लेकिन मानक
Git क्लाइंट ऐसी एंट्री बनाने से इनकार करता है, ठीक इसी हमले को रोकने के लिए।
यह PoC उस क्लाइंट-साइड जाँच को बायपास करता है, कच्चे Git ऑब्जेक्ट्स को सीधे
.git/objects/ में लिखकर (SHA-1 आईडी की हाथ से गणना करके) और refs/heads/main को
मैन्युअल रूप से तैयार करके। फिर पुश की गई रिपॉज़िटरी में .. नाम की एक tree एंट्री होती है,
जिसे root सिंक प्रक्रिया हल करती है और अनुसरण करती है।
www-data के रूप में RCE।.env में सादा-पाठ क्रेडेंशियल्स, सिस्टम पासवर्ड के रूप में
पुनः उपयोग किए गए → निम्न-विशेषाधिकार उपयोगकर्ता के रूप में SSH।root।तीनों चरणों में आवर्ती विषय: एक विशेषाधिकार प्राप्त प्रक्रिया जो हमलावर-नियंत्रित डेटा पर बिना सत्यापन भरोसा करती है (एक स्पूफ़ किया गया MIME प्रकार, एक पुनः उपयोग किया गया पासवर्ड, एक असैनिटाइज़्ड फ़ाइलनाम)।
http://localhost:3000 पर पहुँच योग्य।git, python3, curl, और ssh उपलब्ध (बॉक्स पर डिफ़ॉल्ट)।chmod +x nexus_privesc.sh
./nexus_privesc.sh -t <TARGET_IP> -u <GITEA_USER> -p <GITEA_PASS> [-d DEPTH]
उदाहरण:
./nexus_privesc.sh -t 10.129.21.192 -u jones -p 'REDACTED' -d 5
/tmp/.exploit_key)।.. ट्रैवर्सल tree को एन्कोड करते हैं
जो /root/.ssh/authorized_keys को मैप करता है, सामग्री के रूप में हमलावर की सार्वजनिक कुंजी के साथ।root के रूप में लॉगिन करती है।लक्ष्य पर सिंक की निगरानी करें:
tail -f /var/log/template-sync.log
एक सफल रन इस प्रकार की पंक्ति लॉग करता है:
synced: ../../../../../root/.ssh/authorized_keys
DEPTH को ट्यून करनाDEPTH सिंक/स्टेजिंग डायरेक्टरी से फ़ाइलसिस्टम रूट (/) तक चढ़ने के लिए आवश्यक
.. स्तरों की संख्या है। यदि लॉग में सिंक सफल होता है लेकिन root SSH विफल हो जाता है,
तो ट्रैवर्सल गलत स्तर पर पहुँचा — -d 4 या -d 6 के साथ पुनः प्रयास करें। सटीक मान
/etc/gitea/template-sync.py में स्टेजिंग पथ को पढ़कर पुष्ट किया जा सकता है।
कभी भी अविश्वसनीय नामों से केवल os.path.join() के साथ फ़ाइलसिस्टम पथ न बनाएँ।
पथों को सामान्यीकृत और सीमित करें, जैसे:
base = os.path.realpath(base_dir)
dest = os.path.realpath(os.path.join(base, name))
if not dest.startswith(base + os.sep):
raise ValueError("path traversal blocked")
.., निरपेक्ष पथ, या पथ विभाजक वाले tree एंट्री नामों को अस्वीकार करें।
सिंक सेवा को root के बजाय एक समर्पित निम्न-विशेषाधिकार खाते के अंतर्गत चलाएँ।
विकृत पुश किए गए ऑब्जेक्ट्स को अस्वीकार करने के लिए Gitea में receive.fsckObjects = true सक्षम करें।
<your name / handle> — HackTheBox पर शैक्षिक उपयोग के लिए।
| फ़्लैग | विवरण | डिफ़ॉल्ट |
|---|
-t | लक्ष्य IP (अंतिम root SSH के लिए) | — (आवश्यक) |
-u | Gitea उपयोगकर्ता नाम | — (आवश्यक) |
-p | Gitea पासवर्ड | — (आवश्यक) |
-d | सिंक डायरेक्टरी से / तक पहुँचने के लिए .. स्तरों की संख्या | 5 |