Cara nglindhungi proses lan ekstensi kernel ing macOS

Sugeng rawuh, Habr! Dina iki aku pengin ngomong babagan carane sampeyan bisa nglindhungi proses saka serangan dening panyerang ing macOS. Contone, iki migunani kanggo antivirus utawa sistem serep, utamane amarga ing macOS ana sawetara cara kanggo "mateni" proses. Waca babagan iki lan cara proteksi ing ngisor potong.

Cara nglindhungi proses lan ekstensi kernel ing macOS

Cara klasik kanggo "mateni" proses

Cara sing kondhang kanggo "mateni" proses yaiku ngirim sinyal SIGKILL menyang proses kasebut. Liwat bash sampeyan bisa nelpon standar "kill -SIGKILL PID" utawa "pkill -9 NAME" kanggo mateni. Printah "mateni" wis dikenal wiwit jaman UNIX lan kasedhiya ora mung ing macOS, nanging uga ing sistem kaya UNIX liyane.

Kaya ing sistem kaya UNIX, macOS ngidini sampeyan nyegat sinyal menyang proses kajaba loro - SIGKILL lan SIGSTOP. Artikel iki utamané bakal fokus ing sinyal SIGKILL minangka sinyal sing nimbulaké proses matèni.

spesifik macOS

Ing macOS, telpon sistem mateni ing kernel XNU nelpon fungsi psignal(SIGKILL,...). Coba deleng apa tumindak pangguna liyane ing ruang panganggo sing bisa diarani fungsi psignal. Ayo ngilangi telpon menyang fungsi psignal ing mekanisme internal kernel (sanajan bisa uga ora pati penting, kita bakal ninggalake artikel liyane πŸ™‚ - verifikasi teken, kesalahan memori, metu / mungkasi nangani, nglanggar proteksi file, etc. .

Ayo miwiti review kanthi fungsi lan telpon sistem sing cocog terminate_with_payload. Bisa dideleng manawa saliyane telpon mateni klasik, ana pendekatan alternatif sing khusus kanggo sistem operasi macOS lan ora ditemokake ing BSD. Prinsip operasi loro panggilan sistem uga padha. Iku telpon langsung menyang psignal fungsi kernel. Elinga uga sadurunge mateni proses, mriksa "cansignal" ditindakake - apa proses kasebut bisa ngirim sinyal menyang proses liyane; sistem ora ngidini aplikasi kanggo mateni proses sistem, contone.

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);
	}
...
}

diluncurake

Cara standar kanggo nggawe daemon ing wiwitan sistem lan ngontrol umure diluncurake. Elinga manawa sumber kasebut kanggo versi lawas launchctl nganti macOS 10.10, conto kode diwenehake kanggo tujuan ilustrasi. launchctl modern ngirim sinyal launchd liwat XPC, logika launchctl wis dipindhah menyang.

Ayo goleki kepiye aplikasi mandheg. Sadurunge ngirim sinyal SIGTERM, aplikasi nyoba kanggo mungkasi nggunakake "proc_terminate" sistem telpon.

<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));
		} 
...
<>

Ing hood, proc_terminate, senadyan jenenge, bisa ngirim ora mung psignal karo SIGTERM, nanging uga SIGKILL.

Mateni Ora Langsung - Watesan Sumber Daya

Kasus sing luwih menarik bisa dideleng ing telpon sistem liyane proses_kabijakan. Panggunaan umum saka panggilan sistem iki yaiku kanggo mbatesi sumber daya aplikasi, kayata indeksasi kanggo mbatesi wektu CPU lan kuota memori supaya sistem ora kalem kanthi signifikan dening aktivitas caching file. Yen aplikasi wis tekan watesan sumber, minangka bisa katon saka proc_apply_resource_actions fungsi, dikirim sinyal SIGKILL kanggo proses.

Senajan telpon sistem iki duweni potensi bisa mateni proses, sistem ora cekap mriksa hak proses nelpon sistem nelpon. Bener mriksa ana, nanging cukup nggunakake gendΓ©ra alternatif PROC_POLICY_ACTION_SET kanggo ngliwati kondisi iki.

Mula, yen sampeyan "matesi" kuota panggunaan CPU aplikasi (contone, mung ngidini 1 ns kanggo mbukak), sampeyan bisa mateni proses apa wae ing sistem kasebut. Mangkono, malware bisa mateni proses apa wae ing sistem, kalebu proses antivirus. Uga menarik yaiku efek sing kedadeyan nalika mateni proses kanthi pid 1 (launchctl) - panik kernel nalika nyoba ngolah sinyal SIGKILL :)

Cara nglindhungi proses lan ekstensi kernel ing macOS

Carane ngatasi masalah?

Cara sing paling gampang kanggo nyegah proses mati yaiku ngganti pointer fungsi ing tabel panggilan sistem. Sayange, cara iki ora pati penting amarga akeh alasan.

Kaping pisanan, simbol sing ngontrol lokasi memori sysent ora mung pribadi kanggo simbol kernel XNU, nanging ora bisa ditemokake ing simbol kernel. Sampeyan kudu nggunakake metode telusuran heuristik, kayata mbongkar fungsi kasebut kanthi dinamis lan nggoleki pointer.

Kapindho, struktur entri ing tabel gumantung saka panji sing dikompilasi kernel. Yen flag CONFIG_REQUIRES_U32_MUNGING diumumake, ukuran struktur bakal diganti - kolom tambahan bakal ditambahake sy_arg_munge32. Sampeyan kudu nindakake pemeriksaan tambahan kanggo nemtokake gendΓ©ra sing dikompilasi karo kernel, utawa minangka alternatif, mriksa penunjuk fungsi marang sing dikenal.

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
                                         */
};

Untunge, ing macOS versi modern, Apple nyedhiyakake API anyar kanggo nggarap proses. API Keamanan Endpoint ngidini klien menehi wewenang akeh panjalukan kanggo proses liyane. Mangkono, sampeyan bisa mblokir sinyal kanggo pangolahan, kalebu sinyal SIGKILL, nggunakake API kasebut ing ndhuwur.

#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;
}

Kajaba iku, Kabijakan MAC bisa didaftar ing kernel, sing nyedhiyakake cara proteksi sinyal (proc_check_signal kebijakan), nanging API ora didhukung kanthi resmi.

Proteksi ekstensi kernel

Saliyane nglindhungi proses ing sistem, nglindhungi extension kernel dhewe (kext) uga perlu. macOS nyedhiyakake kerangka kanggo pangembang supaya gampang ngembangake driver piranti IOKit. Saliyane nyediakake alat kanggo nggarap piranti, IOKit nyedhiyakake cara kanggo tumpukan driver nggunakake conto kelas C ++. Aplikasi ing ruang pangguna bakal bisa "nemokake" conto kelas sing kadhaptar kanggo nggawe hubungan kernel-userspace.

Kanggo ndeteksi jumlah instance kelas ing sistem, ana sarana ioclasscount.

my_kext_ioservice = 1
my_kext_iouserclient = 1

Ekstensi kernel apa wae sing pengin ndhaptar tumpukan driver kudu ngumumake kelas sing diturunake saka IOService, contone my_kext_ioservice ing kasus iki. Nyambungake aplikasi pangguna nyebabake nggawe conto anyar saka kelas sing diwenehi warisan saka IOUserClient, ing conto my_kext_iouserclient.

Nalika nyoba mbongkar driver saka sistem (kextunload printah), fungsi virtual "bool mungkasi (opsi IOOptionBits)" disebut. Iku cukup kanggo bali palsu ing telpon kanggo mungkasi nalika nyoba mbongkar kanggo mateni kextunload.

bool Kext::terminate(IOOptionBits options)
{

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

  return super::terminate(options);
}

GendΓ©ra IsUnloadAllowed bisa disetel dening IOUserClient nalika mbukak. Yen ana watesan download, printah kextunload bakal ngasilake output ing ngisor iki:

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.

Perlindhungan sing padha kudu ditindakake kanggo IOUserClient. Instance kelas bisa dibongkar nggunakake fungsi ruang pangguna IOKitLib "IOCatalogueTerminate (mach_port_t, flag uint32_t, deskripsi io_name_t);". Sampeyan bisa bali palsu nalika nelpon printah "siksa" nganti aplikasi userspace "mati", yaiku, fungsi "clientDied" ora disebut.

Proteksi file

Kanggo nglindhungi file, cukup nggunakake API Kauth, sing ngidini sampeyan mbatesi akses menyang file. Apple menehi pangembang kabar babagan macem-macem acara ing ruang lingkup; kanggo kita, operasi KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA lan KAUTH_VNODE_DELETE_CHILD penting. Cara paling gampang kanggo mbatesi akses menyang file yaiku kanthi path - kita nggunakake API "vn_getpath" kanggo entuk path menyang file lan mbandhingake awalan path. Elinga yen kanggo ngoptimalake ganti jeneng path folder file, sistem ora ngidini akses kanggo saben file, nanging mung kanggo folder dhewe sing wis diganti jeneng. Sampeyan perlu kanggo mbandhingakΓ© path tiyang sepah lan matesi KAUTH_VNODE_DELETE kanggo.

Cara nglindhungi proses lan ekstensi kernel ing macOS

Kerugian saka pendekatan iki bisa uga kinerja sing kurang amarga jumlah prefiks mundhak. Kanggo mesthekake yen perbandingan ora padha karo O(awalan*dawa), ing ngendi ater-ater minangka jumlah ater-ater, dawa minangka dawa senar, sampeyan bisa nggunakake otomatis finite deterministik (DFA) sing dibangun kanthi ater-ater.

Ayo dipikirake cara kanggo mbangun DFA kanggo prefiks tartamtu. Kita miwiti kursor ing awal saben awalan. Yen kabeh kursor nuduhake karakter sing padha, banjur tambahake saben kursor kanthi siji karakter lan elinga yen dawa baris sing padha luwih gedhe siji. Yen ana loro kursor kanthi simbol sing beda-beda, dibagi kursor dadi klompok miturut simbol sing dituduhake lan baleni algoritma kanggo saben klompok.

Ing kasus pisanan (kabeh karakter ing kursor padha), kita njaluk negara DFA sing mung siji transisi ing baris padha. Ing kasus kapindho, kita entuk tabel transisi ukuran 256 (jumlah karakter lan jumlah maksimum klompok) menyang negara sabanjure sing dipikolehi kanthi nelpon fungsi kasebut kanthi rekursif.

Ayo katon ing conto. Kanggo sakumpulan prefiks (β€œ/foo/bar/tmp/”, β€œ/var/db/foo/”, β€œ/foo/bar/aba/”, β€œfoo/bar/aac/”) sampeyan bisa njaluk ing ngisor iki DFA. Angka kasebut mung nuduhake transisi menyang negara liya; transisi liyane ora bakal pungkasan.

Cara nglindhungi proses lan ekstensi kernel ing macOS

Nalika ngliwati negara DKA, bisa uga ana 3 kasus.

  1. Negara pungkasan wis tekan - dalan kasebut dilindhungi, kita mbatesi operasi KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA lan KAUTH_VNODE_DELETE_CHILD
  2. Negara pungkasan ora tekan, nanging path "rampung" (terminator null wis tekan) - path punika tiyang sepah, iku perlu kanggo matesi KAUTH_VNODE_DELETE. Elinga yen vnode minangka folder, sampeyan kudu nambah '/' ing pungkasan, yen ora bisa mbatesi file "/foor/bar/t", sing ora bener.
  3. Negara pungkasan ora tekan, dalan ora rampung. Ora ana prefiks sing cocog karo iki, kita ora ngenalake watesan.

kesimpulan

Tujuan saka solusi keamanan sing dikembangake yaiku kanggo nambah tingkat keamanan pangguna lan data. Ing tangan siji, gol iki digayuh kanthi pangembangan produk piranti lunak Acronis, sing nutup kerentanan kasebut ing ngendi sistem operasi kasebut "lemah". Ing sisih liya, kita kudu ora nglirwakake nguatake aspek keamanan sing bisa ditingkatake ing sisih OS, utamane amarga nutup kerentanan kasebut nambah stabilitas kita dhewe minangka produk. Kerentanan kasebut dilaporake menyang Tim Keamanan Produk Apple lan wis didandani ing macOS 10.14.5 (https://support.apple.com/en-gb/HT210119).

Cara nglindhungi proses lan ekstensi kernel ing macOS

Kabeh iki mung bisa ditindakake yen utilitas sampeyan wis diinstal kanthi resmi menyang kernel. Yaiku, ora ana celah kanggo piranti lunak eksternal lan sing ora dikarepake. Nanging, kaya sing sampeyan deleng, malah nglindhungi program sing sah kayata antivirus lan sistem serep mbutuhake karya. Nanging saiki produk Acronis anyar kanggo macOS bakal duwe proteksi tambahan saka unloading saka sistem.

Source: www.habr.com

Add a comment