
TeamCity एक Continuous Integration और Deployment सर्वर है जो आउट-ऑफ-द-बॉक्स निरंतर यूनिट परीक्षण, कोड गुणवत्ता विश्लेषण, और बिल्ड समस्याओं पर शीघ्र रिपोर्टिंग प्रदान करता है।
यह भेद्यता एक लाइब्रेरी में दिखाई देती है जो हमलावरों को बिना प्रमाणीकरण के किसी भी एंडपॉइंट तक पहुंचने की अनुमति देती है।
भेद्य कोड web-openapi.jar लाइब्रेरी में है, जो directory to the lib में स्थित है।
क्लास jetbrains.buildServer.controllers.BaseController अनुरोधों और प्रतिक्रियाओं को संभालने के लिए जिम्मेदार है, लेकिन इसे ठीक से लागू नहीं किया गया है। आइए देखें कि कोड कैसा दिखता है:
public abstract class BaseController extends AbstractController {
//////
public final ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
ModelAndView modelAndView = this.doHandle(request, response);
if (modelAndView != null) {
if (modelAndView.getView() instanceof RedirectView) {
modelAndView.getModel().clear();
} else {
this.updateViewIfRequestHasJspParameter(request, modelAndView);
}
}
ModelAndView विधि का मुख्य उद्देश्य अनुरोधित पृष्ठ को UI पर प्रस्तुत करना है। यहीं से बग शुरू होता है। ध्यान दें कि updateViewIfRequestHasJspParameter तब कॉल किया जाएगा जब हमारा अनुरोध पुनर्निर्देशित नहीं हो रहा हो। भेद्यता के मूल कारण का पता लगाने के लिए, हमें गहराई से जांच करने की आवश्यकता है।
private void updateViewIfRequestHasJspParameter(@NotNull HttpServletRequest request, @NotNull ModelAndView modelAndView) {
boolean isControllerRequestWithViewName = modelAndView.getViewName() != null && !request.getServletPath().endsWith(".jsp");
String jspFromRequest = this.getJspFromRequest(request);
if (isControllerRequestWithViewName && StringUtil.isNotEmpty(jspFromRequest) && !modelAndView.getViewName().equals(jspFromRequest)) {
modelAndView.setViewName(jspFromRequest);
}
}
यह विधि ModelAndView ऑब्जेक्ट के व्यू नाम को तब अपडेट करती है जब वह विशेष शर्तों को पूरा करता है। isControllerRequestWithViewName तब True होगा यदि modelAndView ऑब्जेक्ट के पास एक नाम है और url में path .jsp के साथ समाप्त नहीं होता है। उदाहरण के लिए, इन शर्तों को पूरा करने वाला url है http://localhost:8111/random_string और एक अमान्य url http://localhost:8111/admin/admin.html या http://localhost:8111/admin.jsp होगा। जैसा कि ऊपर चर्चा की गई है, हमें ऐसी चीज़ तक नहीं पहुंचना चाहिए जो पुनर्निर्देशन को ट्रिगर करती है, आमतौर पर वे पृष्ठ जिन्हें प्राधिकरण की आवश्यकता होती है। प्रोग्राम फिर jspFromRequest नामक एक वेरिएबल निर्दिष्ट करेगा जो getJspFromRequest() विधि को कॉल करेगा। अब उस विधि पर चलते हैं, मैं बाकी कोड बाद में समझाऊंगा। एक if स्टेटमेंट है जो जांच करेगा कि सत्य है, विधि के कॉल का परिणाम खाली नहीं है और ऑब्जेक्ट का नाम उस पृष्ठ के बराबर नहीं होना चाहिए जिसे हम अनुरोध के माध्यम से
protected String getJspFromRequest(@NotNull HttpServletRequest request) { String jspFromRequest = request.getParameter("jsp"); return jspFromRequest == null || jspFromRequest.endsWith(".jsp") && !jspFromRequest.contains("admin/") ? jspFromRequest : null; }
यह फ़ंक्शन पहले jsp नामक अनुरोध पैरामीटर का मान प्राप्त करेगा। जांच सुनिश्चित करती है कि jsp .jsp के साथ समाप्त होना चाहिए और इसमें /admin नहीं होना चाहिए। ऊपर दिए गए if स्टेटमेंट के साथ जोड़ने पर:
if (isControllerRequestWithViewName && StringUtil.isNotEmpty(jspFromRequest) && !modelAndView.getViewName().equals(jspFromRequest)) {
modelAndView.setViewName(jspFromRequest);
}
हमें इस बात की समग्र तस्वीर मिल जाएगी कि URL कैसा दिखता है:
.jsp शामिल करना चाहिए।jsp अनुरोध पैरामीटर path के बराबर नहीं होना चाहिए। उदाहरण के लिए, /random?jsp=/random अमान्य होगा।jsp का अंत .jsp के साथ होना चाहिए।TeamCity बाहरी अनुप्रयोगों को एकीकृत करने और TeamCity सर्वर के साथ स्क्रिप्ट इंटरैक्शन बनाने के लिए एक REST API प्रदान करता है। यह URL पथों के माध्यम से संसाधनों तक पहुंचने की अनुमति देता है। आप REST API के साथ काम करना शुरू कर सकते हैं, अपने ब्राउज़र में
http://<TeamCity Server host>:<port>/app/rest/serverURL खोलकर: यह पृष्ठ API को एक्सप्लोर करने के लिए कई संकेत देता है।
TeamCity एक REST API प्रदान करता है जो हमें प्रमाणीकरण बायपास करने पर संवेदनशील संसाधनों तक पहुंचने की अनुमति देता है। इस मामले में, उदाहरण के लिए, हम /app/rest/server तक पहुंचने का प्रयास करेंगे।

हमेशा की तरह, जब हम /app/rest/server का अनुरोध करते हैं तो सर्वर हमें /login.html पर पुनर्निर्देशित करेगा। आइए इस प्रतिबंध को बायपास करने के लिए एक सटीक URL बनाएं। सबसे पहले, हमारा पथ कुछ भी हो सकता है जब तक वह 404 स्टेटस कोड या यहां तक कि 200 लौटाता है, जैसे login.html। अगला, हम /app/rest/server का अनुरोध करने के लिए jsp का उपयोग करेंगे, लेकिन इसका अंत .jsp के साथ होना चाहिए। इस स्थिति में, इस जांच को बायपास करने के लिए हम 2 तरकीबें भी इस्तेमाल कर सकते हैं। हम पैरामीटर विभाजक के रूप में अर्धविराम ; का उपयोग कर सकते हैं: /app/rest/server;.jsp, इस बार .jsp को दूसरे पैरामीटर के रूप में माना जाएगा। दूसरा बायपास URI फ्रैगमेंट # का उपयोग करना है: /app/rest/server%23.jsp. URI फ्रैगमेंट के बाद जो कुछ भी आता है उसका उपयोग रूटिंग के लिए नहीं किया जाएगा, यह केवल पृष्ठ में नेविगेशन के लिए उपयोग किया जाता है। ध्यान दें कि कैरेक्टर को URL-एन्कोडेड होना आवश्यक है, अन्यथा ब्राउज़र इसे पहले अनदेखा कर देगा।

इस तकनीक से, हम एडमिन विशेषाधिकार के साथ नया उपयोगकर्ता भी बना सकते हैं। TeamCity दस्तावेज़ में कहा गया है कि हम इस एंडपॉइंट /app/rest/users के माध्यम से नया उपयोगकर्ता बना सकते हैं।

आइए एडमिन पैनल पर जाकर जांचें कि कोई नया उपयोगकर्ता बनाया गया है या नहीं।

जैसी उम्मीद थी, एडमिन विशेषाधिकार वाला एक नया उपयोगकर्ता बनाया गया है। एडमिन विशेषाधिकार के साथ, हम सर्वर को पूरी तरह से नियंत्रित कर सकते हैं। हालाँकि, हम रिमोट कोड निष्पादन प्राप्त करके इससे भी आगे जा सकते हैं। यह CVE 2023.11.4 से पहले के सभी संस्करणों को प्रभावित करता है, लेकिन यह RCE केवल 2023.11 से पहले के संस्करण में संभव है। एक अनप्रलेखित एंडपॉइंट /app/rest/debug/processes है जो एडमिन विशेषाधिकार वाले उपयोगकर्ता को मनमाना कमांड निष्पादित करने की अनुमति देता है। हम अनुरोध URL में 2 अनुरोध पैरामीटर के साथ एक POST अनुरोध भेजेंगे। Windows के लिए, यह ?exePath=cmd.exe¶ms=/c%20[our command here] होगा और Linux के साथ यह ?exePath=/bin/sh¶ms=-c%20[our command here] होगा। मैं Windows पर TeamCity चला रहा हूँ, इसलिए पूरा पथ /app/rest/debug/processes?exePath=cmd.exe¶ms=/c%20whoami होगा।

सर्वर 403 त्रुटि लौटाता है जिसमें कहा गया है कि अनुरोध में csrf token का अभाव है। TeamCity दस्तावेज़ हमें टोकन प्राप्त करने के लिए एंडपॉइंट भी प्रदान करता है, जो /authenticationTest.html?csrf है।
टोकन प्राप्त करने के बाद, हम इसे अनुरोध हेडर X-TC-CSRF-Token या tc-csrf-token HTTP पैरामीटर में जोड़ देंगे, यहाँ मैं X-TC-CSRF-Token चुनता हूँ।

csrf token प्रदान करने के बाद, हम सफलतापूर्वक कमांड को रिमोट से निष्पादित करते हैं, जिससे हमें सर्वर पर पूर्ण नियंत्रण मिल जाता है।
इस CVE में, सुरक्षा दोष इनपुट सत्यापन में नहीं था, बल्कि कोड के तर्क में था। यह एप्लिकेशन वास्तव में इतना जटिल है कि डेवलपर्स किसी न किसी तरह गलतियाँ कर ही देते हैं। हमने प्रमाणीकरण बायपास करने की एक नई तकनीक भी सीखी। मुझे उम्मीद है कि आप इस विश्लेषण से कुछ उपयोगी सीखेंगे। हैप्पी हैकिंग!!!
isControllerRequestWithViewName getJspFromRequest()modelAndView