BROP - Blind Return Oriented Programming
Basic Information
इस हमले का लक्ष्य है बिना किसी जानकारी के ROP का दुरुपयोग करना। यह हमला निम्नलिखित परिदृश्य पर आधारित है:
एक स्टैक भेद्यता और इसे सक्रिय करने का ज्ञान।
एक सर्वर एप्लिकेशन जो क्रैश के बाद पुनः प्रारंभ होता है।
Attack
1. कमजोर ऑफसेट खोजें एक और चर भेजकर जब तक सर्वर में खराबी का पता नहीं चलता
2. कैनरी का ब्रूट-फोर्स करना
3. स्टोर किए गए RBP और RIP पते का ब्रूट-फोर्स करना
आप इन प्रक्रियाओं के बारे में अधिक जानकारी यहां (BF Forked & Threaded Stack Canaries) और यहां (BF Addresses in the Stack) पा सकते हैं।
4. स्टॉप गैजेट खोजें
यह गैजेट मूल रूप से यह पुष्टि करने की अनुमति देता है कि ROP गैजेट द्वारा कुछ दिलचस्प निष्पादित किया गया था क्योंकि निष्पादन क्रैश नहीं हुआ। आमतौर पर, यह गैजेट कुछ ऐसा होगा जो निष्पादन को रोकता है और यह ROP श्रृंखला के अंत में स्थित होता है जब किसी विशिष्ट ROP गैजेट के निष्पादन की पुष्टि करने के लिए ROP गैजेट खोजा जाता है।
5. BROP गैजेट खोजें
यह तकनीक ret2csu गैजेट का उपयोग करती है। और इसका कारण यह है कि यदि आप कुछ निर्देशों के बीच में इस गैजेट तक पहुंचते हैं तो आपको rsi
और rdi
को नियंत्रित करने के लिए गैजेट मिलते हैं:
ये गैजेट होंगे:
pop rsi; pop r15; ret
pop rdi; ret
ध्यान दें कि इन गैजेट्स के साथ एक फ़ंक्शन को कॉल करने के लिए 2 तर्कों को नियंत्रित करना संभव है।
इसके अलावा, ध्यान दें कि ret2csu गैजेट का बहुत अद्वितीय हस्ताक्षर है क्योंकि यह स्टैक से 6 रजिस्टर को पॉप करेगा। इसलिए एक श्रृंखला भेजना जैसे:
'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP
यदि STOP निष्पादित होता है, तो इसका अर्थ है कि एक पता जो स्टैक से 6 रजिस्टर को पॉप कर रहा था का उपयोग किया गया था। या कि उपयोग किया गया पता भी एक STOP पता था।
इस अंतिम विकल्प को हटाने के लिए एक नई श्रृंखला जैसे निम्नलिखित निष्पादित की जाती है और इसे पिछले वाले को पुष्टि करने के लिए STOP गैजेट को निष्पादित नहीं करना चाहिए:
'A' * offset + canary + rbp + ADDR
ret2csu गैजेट के पते को जानकर, यह संभव है कि rsi
और rdi
को नियंत्रित करने के लिए गैजेट्स के पते का अनुमान लगाया जाए।
6. PLT खोजें
PLT तालिका को 0x400000 से या स्टैक से लीक किए गए RIP पते से खोजा जा सकता है (यदि PIE का उपयोग किया जा रहा है)। तालिका के प्रविष्टियाँ 16B (0x10B) द्वारा अलग होती हैं, और जब एक फ़ंक्शन को कॉल किया जाता है तो सर्वर क्रैश नहीं होता है भले ही तर्क सही न हों। इसके अलावा, PLT में एक प्रविष्टि के पते की जांच करना + 6B भी क्रैश नहीं होता क्योंकि यह पहला कोड है जो निष्पादित होता है।
इसलिए, निम्नलिखित व्यवहारों की जांच करके PLT तालिका को खोजना संभव है:
'A' * offset + canary + rbp + ADDR + STOP
-> कोई क्रैश नहीं'A' * offset + canary + rbp + (ADDR + 0x6) + STOP
-> कोई क्रैश नहीं'A' * offset + canary + rbp + (ADDR + 0x10) + STOP
-> कोई क्रैश नहीं
7. strcmp खोजें
strcmp
फ़ंक्शन रजिस्टर rdx
को तुलना की जा रही स्ट्रिंग की लंबाई पर सेट करता है। ध्यान दें कि rdx
तीसरा तर्क है और हमें इसे 0 से बड़ा होना चाहिए ताकि बाद में write
का उपयोग करके प्रोग्राम को लीक किया जा सके।
हम PLT में strcmp
के स्थान को इसके व्यवहार के आधार पर खोज सकते हैं यह देखते हुए कि हम अब फ़ंक्शनों के 2 पहले तर्कों को नियंत्रित कर सकते हैं:
strcmp(<non read addr>, <non read addr>) -> क्रैश
strcmp(<non read addr>, <read addr>) -> क्रैश
strcmp(<read addr>, <non read addr>) -> क्रैश
strcmp(<read addr>, <read addr>) -> कोई क्रैश नहीं
इसकी जांच करने के लिए PLT तालिका के प्रत्येक प्रविष्टि को कॉल करके या PLT धीमी पथ का उपयोग करके किया जा सकता है जो मूल रूप से PLT तालिका में एक प्रविष्टि को कॉल करने + 0xb (जो dlresolve
को कॉल करता है) के बाद स्टैक में प्रविष्टि संख्या को प्रॉब करने के लिए (शून्य से शुरू) सभी PLT प्रविष्टियों को स्कैन करने के लिए है:
strcmp(<non read addr>, <read addr>) -> क्रैश
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> क्रैश होगाstrcmp(<read addr>, <non read addr>) -> क्रैश
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<read addr>, <read addr>) -> कोई क्रैश नहीं
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
याद रखें कि:
BROP + 0x7
pop RSI; pop R15; ret;
की ओर इशारा करता हैBROP + 0x9
pop RDI; ret;
की ओर इशारा करता हैPLT + 0xb dl_resolve को कॉल करने की ओर इशारा करता है।
strcmp
को खोजने के बाद, rdx
को 0 से बड़ा मान सेट करना संभव है।
ध्यान दें कि आमतौर पर rdx
पहले से ही 0 से बड़ा मान रखेगा, इसलिए यह कदम आवश्यक नहीं हो सकता।
8. Write या समकक्ष खोजें
अंत में, डेटा को एक्सफिल्ट्रेट करने के लिए एक गैजेट की आवश्यकता होती है ताकि बाइनरी को एक्सफिल्ट्रेट किया जा सके। और इस समय यह संभव है कि 2 तर्कों को नियंत्रित करें और rdx
को 0 से बड़ा सेट करें।
इसके लिए 3 सामान्य फ़ंक्शन हैं जिनका दुरुपयोग किया जा सकता है:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
हालांकि, मूल पेपर केवल write
का उल्लेख करता है, इसलिए आइए इसके बारे में बात करते हैं:
वर्तमान समस्या यह है कि हमें नहीं पता write फ़ंक्शन PLT के अंदर कहाँ है और हमें नहीं पता डेटा को हमारे सॉकेट पर भेजने के लिए fd संख्या।
हालांकि, हम जानते हैं PLT तालिका कहाँ है और इसके व्यवहार के आधार पर write को खोजना संभव है। और हम सर्वर के साथ कई कनेक्शन बना सकते हैं और एक उच्च FD का उपयोग कर सकते हैं यह उम्मीद करते हुए कि यह हमारे कुछ कनेक्शनों से मेल खाता है।
इन फ़ंक्शनों को खोजने के लिए व्यवहार हस्ताक्षर:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> यदि डेटा प्रिंट होता है, तो puts मिला'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> यदि डेटा प्रिंट होता है, तो dprintf मिला'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> यदि डेटा प्रिंट होता है, तो write मिला
Automatic Exploitation
References
Original paper: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Last updated