Si të mbroni proceset dhe zgjerimet e bërthamës në macOS

Përshëndetje, Habr! Sot do të doja të flisja për mënyrat se si mund të mbrohen proceset nga sulmet e keqbërësve në macOS. Për shembull, kjo është e dobishme për antivirusin ose sistemin e backup-it, sidomos në dritën e faktit se nën macOS ekzistojnë disa mënyra për "të vrarë" një proces. Lexoni më poshtë për këtë dhe për metodat e mbrojtjes.

Si të mbroni proceset dhe zgjerimet e bërthamës në macOS

Mënyra klasike për "të vrarë" një proces

Mënyra e njohur për të "vrarë" një proces është dërgimi i sinjalit SIGKILL procesit. Nëpërmjet bash-it, mund të thirret standardi "kill -SIGKILL PID" ose "pkill -9 NAME" për të vrarë. Komanda "kill" është e njohur që nga koha e UNIX dhe është e disponueshme jo vetëm në macOS, por edhe në sistemet e tjera UNIX-like.

Ashtu si në sistemet UNIX-like, macOS lejon kapjen e çdo sinjali për procesin përveç dy - SIGKILL dhe SIGSTOP. Në këtë artikull do të shqyrtohet kryesisht sinjali SIGKILL, si një sinjal që shkakton vrasjen e procesit.

Spezifikimi i macOS

NĂ« macOS, thirja e sistemit kill nĂ« bĂ«rthamen XNU thĂ«rret funksionin psignal(SIGKILL,
). Le tĂ« shohim cilat veprime tĂ« tjera tĂ« pĂ«rdoruesit nĂ« userspace mund tĂ« thĂ«rrasin funksionin psignal. Do tĂ« eliminojmĂ« thirrjet e funksionit psignal nĂ« mekanizmat e brendshĂ«m tĂ« bĂ«rthamĂ«s (edhe pse ato mund tĂ« jenĂ« tĂ« ndĂ«rlikuara, do t'i lĂ«mĂ« ato pĂ«r njĂ« artikull tjetĂ«r 🙂 - kontrolli i nĂ«nshkrimit, gabimet e memories, pĂ«rpunimi i exit/terminate, shkelja e mbrojtjes sĂ« skedarĂ«ve, etj.).

TĂ« fillojmĂ« shqyrtimin me funksionin dhe thirrjen e pĂ«rkatshme tĂ« sistemit terminate_with_payload. ËshtĂ« e qartĂ« se pĂ«rveç thirrjes klasike tĂ« kill, ekziston njĂ« qasje alternative, qĂ« Ă«shtĂ« specifike pĂ«r sistemin operativ macOS dhe nuk gjendet nĂ« BSD. Principe e funksionimit tĂ« tĂ« dy thirrjeve tĂ« sistemit janĂ« gjithashtu tĂ« ngjashme. Ato pĂ«rbĂ«jnĂ« thirrje tĂ« drejtpĂ«rdrejta tĂ« funksionit tĂ« bĂ«rthamĂ«s psignal. Gjithashtu, vĂ«mĂ« re se pĂ«rpara se tĂ« vrasĂ« njĂ« proces bĂ«het njĂ« kontroll "cansignal" - a mund tĂ« dĂ«rgojĂ« procesi njĂ« sinjal tek njĂ« proces tjetĂ«r, sistemi nuk lejon asnjĂ« aplikacion tĂ« vrasĂ« proceset sistemike, pĂ«r shembull.

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) {
			d/*
		 * 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);
	}
...
}

launchd

Mënyra standarde për krijimin e demonëve në nisjen e sistemit dhe kontrollimin e kohës së jetës së tyre është launchd. Dua të theksoj se kodet e dhëna janë për një version të vjetër të launchctl para macOS 10.10, dhe shembujt e kodit janë dhënë për ilustërim. Launchctl modern dërgon sinjale launchd përmes XPC, logjika e launchctl është transferuar në të.

Le tĂ« shohim se si ndodhet ndalimi i aplikacioneve. Para dĂ«rgimit tĂ« sinjalit SIGTERM, aplikacionet pĂ«rpiqen tĂ« ndalohen pĂ«rmes thirrjes sĂ« sistemit “proc_terminate”.

...
	error = proc_terminate(j->p, &sig);
	if (error) {
		job_log(j, LOG_ERR | LOG_CONSOLE, "Nuk mund të ndalohet puna: %d: %s", error, strerror(error));
		job_log(j, LOG_NOTICE | LOG_CONSOLE, "Përdorimi i opsionit rezervë për të ndaluar punën...");
		error = kill2(j->p, SIGTERM);
		if (error) {
			job_log(j, LOG_ERR, "Nuk mund të dërgohet sinjali punës: %d: %s", error, strerror(error));
		} 
...

Nën kapak, proc_terminate, pavarësisht emrit të tij, mund të dërgojë jo vetëm psignal me SIGTERM, por edhe SIGKILL.

Vrasja e tĂ«rthortĂ« — kufizimi i burimeve

Një rast më interesant mund të shihet në një thirrje tjetër të sistemit process_policy. Përdorimi standard i kësaj thirrjeje të sistemit është kufizimi i burimeve të aplikacioneve, për shembull, për indeksuesin, kufizimi i kuotës së kohës së procesorit dhe memories, në mënyrë që sistemi të mos ngadalësohet ndjeshëm nga veprimet e ruajtjes së skedarëve. Nëse aplikacioni arrin kufizimin e burimeve, siç mund të shihet nga funksioni proc_apply_resource_actions, procesit i dërgohet sinjali SIGKILL.

Pavarësisht se kjo thirrje e sistemit mund të prodhojë potencialisht vrasjen e procesit, sistemi nuk ka kontrolluar adekuat të drejtat e procesit që bënte thirrjen e sistemit. Në të vërtetë, kontrolli ekzistonte, por mjafton të përdoret flaka alternative PROC_POLICY_ACTION_SET për të anashkaluar këtë kusht.

Nga kĂ«tu, nĂ«se "kufizohet" kuota e pĂ«rdorimit tĂ« CPU nga aplikacioni (pĂ«r shembull, lejohet tĂ« funksionojĂ« vetĂ«m 1 ns), atĂ«herĂ« mund tĂ« vrarĂ« çdo proces nĂ« sistem. KĂ«shtu, njĂ« malware mund tĂ« vrasĂ« çdo proces nĂ« sistem, pĂ«rfshirĂ« procesin e antivirusit. Gjithashtu, Ă«shtĂ« interesante efekti qĂ« ndodh kur vritet procesi me pid 1 (launchctl) — panic nĂ« bĂ«rthamĂ« kur pĂ«rpiqet tĂ« pĂ«rpunojĂ« sinjalin SIGKILL 🙂

Si të mbroni proceset dhe zgjerimet e bërthamës në macOS

Si të zgjidhni problemin?

Mënyra më e drejtpërdrejtë për të ndaluar vrasjen e një procesi është të zëvendësohet treguesi në tabelën e thirrjeve sistemore. Fatkeqësisht, ky opcion është i ndërlikuar për shumë arsye

Së pari, simboli që përgjigjet për pozicionin e sysent në memorie është jo vetëm një simbol privat i bërthames XNU, por nuk mund të gjendet as në simbolet e bërthamës. Do të duhej të përdoren metoda heuristike për të gjetur, për shembull, disasemblim dinamik të funksionit dhe kërkimin e treguesit në të.

SĂ« dyti, struktura e shĂ«nimeve nĂ« tabelĂ« varet nga flamujt me tĂ« cilĂ«t Ă«shtĂ« ndĂ«rtuar bĂ«rthama. NĂ«se Ă«shtĂ« shpallur flamuri CONFIG_REQUIRES_U32_MUNGING, atĂ«herĂ« madhĂ«sia e strukturĂ«s do tĂ« ndryshojĂ« — do tĂ« shtohet njĂ« fushĂ« e re. sy_arg_munge32. Necesiteti pĂ«r tĂ« realizuar njĂ« kontroll tĂ« mĂ«tejshĂ«m se me cilin flamur Ă«shtĂ« kompiluar bĂ«rthama, si njĂ« alternativĂ« pĂ«r tĂ« pĂ«rputhur treguesit me funksionet e njohura.

struct sysent {         
        /* tabela e thirrjeve sistemore */
        sy_call_t       *sy_call;       /* funksioni që implementon */
#if CONFIG_REQUIRES_U32_MUNGING || (__arm__ && (__BIGGEST_ALIGNMENT__ > 4))
        sy_munge_t      *sy_arg_munge32; /* procesi i thirrjes sistemore për argumentet 32-bit */
#endif
        int32_t         sy_return_type; /* llojet e kthimit të thirrjeve sistemore */
        int16_t         sy_narg;        /* numri i argumenteve */
        uint16_t        sy_arg_bytes;   /* Madhësia totale e argumenteve në byte për
                                         * thirrjet sistemore 32-bit
                                         */
};

Me fat, në versionet moderne të macOS, Apple ofron një API të re për të punuar me proceset. API e Sigurisë së Endpoint lejon klientët të autorizojnë shumë kërkesa për procese të tjera. Kështu, është e mundur të bllokohen çdo sinjal për proceset, përfshirë sinjalin SIGKILL me ndihmën e API-së së përmendur më lart.

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

Po ashtu, në bërthamë mund të regjistrohet një Politika MAC, e cila ofron një metodë mbrojtjeje nga sinjalet (policy proc_check_signal), megjithatë API nuk mbështetet zyrtarisht.

Mbrojtja e zgjerimit të bërthamës

Përveç mbrojtjes së proceseve në sistem, është e nevojshme të mbrohet edhe vet zgjerimi i bërthamës (kext). macOS ofron një kuadër për zhvilluesit për zhvillimin e lehtë të drejtorëve të pajisjeve IOKit. Përveç sigurimit të mjeteve për punën me pajisjet, IOKit siguron metoda për grumbullimin e drejtorëve (driver stacking) me anë të instancave të klasave C++. Një aplikacion në userspace mund të "gjejë" një instancë të regjistruar të klasës për të vendosur lidhje mes kernel dhe userspace.

Për të zbuluar numrin e instancave të klasave në sistem, ekziston utilita ioclasscount.

my_kext_ioservice = 1
my_kext_iouserclient = 1

Çdo zgjerim bĂ«rthame, qĂ« dĂ«shiron tĂ« regjistrohet nĂ« grumbullin e drejtorĂ«ve, duhet tĂ« shpallĂ« njĂ« klasĂ« tĂ« trashĂ«guar nga IOService, pĂ«r shembull, my_kext_ioservice nĂ« kĂ«tĂ« rast. KĂ«rkesa e aplikacioneve pĂ«r pĂ«rdorues krijon njĂ« instancĂ« tĂ« re tĂ« klasĂ«s, e cila trashĂ«gohet nga IOUserClient, nĂ« shembullin my_kext_iouserclient.

Kur bëhet një përpjekje për të shkarkuar drejtorin nga sistemi (komanda kextunload), thirret funksioni virtual "bool terminate(IOOptionBits options)". Mjafton të kthehet false në thirrjen e funksionit terminate gjatë përpjekjes për shkarkim, për të ndaluar kextunload.

bool Kext::terminate(IOOptionBits options)
{

  if (!IsUnloadAllowed)
  {
    // Shkarkimi nuk lejohet, duke u kthyer false
    return false;
  }

  return super::terminate(options);
}

Flamuri IsUnloadAllowed mund të vendoset nga IOUserClient gjatë ngarkimit. Nëse ka një kufizim në ngarkim, komanda kextunload do të kthejë daljen e mëposhtme:

admin@admins-Mac drivermanager % sudo kextunload ./test.kext
Fjalëkalimi:
(kernel) Nuk mund të hiqet kext my.kext.test; shërbimet dështuan në mbyllje - 0xe00002c7.
Dështoi eliminimi i my.kext.test - (iokit/common) funksion i papërshtatshëm.

Mbrojtja e ngjashme duhet të realizohet edhe për IOUserClient. Instancat e klasave mund të shkarkohen me anë të funksionit në userspace të IOKitLib "IOCatalogueTerminate(mach_port_t, uint32_t flag, io_name_t description);". Mund të kthehen false në thirrjen e komandës "terminate" derisa aplikacioni në userspace të "vdes", pra derisa të mos thirret funksioni "clientDied".

Mbrojtja e skedarëve

PĂ«r tĂ« mbrojtur skedarĂ«t, mjafton tĂ« pĂ«rdorim Kauth API, i cili lejon kufizimin e aksesit nĂ« skedarĂ«. Apple u ofron zhvilluesve njoftime pĂ«r ngjarje tĂ« ndryshme nĂ« scope, pĂ«r ne janĂ« tĂ« rĂ«ndĂ«sishme operacionet KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA dhe KAUTH_VNODE_DELETE_CHILD. TĂ« kufizosh aksesin nĂ« skedarĂ« Ă«shtĂ« mĂ« e lehtĂ« sipas rrugĂ«s — pĂ«rdorim API 'vn_getpath' pĂ«r tĂ« marrĂ« rrugĂ«n e skedarit dhe kryejmĂ« krahasimin e prefiksit tĂ« rrugĂ«s. Vlen tĂ« theksohet se pĂ«r optimizimin e ndryshimit tĂ« emrave tĂ« rrugĂ«ve tĂ« dosjeve, sistemi nuk autorizon aksesin pĂ«r çdo skedar, por vetĂ«m pĂ«r dosjen e vetĂ« qĂ« Ă«shtĂ« riblerĂ«. Duhet tĂ« kryhet krahasimi i rrugĂ«s prind dhe tĂ« kufizohet KAUTH_VNODE_DELETE pĂ«r tĂ«.

Si të mbroni proceset dhe zgjerimet e bërthamës në macOS

Një dobësi e këtij qasjes mund të jetë performanca e ulët me rritjen e numrit të prefikseve. Për të bërë që krahasimi të mos jetë O(prefix*length), ku prefix është numri i prefikseve, length është gjatësia e vargjeve, mund të përdorim automatin e fundëm të përcaktuar (DFA) të ndërtuar sipas prefikseve.

Le të shqyrtojmë mënyrën e ndërtimit të DFA për këtë grup prefikse. Inicizojmë kursoret në fillim të çdo prefiksi. Nëse të gjitha kursoret tregojnë për të njëjtin simbol, atëherë rrisim çdo kurser me një simbol dhe mbajmë mend se gjatësia e vargjeve të njëjtë është rritur me një. Nëse ekzistojnë dy kursore, simbolet nën të cilat janë ndryshme, i ndajmë kursorët në grupe sipas simbolit që tregojnë dhe e përsërisim algoritmin për secilën grup.

Në rastin e parë (të gjithë simbolët nën kursorë janë të njëjtë) marrim një gjendje të DFA-së që ka vetëm një kalim sipas vargjeve të njëjta. Në rastin e dytë, marrim një tabelë kalimesh me madhësi 256 (numri i simboleve dhe numri maksimal i grupeve) në gjendjet e ardhshme, të fituara gjatë thirrjes rikursive të funksionit.

Le të marrim një shembull. Për grupin e prefikseve ('\/foo\/bar\/tmp\/', '\/var\/db\/foo\/', '\/foo\/bar\/aba\/', 'foo\/bar\/aac\/') mund të marrim këtë DFA. Në vizatim janë shënuar vetëm kalimet që çojnë në gjendje të tjera, kalimet e tjera nuk do të jenë përfundimtare.

Si të mbroni proceset dhe zgjerimet e bërthamës në macOS

Kur kalojmë përmes gjendjeve të DFA-së, mund të ndodhin 3 raste.

  1. ËshtĂ« arritur gjendja pĂ«rfundimtare — rruga Ă«shtĂ« e mbrojtur, kufizojmĂ« operacionet KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA dhe KAUTH_VNODE_DELETE_CHILD.
  2. Nuk u arrit nĂ« njĂ« gjendje pĂ«rfundimtare, por rruga "pĂ«rfundoi" (u arrit terminatori zero) — rruga Ă«shtĂ« prindĂ«rore, duhet tĂ« kufizohet KAUTH_VNODE_DELETE. VĂ«rejmĂ« se, nĂ«se vnode Ă«shtĂ« njĂ« dosje, duhet tĂ« shtohet nĂ« fund ‘\/’, pĂ«rndryshe mund tĂ« bĂ«het kufizimi i skedarit “\/foor\/bar\/t”, qĂ« Ă«shtĂ« e gabuar.
  3. Nuk u arrit në një gjendje përfundimtare, rruga nuk përfundoi. Asnjë nga prefiksat nuk i përputhet kësaj, nuk vendosim kufizime.

Përfundim

Objektivi i zgjidhjeve të sigurisë në zhvillim është të rrisë nivelin e sigurisë së përdoruesit dhe të dhënave të tij. Nga njëra anë, ky objektiv sigurohet përmes zhvillimit të një produkti software Acronis, që mbulon ato dobësi ku "është e dobët" vetë sistemi operativ. Nga ana tjetër, nuk duhet të injorojmë forcimin e aspekteve të sigurisë që mund të përmirësohen nga ana e OS, sidomos duke pasur parasysh se mbyllja e dobësive të tilla rrit qëndrueshmërinë tonë si produkt. Dobësia u raportua nga Ekipi i Sigurisë së Produkteve të Apple dhe u korrigjua në macOS 10.14.5 (https://support.apple.com/en-gb/HT210119).

Si të mbroni proceset dhe zgjerimet e bërthamës në macOS

Kjo mund të bëhet vetëm nëse utilitari juaj është instaluar zyrtarisht në kernel. Kështu që për softuerin e jashtëm dhe të padëshiruar nuk ka rrugë tjetër. Megjithatë, siç e shihni, edhe për të mbrojtur programet legjitime, si antivirus dhe sistemi i rezervimit, kërkohet punë. Megjithatë, tani produktet e reja Acronis për macOS do të kenë mbrojtje shtesë kundër shkarkimit nga sistemi.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster