Ahoana ny fiarovana ny dingana sy ny fanitarana kernel amin'ny macOS

Salama, Habr! Androany aho dia te hiresaka momba ny fomba ahafahanao miaro ny fizotran'ny fanafihana ataon'ny mpanafika amin'ny macOS. Ohatra, ilaina ho an'ny antivirus na rafitra backup izany, indrindra satria amin'ny macOS dia misy fomba maro "mamono" dingana iray. Vakio ny momba izany sy ny fomba fiarovana eo ambanin'ny fanapahana.

Ahoana ny fiarovana ny dingana sy ny fanitarana kernel amin'ny macOS

Ny fomba mahazatra "mamono" dingana iray

Ny fomba iray fanta-daza amin'ny "famonoana" dingana iray dia ny fandefasana famantarana SIGKILL amin'ny dingana. Amin'ny alΓ lan'ny bash dia azonao atao ny miantso ny fenitra "kill -SIGKILL PID" na "pkill -9 NAME" hamono. Ny baiko "kill" dia fantatra hatramin'ny andron'ny UNIX ary tsy hita amin'ny macOS ihany, fa amin'ny rafitra hafa mitovy amin'ny UNIX ihany koa.

Tahaka ny amin'ny rafitra mitovy amin'ny UNIX, ny macOS dia mamela anao hisakana famantarana amin'ny dingana iray afa-tsy ny roa - SIGKILL sy SIGSTOP. Ity lahatsoratra ity dia hifantoka voalohany indrindra amin'ny famantarana SIGKILL ho famantarana izay mahatonga ny dingana ho faty.

macOS manokana

Amin'ny macOS, ny antson'ny rafitra famonoana ao amin'ny kernel XNU dia miantso ny fiasan'ny psignal(SIGKILL,...). Andeha hojerentsika hoe inona ny hetsika mpampiasa hafa ao amin'ny espace mpampiasa azo antsoina amin'ny asa psignal. Andeha isika hanaisotra ny antso amin'ny fiasan'ny psignal amin'ny mekanika anatiny ao amin'ny kernel (na dia mety ho tsy misy dikany aza izy ireo, fa avelao izy ireo ho an'ny lahatsoratra hafa πŸ™‚ - fanamarinana sonia, fahadisoana fitadidiana, fivoahana / fiatoana, fanitsakitsahana ny fiarovana ny rakitra, Sns

Andao hanomboka ny famerenana amin'ny fiasa sy ny antso an-tariby mifandraika amin'izany terminate_with_payload. Hita fa ho fanampin'ny antso famonoana mahazatra, dia misy fomba hafa izay manokana amin'ny rafitra fiasa macOS ary tsy hita ao amin'ny BSD. Mitovy ihany koa ny fitsipiky ny fiasan'ny antso an-tariby roa. Izy ireo dia antso mivantana amin'ny kernel function psignal. Mariho ihany koa fa alohan'ny hamonoana dingana iray dia atao ny fanamarinana "cansignal" - raha afaka mandefa famantarana amin'ny dingana hafa ilay dingana; tsy avelan'ny rafitra misy fampiharana hamono ny fizotran'ny rafitra, ohatra.

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

launchd

Ny fomba mahazatra hamoronana daemon amin'ny fanombohana ny rafitra sy mifehy ny androm-piainany dia natomboka. Mariho fa ny loharano dia natao ho an'ny dikan-teny taloha an'ny launchctl hatramin'ny macOS 10.10, ohatra kaody dia omena ho an'ny tanjona fanoharana. Ny launchctl maoderina dia mandefa famantarana fanombohana amin'ny alΓ lan'ny XPC, nafindra tao ny lojika launchctl.

Andeha hojerentsika hoe ahoana marina ny fampitsaharana ny fampiharana. Alohan'ny handefasana ny mari-pamantarana SIGTERM, dia ezahina ajanona ny fampiharana amin'ny alΓ lan'ny antso an-tariby "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));
		} 
...
<>

Eo ambanin'ny satroka, proc_terminate, na dia eo aza ny anarany, dia afaka mandefa tsy ny psignal miaraka amin'ny SIGTERM, fa SIGKILL ihany koa.

Famonoana ankolaka - fetran'ny loharanon-karena

Tranga mahaliana kokoa dia hita amin'ny antso an-tariby hafa process_policy. Ny fampiasana mahazatra an'ity antso an-tariby ity dia ny famerana ny loharanon'ny rindranasa, toy ny fanondroana mba hamerana ny fotoanan'ny CPU sy ny fetran'ny fitadidiana mba tsy hampiadana be ny rafitra amin'ny hetsika caching rakitra. Raha toa ka nahatratra ny fetran'ny loharanon-karena ny fampiharana iray, araka ny hita amin'ny asa proc_apply_resource_actions, dia misy famantarana SIGKILL alefa any amin'ny dingana.

Na dia mety hamono dingana iray aza ity antson'ny rafitra ity, tsy nojeren'ny rafitra araka ny tokony ho izy ny zon'ny dingana miantso ny antso an-tariby. Tena manamarina nisy, fa ampy ny mampiasa ny saina hafa PROC_POLICY_ACTION_SET mba hialana amin'ity fepetra ity.

Noho izany, raha "mametra" ny fetran'ny fampiasana CPU ianao (ohatra, mamela ny 1 ns fotsiny handeha), dia azonao atao ny mamono ny dingana rehetra ao amin'ny rafitra. Noho izany, ny malware dia afaka mamono ny dingana rehetra amin'ny rafitra, anisan'izany ny fizotran'ny antivirus. Mahaliana ihany koa ny vokatra mitranga rehefa mamono dingana iray miaraka amin'ny pid 1 (launchctl) - panic kernel rehefa manandrana manamboatra ny famantarana SIGKILL :)

Ahoana ny fiarovana ny dingana sy ny fanitarana kernel amin'ny macOS

Ahoana no hamahana ny olana?

Ny fomba tsotra indrindra hisorohana ny dingana tsy ho faty dia ny fanoloana ny tondro fiasa ao amin'ny latabatra antso an-tariby. Indrisy anefa fa tsy misy dikany io fomba io noho ny antony maro.

Voalohany, ny marika mifehy ny toerana fitadidian'ny sysent dia tsy mitokana ho an'ny marika kernel XNU, fa tsy hita ao amin'ny marika kernel. Tsy maintsy mampiasa fomba fikarohana heuristic ianao, toy ny famongorana ny fiasa sy ny fitadiavana tondro ao anatiny.

Faharoa, ny firafitry ny fidirana ao amin'ny tabilao dia miankina amin'ny saina izay nanangona ny kernel. Raha ambara ny saina CONFIG_REQUIRES_U32_MUNGING dia hovana ny haben'ny rafitra - hisy saha fanampiny hiampy sy_arg_munge32. Ilaina ny manao fanaraha-maso fanampiny mba hamaritana hoe iza no saina natambatra tamin'ny kernel, na koa, jereo ny tondro fiasa amin'ireo fantatra.

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

Soa ihany, amin'ny dikan-teny maoderina amin'ny macOS, Apple dia manome API vaovao ho an'ny fiasana amin'ny dingana. Ny Endpoint Security API dia ahafahan'ny mpanjifa manome alalana fangatahana maro amin'ny dingana hafa. Noho izany, azonao atao ny manakana ny famantarana rehetra amin'ny dingana, anisan'izany ny famantarana SIGKILL, amin'ny fampiasana ny API voalaza etsy ambony.

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

Toy izany koa, ny politikan'ny MAC dia azo soratana ao amin'ny kernel, izay manome fomba fiarovana famantarana (politika proc_check_signal), saingy tsy tohanana amin'ny fomba ofisialy ny API.

Fiarovana ny fanitarana kernel

Ankoatra ny fiarovana ny fizotran'ny rafitra dia ilaina ihany koa ny fiarovana ny fanitarana kernel (kext). macOS dia manome rafitra ho an'ny mpamorona hamolavola mora foana ny mpamily fitaovana IOKit. Ho fanampin'ny fanomezana fitaovana amin'ny fiasana amin'ny fitaovana, ny IOKit dia manome fomba fametahana mpamily amin'ny alΓ lan'ny kilasy C++. Ny fampiharana ao amin'ny userspace dia afaka "hitady" ohatra iray voasoratra anarana ao amin'ny kilasy mba hananganana fifandraisana kernel-userspace.

Mba hamantarana ny isan'ny kilasy ao amin'ny rafitra dia misy ny ioclasscount utility.

my_kext_ioservice = 1
my_kext_iouserclient = 1

Ny fanitarana kernel rehetra izay te hisoratra anarana amin'ny stack mpamily dia tsy maintsy manambara kilasy iray mandova avy amin'ny IOService, ohatra my_kext_ioservice amin'ity tranga ity.

Rehefa manandrana manala mpamily avy amin'ny rafitra (baiko kextunload), dia antsoina ny fiasa virtoaly "bool terminate (IOOptionBits options)". Ampy ny mamerina diso amin'ny fiantsoana hampitsahatra rehefa manandrana manaisotra ny kextunload.

bool Kext::terminate(IOOptionBits options)
{

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

  return super::terminate(options);
}

Ny saina IsUnloadAllowed dia azo apetraka amin'ny IOUserClient rehefa mandefa. Rehefa misy fetran'ny fampidinana dia hamerina ity vokatra manaraka ity ny baiko 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.

Ny fiarovana toy izany dia tsy maintsy atao ho an'ny IOUserClient. Ny ohatra amin'ny kilasy dia azo alaina amin'ny fampiasana ny IOKitLib userspace function "IOCatalogueTerminate(mach_port_t, uint32_t flag, io_name_t description);". Azonao atao ny mamerina diso rehefa miantso ny baiko "farafaharatsiny" mandra-pahafatin'ny rindranasa mpampiasaspace, izany hoe tsy antsoina ny fiasa "clientDied".

Fiarovana ny rakitra

Mba hiarovana ny rakitra dia ampy ny mampiasa ny Kauth API, izay ahafahanao mametra ny fidirana amin'ny rakitra. Apple dia manome fampandrenesana ho an'ny mpamorona momba ny hetsika isan-karazany amin'ny sehatra; ho anay dia zava-dehibe ny hetsika KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA ary KAUTH_VNODE_DELETE_CHILD. Ny fomba tsotra indrindra hamerana ny fidirana amin'ny rakitra dia amin'ny alΓ lan'ny lalana - mampiasa ny API "vn_getpath" izahay mba hahazoana ny lalana mankany amin'ny rakitra ary mampitaha ny tovan'ny lalana. Mariho fa mba hanamafisana ny fanovana anarana ny lalan'ny lahatahiry rakitra, ny rafitra dia tsy manome alalana ny fidirana amin'ny rakitra tsirairay, fa ny fampirimana izay novana anarana ihany. Ilaina ny mampitaha ny lalan'ny ray aman-dreny ary mametra ny KAUTH_VNODE_DELETE ho azy.

Ahoana ny fiarovana ny dingana sy ny fanitarana kernel amin'ny macOS

Ny tsy fahampian'ity fomba fiasa ity dia mety ho ambany ny fahombiazany satria mitombo ny isan'ny prefix. Mba hahazoana antoka fa tsy mitovy amin'ny O(prefix*length) ny fampitahana, izay ny prefix dia ny isan'ny prefixes, ny halavany dia ny halavan'ny tady, azonao atao ny mampiasa automaton finite deterministic (DFA) naorin'ny prefixes.

Andeha isika handinika fomba fanamboarana DFA ho an'ny andiana prefixes. Manomboka ny cursors amin'ny fiandohan'ny prefix tsirairay. Raha manondro toetra iray ihany ny cursors rehetra, dia ampitomboy tarehintsoratra iray ny cursor tsirairay ary tadidio fa ny halavan'ny andalana iray dia lehibe kokoa amin'ny iray. Raha misy cursor roa misy marika samy hafa dia zarao ho vondrona ny cursors araka ny marika tondroiny ary avereno ny algorithm ho an'ny vondrona tsirairay.

Amin'ny tranga voalohany (ny endri-tsoratra rehetra eo ambanin'ny cursors dia mitovy), dia mahazo fanjakana DFA izay tsy misy afa-tsy tetezamita iray amin'ny andalana iray ihany. Amin'ny tranga faharoa, dia mahazo latabatra fifindran'ny habe 256 (isan'ny tarehin-tsoratra sy isan'ny vondrona ambony indrindra) mankany amin'ny fanjakana manaraka azo amin'ny fiantsoana miverimberina ilay asa.

Andeha isika hijery ohatra iray. Ho an'ny andiana prefixes (β€œ/foo/bar/tmp/”, β€œ/var/db/foo/”, β€œ/foo/bar/aba/”, β€œfoo/bar/aac/”) dia azonao atao ireto manaraka ireto DFA. Asehon'io tarehimarika io ihany ny fifindrana mankany amin'ny fanjakana hafa; tsy ho farany ny tetezamita hafa.

Ahoana ny fiarovana ny dingana sy ny fanitarana kernel amin'ny macOS

Rehefa mandalo ny fanjakana DKA dia mety misy tranga 3.

  1. Tonga ny fanjakana farany - voaaro ny lalana, feranay ny asa KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA ary KAUTH_VNODE_DELETE_CHILD
  2. Tsy tonga ny fanjakana farany, fa ny lalana dia "nifarana" (tonga ny terminator null) - ray aman-dreny ny lalana, ilaina ny mametra ny KAUTH_VNODE_DELETE. Mariho fa raha laha-tahiry ny vnode dia mila manampy '/' ianao amin'ny farany, raha tsy izany dia mety hametra izany amin'ny rakitra "/foor/bar/t", izay diso.
  3. Tsy tonga ny fanjakana farany, tsy nifarana ny lalana. Tsy misy prefix mifanandrify amin'ity iray ity, tsy mampiditra fameperana izahay.

famaranana

Ny tanjon'ny vahaolana fiarovana izay novolavolaina dia ny hampitombo ny haavon'ny fiarovana ny mpampiasa sy ny angonany. Amin'ny lafiny iray, io tanjona io dia tratra amin'ny alΓ lan'ny fampivoarana ny vokatra rindrambaiko Acronis, izay manidy ireo vulnerability izay "malemy" ny rafitra fandidiana. Amin'ny lafiny iray, tsy tokony hatao tsirambina ny fanamafisana ireo lafiny fiarovana izay azo hatsaraina amin'ny lafiny OS, indrindra satria ny fanakatonana ireo vulnerable ireo dia mampitombo ny fahamarinantsika manokana ho toy ny vokatra. Notaterina tamin'ny Apple Product Security Team ny vulnerability ary raikitra tao amin'ny macOS 10.14.5 (https://support.apple.com/en-gb/HT210119).

Ahoana ny fiarovana ny dingana sy ny fanitarana kernel amin'ny macOS

Izany rehetra izany dia tsy azo atao raha toa ka napetraka amin'ny fomba ofisialy ao amin'ny kernel ny fitaovanao. Izany hoe, tsy misy lavaka toy izany ho an'ny rindrambaiko ivelany sy tsy ilaina. Na izany aza, araka ny hitanao, na dia ny fiarovana ny programa ara-dalΓ na toy ny antivirus sy ny rafitra backup dia mitaky asa. Fa ankehitriny ny vokatra Acronis vaovao ho an'ny macOS dia hanana fiarovana fanampiny amin'ny fandefasana entana amin'ny rafitra.

Source: www.habr.com

Add a comment