میک او ایس پر عمل اور کرنل ایکسٹینشن کی حفاظت کیسے کریں۔

ہیلو، حبر! آج میں اس بارے میں بات کرنا چاہوں گا کہ آپ میکوس میں حملہ آوروں کے حملوں سے عمل کو کیسے بچا سکتے ہیں۔ مثال کے طور پر، یہ اینٹی وائرس یا بیک اپ سسٹم کے لیے مفید ہے، خاص طور پر چونکہ macOS کے تحت کسی عمل کو "مارنے" کے کئی طریقے ہیں۔ کٹ کے نیچے اس اور تحفظ کے طریقوں کے بارے میں پڑھیں۔

میک او ایس پر عمل اور کرنل ایکسٹینشن کی حفاظت کیسے کریں۔

ایک عمل کو "مارنے" کا کلاسک طریقہ

کسی عمل کو "مارنے" کا ایک معروف طریقہ یہ ہے کہ اس عمل کو SIGKILL سگنل بھیجیں۔ bash کے ذریعے آپ مارنے کے لیے معیاری "kill -SIGKILL PID" یا "pkill -9 NAME" کو کال کر سکتے ہیں۔ "کِل" کمانڈ کو UNIX کے دنوں سے جانا جاتا ہے اور یہ نہ صرف macOS پر، بلکہ UNIX جیسے دوسرے سسٹمز پر بھی دستیاب ہے۔

بالکل اسی طرح جیسے UNIX جیسے سسٹمز میں، macOS آپ کو دو کے علاوہ کسی بھی عمل کے سگنل کو روکنے کی اجازت دیتا ہے - SIGKILL اور SIGSTOP۔ یہ مضمون بنیادی طور پر SIGKILL سگنل پر ایک سگنل کے طور پر توجہ مرکوز کرے گا جس کی وجہ سے کسی عمل کو ہلاک کیا جا سکتا ہے۔

macOS کی تفصیلات

macOS پر، XNU کرنل میں کِل سسٹم کال psignal(SIGKILL,...) فنکشن کو کال کرتی ہے۔ آئیے یہ دیکھنے کی کوشش کرتے ہیں کہ یوزر اسپیس میں صارف کے دیگر اعمال کو psignal فنکشن کے ذریعے کیا کہا جا سکتا ہے۔ آئیے کرنل کے اندرونی میکانزم میں psignal فنکشن کی کالوں کو ختم کرتے ہیں (اگرچہ وہ غیر معمولی ہو سکتے ہیں، ہم انہیں کسی اور مضمون کے لیے چھوڑ دیں گے 🙂 - دستخط کی تصدیق، میموری کی خرابیاں، باہر نکلنا/ٹرمینیٹ ہینڈلنگ، فائل پروٹیکشن کی خلاف ورزیاں، وغیرہ .

آئیے فنکشن اور متعلقہ سسٹم کال کے ساتھ جائزہ شروع کریں۔ پے لوڈ کے ساتھ ختم کریں۔. یہ دیکھا جا سکتا ہے کہ کلاسک کِل کال کے علاوہ، ایک متبادل طریقہ بھی ہے جو macOS آپریٹنگ سسٹم کے لیے مخصوص ہے اور BSD میں نہیں پایا جاتا ہے۔ دونوں سسٹم کالز کے آپریٹنگ اصول بھی ایک جیسے ہیں۔ وہ کرنل فنکشن psignal پر براہ راست کالز ہیں۔ یہ بھی نوٹ کریں کہ کسی عمل کو ختم کرنے سے پہلے، ایک "کین سگنل" چیک کیا جاتا ہے - آیا یہ عمل کسی دوسرے عمل کو سگنل بھیج سکتا ہے؛ مثال کے طور پر، سسٹم کسی بھی ایپلیکیشن کو سسٹم کے عمل کو ختم کرنے کی اجازت نہیں دیتا ہے۔

static int
terminate_with_payload_internal(struct proc *cur_proc, int target_pid, uint32_t reason_namespace,
				uint64_t reason_code, user_addr_t payload, uint32_t payload_size,
				user_addr_t reason_string, uint64_t reason_flags)
{
...
	target_proc = proc_find(target_pid);
...
	if (!cansignal(cur_proc, cur_cred, target_proc, SIGKILL)) {
		proc_rele(target_proc);
		return EPERM;
	}
...
	if (target_pid == cur_proc->p_pid) {
		/*
		 * psignal_thread_with_reason() will pend a SIGKILL on the specified thread or
		 * return if the thread and/or task are already terminating. Either way, the
		 * current thread won't return to userspace.
		 */
		psignal_thread_with_reason(target_proc, current_thread(), SIGKILL, signal_reason);
	} else {
		psignal_with_reason(target_proc, SIGKILL, signal_reason);
	}
...
}

شروع کیا

سسٹم کے آغاز پر ڈیمون بنانے اور ان کی زندگی کو کنٹرول کرنے کا معیاری طریقہ شروع کیا گیا ہے۔ براہ کرم نوٹ کریں کہ ماخذ میکوس 10.10 تک لانچ سی ٹی ایل کے پرانے ورژن کے لیے ہیں، کوڈ کی مثالیں مثالی مقاصد کے لیے فراہم کی گئی ہیں۔ جدید لانچ سی ٹی ایل ایکس پی سی کے ذریعے لانچڈ سگنل بھیجتا ہے، لانچ سی ٹی ایل منطق کو اس میں منتقل کر دیا گیا ہے۔

آئیے دیکھتے ہیں کہ ایپلی کیشنز کو کس طرح روکا جاتا ہے۔ SIGTERM سگنل بھیجنے سے پہلے، "proc_terminate" سسٹم کال کا استعمال کرتے ہوئے ایپلیکیشن کو روکنے کی کوشش کی جاتی ہے۔

<launchctl src/core.c>
...
	error = proc_terminate(j->p, &sig);
	if (error) {
		job_log(j, LOG_ERR | LOG_CONSOLE, "Could not terminate job: %d: %s", error, strerror(error));
		job_log(j, LOG_NOTICE | LOG_CONSOLE, "Using fallback option to terminate job...");
		error = kill2(j->p, SIGTERM);
		if (error) {
			job_log(j, LOG_ERR, "Could not signal job: %d: %s", error, strerror(error));
		} 
...
<>

ہڈ کے نیچے، proc_terminate، اپنے نام کے باوجود، SIGTERM کے ساتھ نہ صرف psignal بھیج سکتا ہے، بلکہ SIGKILL بھی۔

بالواسطہ قتل - وسائل کی حد

ایک اور دلچسپ کیس دوسرے سسٹم کال میں دیکھا جا سکتا ہے۔ عمل_پالیسی. اس سسٹم کال کا عام استعمال ایپلیکیشن کے وسائل کو محدود کرنا ہے، جیسے کہ انڈیکسر کے لیے CPU وقت اور میموری کوٹہ کو محدود کرنا تاکہ فائل کیشنگ کی سرگرمیوں سے سسٹم کو نمایاں طور پر سست نہ کیا جائے۔ اگر کوئی ایپلیکیشن اپنے وسائل کی حد تک پہنچ گئی ہے، جیسا کہ proc_apply_resource_actions فنکشن سے دیکھا جا سکتا ہے، ایک SIGKILL سگنل اس عمل کو بھیجا جاتا ہے۔

اگرچہ یہ سسٹم کال ممکنہ طور پر کسی عمل کو ختم کر سکتی ہے، لیکن سسٹم نے سسٹم کال کو کال کرنے کے عمل کے حقوق کی مناسب جانچ نہیں کی۔ اصل میں چیک کر رہا ہے۔ وجودلیکن اس شرط کو نظرانداز کرنے کے لیے متبادل پرچم PROC_POLICY_ACTION_SET استعمال کرنا کافی ہے۔

لہذا، اگر آپ ایپلیکیشن کے CPU استعمال کوٹہ کو "محدود" کرتے ہیں (مثال کے طور پر، صرف 1 ns کو چلانے کی اجازت دینا)، تو آپ سسٹم میں کسی بھی عمل کو ختم کر سکتے ہیں۔ اس طرح، میلویئر اینٹی وائرس کے عمل سمیت سسٹم پر کسی بھی عمل کو ختم کر سکتا ہے۔ یہ بھی دلچسپ اثر ہے جو pid 1 (launchctl) کے ساتھ کسی عمل کو ختم کرنے پر ہوتا ہے - SIGKILL سگنل پر کارروائی کرنے کی کوشش کرتے وقت کرنل گھبراہٹ :)

میک او ایس پر عمل اور کرنل ایکسٹینشن کی حفاظت کیسے کریں۔

مسئلہ کیسے حل کیا جائے؟

کسی عمل کو ہلاک ہونے سے روکنے کا سب سے سیدھا طریقہ سسٹم کال ٹیبل میں فنکشن پوائنٹر کو تبدیل کرنا ہے۔ بدقسمتی سے، یہ طریقہ بہت سی وجوہات کی بناء پر غیر معمولی ہے۔

سب سے پہلے، وہ علامت جو sysent کی میموری کی جگہ کو کنٹرول کرتی ہے، نہ صرف XNU کرنل علامت کے لیے نجی ہے، بلکہ کرنل کی علامتوں میں نہیں مل سکتی۔ آپ کو ہورسٹک تلاش کے طریقے استعمال کرنے ہوں گے، جیسے فنکشن کو متحرک طور پر الگ کرنا اور اس میں پوائنٹر تلاش کرنا۔

دوم، ٹیبل میں اندراجات کی ساخت کا انحصار ان جھنڈوں پر ہوتا ہے جن کے ساتھ دانا کو مرتب کیا گیا تھا۔ اگر CONFIG_REQUIRES_U32_MUNGING پرچم کا اعلان کیا جاتا ہے، تو ڈھانچے کا سائز تبدیل کر دیا جائے گا - ایک اضافی فیلڈ شامل کی جائے گی sy_arg_munge32. اس بات کا تعین کرنے کے لیے ایک اضافی چیک کرنا ضروری ہے کہ دانا کس جھنڈے کے ساتھ مرتب کیا گیا تھا، یا متبادل طور پر، معلوم لوگوں کے خلاف فنکشن پوائنٹرز کو چیک کریں۔

struct sysent {         /* system call table */
        sy_call_t       *sy_call;       /* implementing function */
#if CONFIG_REQUIRES_U32_MUNGING || (__arm__ && (__BIGGEST_ALIGNMENT__ > 4))
        sy_munge_t      *sy_arg_munge32; /* system call arguments munger for 32-bit process */
#endif
        int32_t         sy_return_type; /* system call return types */
        int16_t         sy_narg;        /* number of args */
        uint16_t        sy_arg_bytes;   /* Total size of arguments in bytes for
                                         * 32-bit system calls
                                         */
};

خوش قسمتی سے، میک او ایس کے جدید ورژن میں، ایپل پروسیس کے ساتھ کام کرنے کے لیے ایک نیا API فراہم کرتا ہے۔ Endpoint Security API کلائنٹس کو دوسرے عمل کے لیے بہت سی درخواستوں کی اجازت دیتا ہے۔ اس طرح، آپ مندرجہ بالا API کا استعمال کرتے ہوئے، SIGKILL سگنل سمیت پروسیسز کے لیے کسی بھی سگنل کو روک سکتے ہیں۔

#include <bsm/libbsm.h>
#include <EndpointSecurity/EndpointSecurity.h>
#include <unistd.h>

int main(int argc, const char * argv[]) {
    es_client_t* cli = nullptr;
    {
        auto res = es_new_client(&cli, ^(es_client_t * client, const es_message_t * message) {
            switch (message->event_type) {
                case ES_EVENT_TYPE_AUTH_SIGNAL:
                {
                    auto& msg = message->event.signal;
                    auto target = msg.target;
                    auto& token = target->audit_token;
                    auto pid = audit_token_to_pid(token);
                    printf("signal '%d' sent to pid '%d'n", msg.sig, pid);
                    es_respond_auth_result(client, message, pid == getpid() ? ES_AUTH_RESULT_DENY : ES_AUTH_RESULT_ALLOW, false);
                }
                    break;
                default:
                    break;
            }
        });
    }

    {
        es_event_type_t evs[] = { ES_EVENT_TYPE_AUTH_SIGNAL };
        es_subscribe(cli, evs, sizeof(evs) / sizeof(*evs));
    }

    printf("%dn", getpid());
    sleep(60); // could be replaced with other waiting primitive

    es_unsubscribe_all(cli);
    es_delete_client(cli);

    return 0;
}

اسی طرح، ایک MAC پالیسی کو کرنل میں رجسٹر کیا جا سکتا ہے، جو سگنل پروٹیکشن طریقہ (پالیسی proc_check_signal) فراہم کرتا ہے، لیکن API سرکاری طور پر تعاون یافتہ نہیں ہے۔

دانا کی توسیع کا تحفظ

نظام میں عمل کی حفاظت کے علاوہ، خود دانا کی توسیع (کیکسٹ) کی حفاظت بھی ضروری ہے۔ macOS ڈویلپرز کو آسانی سے IOKit ڈیوائس ڈرائیور تیار کرنے کے لیے ایک فریم ورک فراہم کرتا ہے۔ ڈیوائسز کے ساتھ کام کرنے کے لیے ٹولز فراہم کرنے کے علاوہ، IOKit C++ کلاسز کی مثالوں کا استعمال کرتے ہوئے ڈرائیور کو اسٹیک کرنے کے طریقے فراہم کرتا ہے۔ یوزر اسپیس میں ایک ایپلیکیشن کرنل یوزر اسپیس رشتہ قائم کرنے کے لیے کلاس کی رجسٹرڈ مثال کو "تلاش" کر سکے گی۔

سسٹم میں کلاس مثالوں کی تعداد کا پتہ لگانے کے لیے، ioclasscount یوٹیلیٹی موجود ہے۔

my_kext_ioservice = 1
my_kext_iouserclient = 1

کوئی بھی کرنل ایکسٹینشن جو ڈرائیور اسٹیک کے ساتھ رجسٹر ہونا چاہتی ہے اسے ایک کلاس کا اعلان کرنا چاہیے جو IOService سے وراثت میں ملتی ہے، مثال کے طور پر اس معاملے میں my_kext_ioservice۔ صارف کی ایپلی کیشنز کو جوڑنے سے کلاس کی ایک نئی مثال بنتی ہے جو IOUserClient سے وراثت میں ملتی ہے، مثال کے طور پر my_kext_iouserclient۔

سسٹم (kextunload کمانڈ) سے ڈرائیور کو اتارنے کی کوشش کرتے وقت، ورچوئل فنکشن "بول ٹرمینیٹ(IOOptionBits آپشنز)" کہا جاتا ہے۔ کیکسٹون لوڈ کو غیر فعال کرنے کے لیے ان لوڈ کرنے کی کوشش کرتے وقت ختم کرنے کے لیے کال پر غلط لوٹنا کافی ہے۔

bool Kext::terminate(IOOptionBits options)
{

  if (!IsUnloadAllowed)
  {
    // Unload is not allowed, returning false
    return false;
  }

  return super::terminate(options);
}

لوڈ کرتے وقت IsUnloadAllowed جھنڈا IOUserClient کے ذریعے سیٹ کیا جا سکتا ہے۔ جب ڈاؤن لوڈ کی حد ہوتی ہے، تو kextunload کمانڈ درج ذیل آؤٹ پٹ کو واپس کرے گی۔

admin@admins-Mac drivermanager % sudo kextunload ./test.kext
Password:
(kernel) Can't remove kext my.kext.test; services failed to terminate - 0xe00002c7.
Failed to unload my.kext.test - (iokit/common) unsupported function.

اسی طرح کا تحفظ IOUserClient کے لیے کیا جانا چاہیے۔ IOKitLib userspace فنکشن "IOCatalogueTerminate(mach_port_t, uint32_t پرچم، io_name_t تفصیل)" کا استعمال کرتے ہوئے کلاسز کی مثالیں اتاری جا سکتی ہیں۔ آپ "ٹرمینیٹ" کمانڈ کو کال کرتے وقت غلط لوٹ سکتے ہیں جب تک کہ یوزر اسپیس ایپلی کیشن "ڈیز" نہ ہو، یعنی "کلائنٹ ڈیڈ" فنکشن کو کال نہ کیا جائے۔

فائل پروٹیکشن

فائلوں کی حفاظت کے لیے، Kauth API کا استعمال کرنا کافی ہے، جو آپ کو فائلوں تک رسائی کو محدود کرنے کی اجازت دیتا ہے۔ Apple ڈویلپرز کو دائرہ کار میں مختلف واقعات کے بارے میں اطلاعات فراہم کرتا ہے؛ ہمارے لیے KAUTH_VNODE_DELETE، KAUTH_VNODE_WRITE_DATA اور KAUTH_VNODE_DELETE_CHILD آپریشنز اہم ہیں۔ فائلوں تک رسائی کو محدود کرنے کا سب سے آسان طریقہ راستہ سے ہے - ہم فائل کا راستہ حاصل کرنے کے لیے "vn_getpath" API کا استعمال کرتے ہیں اور پاتھ کے سابقہ ​​کا موازنہ کرتے ہیں۔ نوٹ کریں کہ فائل فولڈر کے راستوں کے نام کو بہتر بنانے کے لیے، سسٹم ہر فائل تک رسائی کی اجازت نہیں دیتا، بلکہ صرف اس فولڈر تک رسائی کی اجازت دیتا ہے جس کا نام تبدیل کیا گیا ہے۔ پیرنٹ پاتھ کا موازنہ کرنا اور اس کے لیے KAUTH_VNODE_DELETE کو محدود کرنا ضروری ہے۔

میک او ایس پر عمل اور کرنل ایکسٹینشن کی حفاظت کیسے کریں۔

اس نقطہ نظر کا نقصان کم کارکردگی ہو سکتا ہے کیونکہ سابقوں کی تعداد میں اضافہ ہوتا ہے۔ اس بات کو یقینی بنانے کے لیے کہ موازنہ O(prefix*length) کے برابر نہیں ہے، جہاں پریفکس سابقے کی تعداد ہے، لمبائی سٹرنگ کی لمبائی ہے، آپ سابقے کے ذریعے بنایا گیا ایک deterministic finite automaton (DFA) استعمال کر سکتے ہیں۔

آئیے دیے گئے سابقوں کے سیٹ کے لیے ڈی ایف اے بنانے کے طریقے پر غور کریں۔ ہم ہر سابقہ ​​کے شروع میں کرسر کو شروع کرتے ہیں۔ اگر تمام کرسر ایک ہی کریکٹر کی طرف اشارہ کرتے ہیں تو پھر ہر کرسر کو ایک کریکٹر سے بڑھائیں اور یاد رکھیں کہ ایک ہی لائن کی لمبائی ایک سے زیادہ ہے۔ اگر مختلف علامتوں کے ساتھ دو کرسر ہیں، تو کرسر کو اس علامت کے مطابق گروپوں میں تقسیم کریں جس کی وہ اشارہ کرتے ہیں اور ہر گروپ کے لیے الگورتھم کو دہرائیں۔

پہلی صورت میں (کرسر کے نیچے تمام حروف ایک جیسے ہیں)، ہمیں ایک DFA حالت ملتی ہے جس میں ایک ہی لائن کے ساتھ صرف ایک منتقلی ہوتی ہے۔ دوسری صورت میں، ہمیں 256 سائز (حروف کی تعداد اور گروپوں کی زیادہ سے زیادہ تعداد) کے بعد کی ریاستوں میں ٹرانزیشن کا ٹیبل ملتا ہے جو فنکشن کو بار بار کال کرکے حاصل کیا جاتا ہے۔

آئیے ایک مثال دیکھتے ہیں۔ سابقے کے ایک سیٹ کے لیے ("/foo/bar/tmp/", "/var/db/foo/", "/foo/bar/aba/", "foo/bar/aac/") آپ درج ذیل حاصل کر سکتے ہیں ڈی ایف اے۔ اعداد و شمار صرف دوسری ریاستوں کی طرف جانے والی تبدیلیوں کو دکھاتا ہے؛ دیگر ٹرانزیشن حتمی نہیں ہوں گی۔

میک او ایس پر عمل اور کرنل ایکسٹینشن کی حفاظت کیسے کریں۔

DKA ریاستوں سے گزرتے وقت، 3 معاملات ہو سکتے ہیں۔

  1. حتمی حالت تک پہنچ گئی ہے - راستہ محفوظ ہے، ہم KAUTH_VNODE_DELETE، KAUTH_VNODE_WRITE_DATA اور KAUTH_VNODE_DELETE_CHILD کو محدود کرتے ہیں
  2. حتمی حالت تک نہیں پہنچی تھی، لیکن راستہ "ختم ہو گیا" (نال ٹرمینیٹر تک پہنچ گیا) - راستہ ایک پیرنٹ ہے، اسے KAUTH_VNODE_DELETE کو محدود کرنا ضروری ہے۔ نوٹ کریں کہ اگر vnode ایک فولڈر ہے، تو آپ کو آخر میں '/' شامل کرنے کی ضرورت ہے، ورنہ یہ اسے فائل "/foor/bar/t" تک محدود کر سکتا ہے، جو کہ غلط ہے۔
  3. آخری حالت تک نہیں پہنچی، راستہ ختم نہیں ہوا۔ کوئی بھی سابقہ ​​اس سے میل نہیں کھاتا، ہم پابندیاں متعارف نہیں کراتے ہیں۔

حاصل يہ ہوا

تیار کیے جانے والے سیکیورٹی سلوشنز کا مقصد صارف اور اس کے ڈیٹا کی سیکیورٹی کی سطح کو بڑھانا ہے۔ ایک طرف، یہ مقصد Acronis سافٹ ویئر پروڈکٹ کی ترقی سے حاصل ہوتا ہے، جو ان کمزوریوں کو ختم کرتا ہے جہاں آپریٹنگ سسٹم خود "کمزور" ہے۔ دوسری طرف، ہمیں ان حفاظتی پہلوؤں کو مضبوط کرنے میں کوتاہی نہیں کرنی چاہیے جنہیں OS کی طرف بہتر بنایا جا سکتا ہے، خاص طور پر چونکہ ایسی کمزوریوں کو بند کرنے سے بطور پروڈکٹ ہمارے اپنے استحکام میں اضافہ ہوتا ہے۔ اس خطرے کی اطلاع Apple پروڈکٹ سیکیورٹی ٹیم کو دی گئی تھی اور اسے macOS 10.14.5 (https://support.apple.com/en-gb/HT210119) میں ٹھیک کر دیا گیا ہے۔

میک او ایس پر عمل اور کرنل ایکسٹینشن کی حفاظت کیسے کریں۔

یہ سب صرف اس صورت میں کیا جا سکتا ہے جب آپ کی افادیت باضابطہ طور پر کرنل میں انسٹال ہو گئی ہو۔ یعنی، بیرونی اور ناپسندیدہ سافٹ ویئر کے لیے ایسی کوئی خامیاں نہیں ہیں۔ تاہم، جیسا کہ آپ دیکھ سکتے ہیں، اینٹی وائرس اور بیک اپ سسٹم جیسے جائز پروگراموں کی حفاظت کے لیے بھی کام کی ضرورت ہوتی ہے۔ لیکن اب MacOS کے لیے نئی Acronis مصنوعات کو سسٹم سے اتارنے کے خلاف اضافی تحفظ حاصل ہوگا۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں