
Rust-आधारित PoC जो Windows fibers का उपयोग करके मेमोरी में कोड को चुपके से निष्पादित करता है, kernel callbacks के बिना control और payload fibers के बीच स्विच करके EDR से payload stacks को छिपाता है।
फाइबर निष्पादन की एक इकाई है जिसे एप्लिकेशन द्वारा मैन्युअल रूप से शेड्यूल किया जाना चाहिए, न कि विंडोज में निर्मित प्राथमिकता-आधारित शेड्यूलिंग तंत्र पर निर्भर रहना चाहिए। फाइबर को अक्सर लाइटवेट थ्रेड कहा जाता है। फाइबर क्या हैं और वे कैसे काम करते हैं, इसके बारे में अधिक विस्तृत जानकारी के लिए आधिकारिक दस्तावेज़ीकरण देखें। फाइबर एक ही थ्रेड में कई निष्पादन प्रवाहों की अनुमति देते हैं, प्रत्येक का अपना रजिस्टर स्थिति और स्टैक होता है। दूसरी ओर, फाइबर कर्नेल के लिए अदृश्य होते हैं, जो उन्हें नए थ्रेड बनाने की तुलना में इन-मेमोरी कोड निष्पादित करने का एक अधिक गुप्त (और सस्ता) तरीका बनाता है।
एक थ्रेड कई फाइबर बना सकता है, और SwitchToFiber फ़ंक्शन को कॉल करके उनके बीच स्विच कर सकता है। इससे पहले, वर्तमान थ्रेड को स्वयं ConvertThreadToFiber कॉल करके एक फाइबर बनना होगा, क्योंकि केवल एक फाइबर ही अन्य फाइबर बना सकता है। अंत में, एक ऐसा फाइबर बनाने के लिए जो शेड्यूल होने पर इन-मेमोरी कोड (उदाहरण के लिए, एक PE को रिफ्लेक्टिवली लोड करने या कुछ शेलकोड के बाद) निष्पादित करता है, बस CreateFiber को कॉल करना आवश्यक है।
SwitchToFiber फ़ंक्शन इस प्रक्रिया का सबसे महत्वपूर्ण हिस्सा है और जहां सारा जादू होता है। यह फ़ंक्शन एक फाइबर या दूसरे को शेड्यूल करने की अनुमति देता है, यह सब उपयोगकर्ता स्थान पर होता है। आधिकारिक दस्तावेज़ीकरण के अनुसार, "SwitchToFiber फ़ंक्शन वर्तमान फाइबर की स्थिति जानकारी सहेजता है और निर्दिष्ट फाइबर की स्थिति को पुनर्स्थापित करता है"। इसका मतलब है कि जब यह फ़ंक्शन कॉल किया जाता है, तो रजिस्टरों के मान और स्टैक वर्तमान फाइबर अवस्था से लक्ष्य फाइबर अवस्था में स्विच हो जाते हैं, जिससे प्रक्रिया पूर्ण होने पर वर्तमान फाइबर के स्टैक को "छिपाना" संभव हो जाता है। यह लक्ष्य फाइबर के निष्पादन को उसी बिंदु से जारी रखने की भी अनुमति देता है जहां निष्पादन रोका गया था (उसी तरह जैसे शेड्यूलर अपने स्वयं के प्राथमिकता तर्क के अनुसार थ्रेड्स के बीच स्विच करता है)।
और यही वह है जो यह सरल PoC करता है:
run() फ़ंक्शन को चलाने के लिए एक नया फाइबर बनाएगा। इस फाइबर को अब से पेलोड फाइबर के रूप में जाना जाएगा।यह प्रक्रिया अनिश्चित काल तक दोहराई जाती है।
फाइबर का उपयोग कुछ प्रकार के पेलोड (जैसे C2 बीकन) के लिए फायदेमंद हो सकता है, इनमें से कुछ कारणों से:
JMP या CALL निर्देशों का उपयोग किए बिना इन-मेमोरी कोड चलाने की अनुमति देते हैं।चूंकि हम स्ट्रिंग शाब्दिक को अस्पष्ट करने के लिए LITCRYPT प्लगइन का उपयोग कर रहे हैं, कोड संकलित करने से पहले पर्यावरण चर LITCRYPT_ENCRYPT_KEY सेट करना आवश्यक है:
C:\Users\User\Desktop\Fiber> set LITCRYPT_ENCRYPT_KEY="yoursupersecretkey"
उसके बाद, बस पेलोड और लोडर दोनों को संकलित करें और अंतिम को चलाएं:
C:\Users\User\Desktop\Fiber\payload> cargo build --release
C:\Users\User\Desktop\Fiber\loader> cargo build --release
C:\Users\User\Desktop\Fiber\loader\target\release> loader.exe
इस PoC निष्पादन में कोई खास रहस्य नहीं है। बस लोडर को चलाना है और थ्रेड स्टैक का निरीक्षण करने के लिए ProcessHacker जैसे किसी टूल का उपयोग करना है। चूंकि पेलोड सोने से पहले वापस नियंत्रण फाइबर पर स्विच करता है, पेलोड फाइबर का स्टैक अधिकांश समय छिपा रहता है। आप आउटपुट में देखेंगे कि कैसे दो फाइबर पहले से टिप्पणी किए गए तर्क का अनुसरण करते हुए क्रमिक रूप से शेड्यूल किए जाते हैं।
कोड फाइबर का उपयोग, निर्माण और शेड्यूल करने का तरीका दिखाने के लिए टिप्पणी की गई है। आप देखेंगे कि लोडर और पेलोड दोनों, उदाहरण के रूप में दिए गए, एक अनंत लूप में "अटके" हैं, जो फाइबर के बीच अनिश्चित काल तक स्विच करने और निष्पादन जारी रखने की अनुमति देता है।
यदि कोई भिन्न पेलोड का परीक्षण करना चाहते हैं, तो लोडर की फ़ाइल src::main.rs की पंक्ति 32 पर स्थित पथ को संशोधित करें। उस स्थिति में, नए dll को एक run(PVOID) फ़ंक्शन निर्यात करना होगा जो इनपुट पैरामीटर के रूप में नियंत्रण फाइबर का पता प्राप्त करेगा। इस फ़ंक्शन को Sleep फ़ंक्शन को कॉल करने के लिए वापस नियंत्रण फाइबर पर स्विच करना होगा, हालाँकि आप अपनी आवश्यकताओं के अनुसार इस व्यवहार को संशोधित कर सकते हैं।
एक यादृच्छिक पेलोड के साथ इस टूल का परीक्षण करने का दूसरा तरीका IAT हुकिंग करना है ताकि पेलोड द्वारा किए गए Sleep फ़ंक्शन (या किसी अन्य आयातित फ़ंक्शन) के किसी भी कॉल को लोडर पर स्थित एक फ़ंक्शन पर रीडायरेक्ट किया जा सके, जिससे इस कॉल के होने पर वापस नियंत्रण फाइबर पर स्विच किया जा सके। आप पर निर्भर है।
निम्नलिखित स्क्रीनशॉट में हम देख सकते हैं कि कैसे वर्तमान थ्रेड का स्टैक एक निजी मेमोरी क्षेत्र से दूसरे में चला जाता है जब हम फाइबर स्विच करते हैं:
