
Apache Shiro (CVE-2020-17523) के लिए दो प्रमाणीकरण बायपास तकनीकों का विश्लेषण, एक पुनरुत्पादनीय एक्सप्लॉइट वातावरण और विस्तृत मूल कारण जांच के साथ।
Apache Shiro एक शक्तिशाली और उपयोग में आसान Java सुरक्षा ढांचा है, जो प्रमाणीकरण, प्राधिकरण, पासवर्ड और सत्र प्रबंधन करता है। Shiro के समझने योग्य API का उपयोग करके, आप किसी भी एप्लिकेशन, सबसे छोटे मोबाइल एप्लिकेशन से लेकर सबसे बड़े नेटवर्क और एंटरप्राइज़ एप्लिकेशन तक, जल्दी और आसानी से प्राप्त कर सकते हैं।
जब इसे Spring के साथ उपयोग किया जाता है, तो कुछ अनुमति मिलान नियमों के तहत, हमलावर विशेष HTTP अनुरोध पैकेट बनाकर प्रमाणीकरण बाईपास पूरा कर सकता है।
प्रभावित संस्करण: Apache Shiro < 1.7.1
shiro 1.7.0
https://github.com/jweny/shiro-cve-2020-17523 दोनों तरीकों के लिए भेद्यता वातावरण अपडेट कर दिए गए हैं।
तरीका एक:
http://127.0.0.1:8080/admin/%20 या http://127.0.0.1:8080/admin/%20/
रिक्त स्थान जैसे खाली वर्णों का उपयोग करके, Shiro प्रमाणीकरण को बायपास किया जा सकता है।

तरीका दो:
p0desta गुरु के साथ चर्चा के बाद, एक और विशेष परिदृश्य में उपयोग का तरीका पाया गया।
http://127.0.0.1:8080/admin/%2e या http://127.0.0.1:8080/admin/%2e/
लेकिन . (और /) Spring के पथ मिलान नियमों में पथ विभाजक का प्रतिनिधित्व करते हैं, सामान्य वर्ण के रूप में मिलान नहीं होते। इसलिए डिफ़ॉल्ट शर्तों के तहत /admin/. तक पहुंचने पर 404 वापस आएगा।
लेकिन पूर्ण पथ सक्षम होने पर setAlwaysUseFullPath(true) सामान्य रूप से मिलान कर सकता है।

Shiro में URL प्राप्ति और मिलान org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain में होता है
पहले इस getChain विधि को संक्षेप में देखें:


यह विधि पहले जाँचती है कि requestURI / पर समाप्त होता है या नहीं, यदि हाँ, तो अंतिम / को हटा देती है।
फिर पथ मिलान के लूप में, पहले जाँचती है कि pathPattern / पर समाप्त होता है या नहीं, यदि हाँ, तो हटा देती है। फिर pathMatches() विधि को कॉल करती है।
इसलिए दोनों उपयोग विधियों में, / पर समाप्त होना मायने नहीं रखता, क्योंकि getChain विधि से गुज़रने पर इसे हटा दिया जाएगा।
pathMatches() विधि पर ध्यान दें:
Evaluate निकालें, क्रमशः pathMatches("/admin/*","/admin/1") और pathMatches("/admin/*","/admin/ ") की गणना करें। पहला सामान्य रूप से मिलता है, दूसरा मिलान में विफल रहता है।


डीबगिंग शुरू करें, डीबगिंग लंबे F7 के बाद शुरू होती है। doMatch("/admin/*","/admin/ ") तक। देखा जा सकता है कि tokenizeToStringArray द्वारा लौटाए गए pathDirs में दूसरी परत का पथ नहीं है। इसलिए /admin/* और /admin मेल नहीं खाते।

tokenizeToStringArray विधि का अनुसरण करें, पता चलता है कि tokenizeToStringArray विधि को कॉल करते समय trimTokens पैरामीटर true है।

और tokenizeToStringArray विधि, जब trimTokens true होता है, trim() प्रोसेसिंग से गुज़रती है, जिससे रिक्त स्थान हटा दिया जाता है। वापस getChain पर जाने पर अंतिम / हटा दिया जाता है। इसलिए tokenizeToStringArray द्वारा लौटाए गए pathDirs में दूसरी परत का पथ नहीं है।

सारांश: भेद्य शिरो संस्करण में, tokenizeToStringArray विधि को कॉल करते समय trimTokens पैरामीटर डिफ़ॉल्ट रूप से true होता है, रिक्त स्थान trim() प्रोसेसिंग से गुज़रता है, जिससे रिक्त स्थान हटा दिया जाता है। वापस getChain पर जाने पर अंतिम / हटा दिया जाता है, इसलिए /admin और /admin/* मिलान विफल हो जाता है, जिससे प्रमाणीकरण बाईपास होता है। और Spring को प्राप्त एक्सेस पथ /admin/%20 होता है, सामान्य तर्क के अनुसार प्रतिक्रिया देता है, इस प्रकार अनुमति बाईपास होती है।
दूसरे तरीके में /. और /./ देखकर, क्या कोई परिचित विधि याद आती है? हाँ, वह है normalize()।

सरल अनुवाद इस प्रकार है:
| शर्त | उदाहरण |
|---|---|
| स्लैश को बैकस्लैश में बदलें | \ -> / |
| डबल बैकस्लैश को स्लैश में बदलें | // -> / |
| /. या /.. पर समाप्त होने पर, अंत में / जोड़ें |
इसलिए /admin/. को /admin/./ में प्रोसेस करने के बाद /admin/ बन जाता है।

org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain प्रोसेसिंग से गुज़रने पर, चूंकि / पर समाप्त होता है, यदि ऐसा है, तो अंतिम / हटा दें, जो /admin बन जाता है। /admin और /admin/* मेल नहीं खाते, इसलिए Shiro प्रमाणीकरण बायपास हो जाता है।

और इस समय Spring को प्राप्त अनुरोध /admin/. है। यदि पूर्ण पथ मिलान सक्षम नहीं है, तो Spring में . और / पथ विभाजक के रूप में कार्य करते हैं, पथ मिलान में भाग नहीं लेते। इसलिए मैपिंग नहीं मिलेगी, और 404 वापस आएगा।

पूर्ण पथ मिलान सक्षम करने पर, यह पूरे URL का मिलान करेगा, इसलिए Spring 200 लौटाता है।
यहाँ पूर्ण पथ मिलान सक्षम करने का कोड है:
@SpringBootApplication
public class SpringbootShiroApplication extends SpringBootServletInitializer implements BeanPostProcessor {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootShiroApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(SpringbootShiroApplication.class, args);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping) bean).setAlwaysUseFullPath(true);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
उपरोक्त विश्लेषण के बाद, Shiro अनुमति बाईपास के दो कारण हैं:
tokenizeToStringArray फ़ंक्शन ने रिक्त स्थान को सही ढंग से संभाला नहीं।/ को संभालने का तर्क, पथ मिलान के लूप तर्क से पहले नहीं होना चाहिए।इसलिए आधिकारिक समाधान है:
https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df
tokenizeToStringArray के trimTokens पैरामीटर को false पर सेट करें।
/ हटाने के तर्क को समायोजित करें। पहले मूल पथ से मिलान करें, मिलान विफल होने पर अंतिम / हटाने का तर्क अपनाएँ।
सिद्धांततः trim() स्ट्रिंग के आगे-पीछे सभी व्हाइटस्पेस को हटा देता है, रिक्त स्थान उनमें से एक है, लेकिन परीक्षण में पाया गया कि रिक्त स्थान के अलावा अन्य व्हाइटस्पेस, जैसे %08, %09, %0a, spring+tomcat प्रोसेसिंग करते समय 400 लौटाते हैं।
इसलिए पहले तरीके में, रिक्त स्थान के अलावा, अभी तक कोई अन्य उपयोगी payload नहीं मिला है।
https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df
| /. -> /./ /.. -> /../ |
| /./ का सामान्यीकरण | /./ -> / |
| पथ छलांग | /aaa/../bbb -> /bbb |