BROP - Blind Return Oriented Programming
मूल जानकारी
इस हमले का उद्देश्य है कि एक ROP का दुरुपयोग किया जा सके बिना किसी भी जानकारी के वंशावली बाइनरी के बारे में। यह हमला निम्नलिखित परिदृश्य पर आधारित है:
एक स्टैक कमजोरी और इसे ट्रिगर करने के तरीके की जानकारी।
एक सर्वर एप्लिकेशन जो एक क्रैश के बाद पुनरारंभ होता है।
हमला
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 गैजेट को निष्पादित न करना चाहिए ताकि पिछले वाला एक 6 रजिस्टर्स पॉप किया गया था:
'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
का उपयोग करके प्रोग्राम को लीक कर सकें।
इसे खोजना संभव है strcmp
की स्थान पर PLT में इसके व्यवहार का उपयोग करके जिसका अर्थ है कि हम अब फ़ंक्शनों के पहले 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
को कॉल करता है) को कॉल किया जाता है और स्टैक में एंट्री नंबर (जो शुरू होता है शून्य से) के बाद जांचने के लिए:
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
8. लेखन या समकक्ष खोजना
अंत में, डेटा निकालने के लिए एक गैजेट की आवश्यकता है ताकि बाइनरी को निकाला जा सके। और इस समय 2 तर्कों को नियंत्रित करना और rdx
को 0 से अधिक सेट करना संभव है।
इसके लिए 3 सामान्य फ़ंक्शन हैं जिनका दुरुपयोग किया जा सकता है:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
हालांकि, मूल पेपर में केवल write
का उल्लेख है, इसलिए इसके बारे में चर्चा करें:
वर्तमान समस्या यह है कि हमें पता नहीं है PLT के अंदर लेखन कार्य कहाँ है और हमें हमारे सॉकेट को डेटा भेजने के लिए कोई fd नंबर पता नहीं है।
हालांकि, हमें पता है PLT तालिका कहाँ है और इसके व्यवहार के आधार पर लेखन को खोजना संभव है। और हम सर्वर के साथ कई कनेक्शन बना सकते हैं और एक उच्च FD का उपयोग करके उम्मीद कर सकते हैं कि यह हमारे कनेक्शनों में से किसी से मेल खाता है।
उन फ़ंक्शनों को खोजने के लिए व्यवहार हस्ताक्षर:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> यदि डेटा प्रिंट होता है, तो पट्स मिल गया था'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> यदि डेटा प्रिंट होता है, तो डीप्रिंट मिल गया था'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
-> यदि डेटा प्रिंट होता है, तो लेखन मिल गया था
स्वचालित उत्पीड़न
संदर्भ
Last updated