Блог о безопасности FireAI

Автор FireAI Security & Research Team · Опубликовано

Охота за механизмами закрепления в macOS: LaunchAgents, элементы входа и фоновые задачи

Охота за механизмами закрепления в macOS: LaunchAgents, элементы входа и фоновые задачи

«Персистентность» (persistence) — термин из области безопасности для одной конкретной проблемы: как код, который уже запускался однажды, устраивает так, чтобы запускаться снова автоматически после перезагрузки или входа в систему, без того, чтобы кто-то запускал его вручную заново. macOS даёт легитимному программному обеспечению множество санкционированных способов делать именно это — программа проверки обновлений, клиент синхронизации в строке меню, вспомогательный процесс драйвера принтера — и каждый из этих механизмов в равной степени доступен и тому, что вы предпочли бы вообще не видеть запущенным. Это обзор реальных мест, куда стоит заглянуть, с настоящими командами, и честный рассказ о том, где сетевой инструмент вроде FireAI вписывается в эту картину, а где — намеренно нет.

LaunchAgents и LaunchDaemons: два главных механизма

macOS запускает почти всё через launchd, управляемый файлами property list (.plist) в небольшом числе известных каталогов. Техника T1543.001 из MITRE ATT&CK описывает механизм прямо: при входе в систему процесс launchd, запущенный от имени пользователя, загружает plist-файлы из пользовательских и системных каталогов LaunchAgents, а plist с RunAtLoad, установленным в true, запускается автоматически в момент загрузки — без каких-либо дальнейших действий со стороны того, кто его туда поместил. Техника перечисляет три важных расположения: /System/Library/LaunchAgents, /Library/LaunchAgents и ~/Library/LaunchAgents. MITRE отмечает то, что стоит помнить, пролистывая список таких файлов: агенты, установленные для персистентности, часто «маскируются… под имена, напоминающие легитимные компоненты ОС или ПО» — файл, похожий на com.apple.something.plist, стоит проверить лишний раз именно потому, что он старается этой проверки избежать.

LaunchDaemons, описанные в родственной технике T1543.004, — это системная версия без необходимости входа в систему: они работают от имени root, запускаются при загрузке из /System/Library/LaunchDaemons/ или /Library/LaunchDaemons/. Поскольку установка демона туда изначально требует прав администратора, MITRE описывает этот путь как способ превратить первоначальный привилегированный доступ в нечто, что переживает перезагрузку, работая после этого с правами root — и именно поэтому новый, незнакомый файл в /Library/LaunchDaemons — более серьёзный сигнал, чем такой же файл в собственной папке LaunchAgents пользователя.

Терминал — что реально зарегистрировано
ls -la ~/Library/LaunchAgents /Library/LaunchAgents /Library/LaunchDaemons
# compare this list against what you remember installing; anything you don't
# recognise is worth reading with: plutil -p /path/to/the.plist

Наличие файла на диске и то, что задача реально загружена, — два разных вопроса, и на второй отвечает launchctl print. Согласно man-странице, эта команда выводит «информацию об указанном сервисе или домене» — указав домен вроде system/ или gui/501/ (где 501 — UID пользователя), вы получаете список всех сервисов и точек, загруженных в данном контексте, вместе с состоянием каждой из них:

Терминал — что реально загружено прямо сейчас
launchctl print gui/$(id -u)
# example output, trimmed — a real run lists every loaded agent for your session
	"com.apple.someAgent" => {
		active count = 1
		path = /Library/LaunchAgents/com.apple.someAgent.plist
		state = running
	}

Элементы входа и Background Task Management

Пользовательский интерфейс для многого из этого — раздел «Элементы входа» в Системных настройках, и его стоит проверить своими глазами, а не только из командной строки. Руководство поддержки Apple описывает это прямо: вы можете «выбрать элементы входа, которые открываются автоматически при входе в систему», добавлять и удалять их там, а также отдельно разрешать или запрещать приложениям «выполнять задачи, когда приложение не открыто, например проверять обновления ПО или синхронизировать данные» — эта вторая категория охватывает фоновые помощники, которые не являются полноценными элементами входа, но всё равно работают без присмотра.

Начиная с macOS Ventura, систему под этой панелью настроек в сообществе безопасности принято называть Background Task Management (BTM) — сервис, который отслеживает каждый launch agent, launch daemon и элемент входа по мере их регистрации, что и позволяет Системным настройкам показывать вам живой централизованный список вместо того, чтобы вручную обшаривать три папки с plist-файлами. Есть недокументированный инструмент командной строки, sfltool, который некоторые исследователи используют для более прямого запроса к этой базе данных через sfltool dumpbtm — Apple не поставляет для него man-страницу, а формат вывода не гарантированно останется стабильным, так что относитесь к нему как к исследовательскому любопытству, которое можно попробовать на своей машине, а не как к инструменту для рабочего процесса. Поддерживаемый, стабильный способ увидеть ту же информацию — по-прежнему панель «Элементы входа» в Системных настройках либо launchctl print для актуального состояния конкретной задачи.

cron: старше, тише, но всё ещё здесь

launchd долгое время остаётся предпочитаемым планировщиком Apple, но более старый Unix-демон cron по-прежнему поставляется и по-прежнему выполняет всё, что в нём запланировано. Man-страница crontab прямо описывает формат файла: каждая строка содержит пять полей времени и даты — минута, час, день месяца, месяц, день недели — за которыми следует команда для выполнения, а на некоторых системах вместо пяти полей доступны сокращения вроде @reboot. crontab -l, согласно man-странице crontab(1), «выводит текущий crontab на стандартный вывод» для текущего пользователя:

Терминал — проверяем свой crontab и crontab root
crontab -l
sudo crontab -l -u root

Пустой результат для обоих сегодня нормален для большинства Mac — именно поэтому что бы то ни было там заслуживает внимания. cron незаметен и его редко проверяют, что как раз и объясняет, почему он всё ещё фигурирует как запасное место закрепления в отчётах о расследовании инцидентов.

Конфигурационные профили: персистентность с бумажным следом

Конфигурационный профиль может установить LaunchDaemon, выдать разрешения приватности или применить настройки на целом парке Mac — легитимно именно так работает MDM (управление мобильными устройствами). Нелегитимно профиль — это документированный способ закрепить изменения, не трогая plist-файл напрямую. Инструмент командной строки profiles показывает, что установлено: profiles list выводит установленные профили, и, как отмечает man-страница, запуск от root с -all «выведет список всех конфигурационных профилей в системе», а не только текущего пользователя.

Терминал — все конфигурационные профили на Mac
sudo profiles list -all
sudo profiles show -all

На личном Mac, не зарегистрированном в MDM, обычно вообще не должно быть профилей — либо только те, что вы установили сознательно (конфигурация VPN, рабочий профиль). Профиль, установку которого вы не помните, стоит расследовать, прежде чем удалять, поскольку profiles поддерживает удаление с защитой паролем именно для этого шага.

Плагины авторизации и длинный хвост

Помимо четырёх основных механизмов выше, есть длинный хвост менее заметных, более старых механизмов: плагины Directory Service и авторизации, импортёры Spotlight, генераторы QuickLook, плагины Dock-иконок и файлы запуска shell, которые выполняются при каждом открытии новой сессии терминала. Именно здесь специализированный инструмент оправдывает себя по сравнению с ручной проверкой. Например, KnockKnock от Objective-See за один проход перечисляет более двадцати категорий мест закрепления — включая launch agents и daemons, элементы входа, расширения браузера, задачи cron, расширения ядра и системы, плагины авторизации и Directory Service — и показывает статус подписи кода для найденного в каждой из них. Его напарник, BlockBlock, берёт тот же список расположений и следит за ним непрерывно, оповещая в момент, когда что-то новое регистрируется; согласно собственному описанию, он «отслеживает распространённые места закрепления и оповещает при добавлении нового постоянного компонента», показывая ответственный процесс, статус его подписи и позволяя разрешить или заблокировать его на месте.

Файлы запуска shell: тихий универсальный вариант

Ещё одно место, на которое стоит взглянуть напрямую, поскольку оно не требует ни plist, ни привилегированной установки: файлы конфигурации shell. ~/.zshrc, ~/.zprofile и ~/.bash_profile выполняются при каждом открытии соответствующей новой сессии терминала, и одной добавленной строки — перенаправление в скрипт, экспорт подменённого PATH, запуск фонового процесса — достаточно, чтобы заново закрепляться каждый раз при открытии терминала, при этом ничего не загружается в launchd и launchctl print ничего не покажет. KnockKnock от Objective-See включает именно эту категорию в свой сканер, перечисляя файлы конфигурации shell рядом с launch agents и элементами входа, по той же причине, по которой она заслуживает места в этой статье: это достаточно распространённое и достаточно непримечательное место, чтобы его стоило проверять, а не предполагать, что там ничего нет.

Терминал — быстрый просмотр, не заменяющий вдумчивое чтение
cat -A ~/.zshrc ~/.zprofile ~/.bash_profile 2>/dev/null | less
# -A shows non-printing characters, which surfaces anything hidden with
# trailing whitespace or a carriage return trying to push it off-screen

Где FireAI вписывается, а где — намеренно нет

Скажем прямо: FireAI не сканирует /Library/LaunchDaemons, не читает plist-файлы и не пытается обнаружить новый элемент входа или конфигурационный профиль. Это отдельная дисциплина, отличная от того, чем занимается FireAI, и инструменты, созданные специально для неё — в том числе KnockKnock и BlockBlock, — уже хорошо справляются с этой задачей. FireAI наблюдает за шагом, который наступает после закрепления и который в конечном итоге нужен каждому из этих механизмов, чтобы принести пользу тому, кто его установил: сетевое соединение. LaunchAgent, который тихо запускается при каждом входе, но никогда не обращается к сети, с точки зрения сетевого файрвола невидим — а на практике ещё и гораздо менее полезен атакующему. В момент, когда он всё же открывает сокет, к нему применяются пооконные правила FireAI, как и к любому другому процессу: незнакомый подписанный или неподписанный бинарник, устанавливающий первое соединение, вызывает запрос с объяснением рассуждений локальной модели на понятном языке, и каждое решение видимо, отменяемо и экспортируемо как текстовое правило впоследствии.

Честный способ соединить обе дисциплины: проверяйте расположения из этой статьи по расписанию, соответствующему вашей терпимости к риску — раз в месяц разумно для большинства людей, раз в неделю, если вы часто устанавливаете стороннее ПО, — и позвольте сетевому инструменту нести нагрузку в промежутках, исходя из того, что всё, что закрепилось незаметно, рано или поздно будет вынуждено заговорить, чтобы связаться с тем, кому оно отвечает.

Какое место здесь занимают FireAI и HisnLabs

FireAI does not scan for persistence — that is a different job, and tools like KnockKnock and BlockBlock already do it well; what FireAI watches is what that persisted code does the moment it opens a socket, which is the step every one of these mechanisms eventually has to take to be useful to whoever installed it.

FireAI — это собственный продукт HisnLabs: ИИ-файрвол, работающий прямо на вашем Mac. Он показывает простым языком каждое соединение, которое устанавливают ваши приложения, и позволяет вам решать, что покидает ваш Mac, — его ИИ работает локально, поэтому ваш трафик никогда не отправляется ни нам, ни кому-либо ещё. Команда исследователей безопасности HisnLabs — это те, кто поддерживает точность этих решений: они каталогизируют, какие домены являются обычной телеметрией, а какие — реальным сервисом, отслеживают страну и сеть, стоящие за соединением, и обучают локальную модель (функцию Autopilot) на реальных шаблонах трафика — и ничего из этого не покидает ваш Mac.

Вы можете прочитать о технических решениях, лежащих в его основе, или попробовать FireAI в течение 17 дней на странице FireAI от HisnLabs.

Источники