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.
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
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
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
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 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
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.
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.
Quandu passa per i stati DKA, pò esse 3 casi.
- U statu finali hè statu righjuntu - a strada hè prutetta, limitemu l'operazioni KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA è KAUTH_VNODE_DELETE_CHILD
- 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.
- 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).
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