Cumu prutegge i prucessi è l'estensioni di kernel in macOS

Ciao, Habr! Oghje vogliu parlà di cumu pudete prutegge i prucessi da attacchi da l'attaccanti in macOS. Per esempiu, questu hè utile per un sistema antivirus o di salvezza, soprattuttu chì sottu macOS ci sò parechje manere di "ammazzà" un prucessu. Leghjite nantu à questu è i metudi di prutezzione sottu u cut.

Cumu prutegge i prucessi è l'estensioni di kernel in macOS

U modu classicu di "ammazzà" un prucessu

Una manera ben cunnisciuta di "ammazzà" un prucessu hè di mandà un signalu SIGKILL à u prucessu. Per mezu di bash pudete chjamà u standard "kill -SIGKILL PID" o "pkill -9 NAME" per tumbà. U cumandamentu "uccisione" hè cunnisciutu da i tempi di UNIX è hè dispunibule micca solu in macOS, ma ancu in altri sistemi simili à UNIX.

Cum'è in sistemi UNIX-like, macOS permette di interceptà qualsiasi signali à un prucessu eccettu dui - SIGKILL è SIGSTOP. Questu articulu hà da fucalizza principarmenti nantu à u signale SIGKILL cum'è un signalu chì provoca un prucessu per esse uccisu.

specifiche di macOS

In macOS, a chjama di u sistema di uccisione in u kernel XNU chjama a funzione psignal(SIGKILL,...). Pruvemu di vede ciò chì altre azzioni di l'utilizatori in u spaziu di l'utilizatori ponu esse chjamati da a funzione psignal. Eliminate e chjama à a funzione psignal in i miccanismi interni di u kernel (ancu se ponu esse micca triviali, li lasciaremu per un altru articulu 🙂 - verificazione di firma, errori di memoria, gestione di uscita / terminazione, violazioni di prutezzione di file, etc. .

Cuminciamu a rivista cù a funzione è a chjama di u sistema currispundente terminate_with_payload. Pò esse vistu chì in più di u classicu kill call, ci hè un approcciu alternativu chì hè specificu à u sistema operatore macOS è ùn si trova micca in BSD. I principii di u funziunamentu di i dui chjamati sistema sò ancu simili. Sò chjamati diretti à a funzione di u kernel psignal. Innota ancu chì prima di tumbà un prucessu, un cuntrollu di "cansignal" hè realizatu - se u prucessu pò mandà un signalu à un altru prucessu; u sistema ùn permette micca alcuna applicazione per tumbà i prucessi di u sistema, per esempiu.

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

lanciatu

U modu standard per creà demoni à l'iniziu di u sistema è cuntrullà a so vita hè lanciata. Per piacè nutate chì e fonti sò per a vechja versione di launchctl finu à macOS 10.10, esempi di codice sò furniti per scopi illustrativi. Launchctl mudernu manda signali launchd via XPC, a logica launchctl hè stata spustata.

Fighjemu cumu si fermanu esattamente l'applicazioni. Prima di mandà u signale SIGTERM, l'applicazione hè pruvata à esse firmata cù a chjama di u sistema "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));
		} 
...
<>

Sottu u cappucciu, proc_terminate, malgradu u so nome, pò mandà micca solu psignal cù SIGTERM, ma ancu SIGKILL.

Uccidiu indirettu - Limite di risorse

Un casu più interessante pò esse vistu in un altru sistema chjamatu prucessu_pulitica. Un usu cumuni di sta chjama di u sistema hè di limità e risorse di l'applicazioni, cum'è per un indexatore per limità u tempu di CPU è e quote di memoria per chì u sistema ùn hè micca rallentatu significativamente da attività di cache di file. Se una applicazione hà righjuntu u so limitu di risorse, cum'è pò esse vistu da a funzione proc_apply_resource_actions, un signalu SIGKILL hè mandatu à u prucessu.

Ancu s'ellu sta chjama di u sistema puderia putenzialmente tumbà un prucessu, u sistema ùn hà micca verificatu bè i diritti di u prucessu chì chjama a chjama di u sistema. Veramente cuntrolla esisteva, ma hè abbastanza à utilizà a bandiera alternativa PROC_POLICY_ACTION_SET per scaccià sta cundizione.

Dunque, se "limitate" a quota d'utilizazione di CPU di l'applicazione (per esempiu, permettendu solu 1 ns per eseguisce), pudete tumbà ogni prucessu in u sistema. Cusì, u malware pò tumbà ogni prucessu nantu à u sistema, cumpresu u prucessu antivirus. Interessante hè ancu l'effettu chì si trova quandu uccide un prucessu cù pid 1 (launchctl) - panicu di u kernel quandu si prova à processà u signale SIGKILL :)

Cumu prutegge i prucessi è l'estensioni di kernel in macOS

Cumu risolve u prublema?

U modu più simplice per impedisce un prucessu da esse uccisu hè di rimpiazzà l'indicatore di funzione in a tabella di chjama di u sistema. Sfurtunatamente, stu metudu ùn hè micca trivial per parechje ragioni.

Prima, u simbulu chì cuntrolla u locu di memoria di sysent ùn hè micca solu privatu à u simbulu di u kernel XNU, ma ùn pò micca esse truvatu in i simboli di u kernel. Duverete aduprà metudi di ricerca heuristic, cum'è disassemblamentu dinamicu di a funzione è circà un punteru in questu.

Siconda, a struttura di e voci in a tavula dipende da i bandieri cù quale u kernel hè statu compilatu. Se a bandiera CONFIG_REQUIRES_U32_MUNGING hè dichjarata, a dimensione di a struttura serà cambiata - un campu supplementu serà aghjuntu sy_arg_munge32. Hè necessariu di realizà una verificazione supplementaria per determinà quale bandiera hè stata cumpilata cù u kernel, o in alternativa, verificate l'indicatori di funzione contr'à quelli cunnisciuti.

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

Fortunatamente, in e versioni muderni di macOS, Apple furnisce una nova API per travaglià cù prucessi. L'API Endpoint Security permette à i clienti d'autorizà parechje dumande à altri prucessi. Cusì, pudete bluccà qualsiasi signali à i prucessi, cumpresu u signale SIGKILL, usendu l'API sopra citata.

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

In listessu modu, una pulitica MAC pò esse registrata in u kernel, chì furnisce un metudu di prutezzione di signale (politica proc_check_signal), ma l'API ùn hè micca supportatu ufficialmente.

Prutezzione di l'estensione di u kernel

In più di prutezzione di i prucessi in u sistema, a prutezzione di l'estensione di u kernel stessu (kext) hè ancu necessariu. macOS furnisce un framework per i sviluppatori per sviluppà facilmente i driver di dispositivi IOKit. In più di furnisce strumenti per travaglià cù i dispositi, IOKit furnisce metudi per stacking driver usendu istanze di classi C++. Una applicazione in u spaziu di l'utilizatori puderà "truvà" una istanza registrata di a classe per stabilisce una relazione kernel-userspace.

Per detectà u numeru di istanze di classi in u sistema, ci hè l'utilità ioclasscount.

my_kext_ioservice = 1
my_kext_iouserclient = 1

Qualchese estensione di u kernel chì vulete registrà cù a pila di driver deve dichjarà una classa chì eredita da IOService, per esempiu my_kext_ioservice in questu casu. A cunnessione di l'applicazioni d'utilizatori provoca a creazione di una nova istanza di a classe chì eredita da IOUserClient, in l'esempiu my_kext_iouserclient.

Quandu pruvate di scaricà un driver da u sistema (cumandamentu kextunload), a funzione virtuale "bool terminate (IOOptionBits options)" hè chjamata. Hè abbastanza per vultà falsu nantu à a chjama per finisce quandu pruvate di scaricà per disattivà kextunload.

bool Kext::terminate(IOOptionBits options)
{

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

  return super::terminate(options);
}

A bandiera IsUnloadAllowed pò esse stabilita da IOUserClient quandu si carica. Quandu ci hè un limitu di scaricamentu, u cumandimu kextunload restituverà a seguente output:

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.

A prutezzione simile deve esse fatta per IOUserClient. L'istanze di classi ponu esse scaricate usendu a funzione di u spaziu d'utilizatore IOKitLib "IOCatalogueTerminate (mach_port_t, uint32_t flag, io_name_t description);". Pudete vultà falsu quandu chjamate u cumandamentu "terminate" finu à chì l'applicazione di u spaziu di l'utilizatori "morte", vale à dì, a funzione "clientDied" ùn hè micca chjamata.

Prutezzione di i schedari

Per prutege i schedari, hè abbastanza à utilizà l'API Kauth, chì vi permette di limità l'accessu à i schedari. Apple furnisce i sviluppatori notificazioni nantu à diversi avvenimenti in u scopu; per noi, l'operazioni KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA è KAUTH_VNODE_DELETE_CHILD sò impurtanti. U modu più faciule per limità l'accessu à i fugliali hè per via - usemu l'API "vn_getpath" per uttene u percorsu à u schedariu è paragunate u prefissu di u percorsu. Innota chì per ottimisà a rinominazione di i percorsi di u cartulare di u schedariu, u sistema ùn autorizeghja micca l'accessu à ogni schedariu, ma solu à u cartulare stessu chì hè statu rinominatu. Hè necessariu paragunà a strada parentale è restringe KAUTH_VNODE_DELETE per questu.

Cumu prutegge i prucessi è l'estensioni di kernel in macOS

U svantaghju di questu approcciu pò esse un rendimentu bassu cum'è u numeru di prefissi aumenta. Per assicurà chì a comparazione ùn hè micca uguale à O (prefissu * lunghezza), induve u prefissu hè u numeru di prefissi, a lunghezza hè a lunghezza di a stringa, pudete aduprà un automatu finitu deterministicu (DFA) custruitu da prefissi.

Cunsideremu un metudu per custruisce un DFA per un determinatu settore di prefissi. Inizialemu i cursori à u principiu di ogni prefissu. Sì tutti i cursori puntanu à u stessu caratteru, allora aumenta ogni cursore da un caratteru è ricordate chì a lunghezza di a stessa linea hè più grande da unu. Se ci sò dui cursori cù simboli diffirenti, dividite i cursori in gruppi secondu u simbulu chì puntanu è ripetite l'algoritmu per ogni gruppu.

In u primu casu (tutti i caratteri sottu i cursori sò listessi), avemu un statu DFA chì hà solu una transizione in a stessa linea. In u sicondu casu, avemu un tavulu di transizzioni di grandezza 256 (numaru di caratteri è numeru massimu di gruppi) à stati successivi ottenuti chjamendu recursivamente a funzione.

Fighjemu un esempiu. Per un inseme di prefissi ("/foo/bar/tmp/", "/var/db/foo/", "/foo/bar/aba/", "foo/bar/aac/") pudete ottene u seguente DFA. A figura mostra solu transizioni chì portanu à altri stati; altre transizioni ùn saranu micca finali.

Cumu prutegge i prucessi è l'estensioni di kernel in macOS

Quandu passa per i stati DKA, pò esse 3 casi.

  1. U statu finali hè statu righjuntu - a strada hè prutetta, limitemu l'operazioni KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA è KAUTH_VNODE_DELETE_CHILD
  2. U statu finali ùn hè statu righjuntu, ma u percorsu "finitu" (u null terminator hè stata ghjunta) - a strada hè un parente, hè necessariu limità KAUTH_VNODE_DELETE. Nota chì se vnode hè un cartulare, avete bisognu di aghjunghje un '/' à a fine, altrimenti pò limità à u schedariu "/foor/bar/t", chì hè incorrectu.
  3. U statu finali ùn hè statu ghjuntu, a strada ùn hè micca finita. Nisunu di i prefissi currispondenu à questu, ùn introducemu micca restrizioni.

cunchiusioni

U scopu di e suluzioni di sicurità sviluppatu hè di aumentà u livellu di sicurità di l'utilizatore è i so dati. Da una banda, stu scopu hè ottenutu da u sviluppu di u pruduttu software Acronis, chì chjude quelli vulnerabili induve u sistema operatore stessu hè "debule". Per d 'altra banda, ùn duvemu micca trascuratà di rinfurzà quelli aspetti di sicurezza chì ponu esse migliurati da u latu di u SO, soprattuttu chì a chjusura di tali vulnerabilità aumenta a nostra stabilità cum'è un pruduttu. A vulnerabilità hè stata signalata à u Team di Sicurezza di u Produttu Apple è hè stata riparata in macOS 10.14.5 (https://support.apple.com/en-gb/HT210119).

Cumu prutegge i prucessi è l'estensioni di kernel in macOS

Tuttu chistu pò esse fattu solu se a vostra utilità hè stata stallata ufficialmente in u kernel. Vale à dì, ùn ci sò micca tali loopholes per u software esternu è indesideratu. Tuttavia, cum'è pudete vede, ancu a prutezzione di i prugrammi legittimi cum'è l'antivirus è i sistemi di salvezza necessitanu travagliu. Ma avà i novi prudutti Acronis per macOS anu una prutezzione supplementaria contra a scaricamentu da u sistema.

Source: www.habr.com

Add a comment