Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
xspawn — Запускает программы macOS через частный интерфейс XPC launchd, не выполняя их через exec, из-за чего EDR фиксирует launchd как родительский процесс. Поддерживает одноразовые задания, задания KeepAlive и задания на основе plist. | Kitploit
Инструменты/GitHubGitHub/cenobyte-vincit/xspawn
Механизмы персистентностиОбход IDS/IPSПост-эксплуатацияRed TeamingСостязательная Атака
GitHubcenobyte-vincit/xspawn

xspawn

Запускает программы macOS через частный интерфейс XPC launchd, не выполняя их через exec, из-за чего EDR фиксирует launchd как родительский процесс. Поддерживает одноразовые задания, задания KeepAlive и задания на основе plist.

Репозиторий
112 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

xspawn

xspawn запускает программу в macOS через launchd и сам никогда не выполняет exec этой программы. Цель в том, чтобы EDR фиксировал в качестве родительского процесса launchd, а не этот инструмент или вызвавшую его оболочку.

автор: cenobyte [email protected], 2026

https://github.com/cenobyte-vincit/xspawn

Summary

xspawn открывает xpc_pipe_create_from_port(bootstrap_port) и выполняет bootstrap запуска программы через _xpc_pipe_interface_routine — тот же приватный XPC-канал, который использует launchctl, — и сам никогда не выполняет exec этой программы.

  • Запуск от root отклоняется. Режима для другого UID не предусмотрено.
  • Один вызов регистрирует или удаляет одно задание.
  • Клиент никогда не выполняет exec /bin/launchctl.

Requirements

Runtime host

  • macOS (Darwin) с сессией gui/<uid>

Build host

  • macOS (Darwin) с Xcode Command Line Tools или Xcode
  • компилятор C17 (cc)
  • make
  • cppcheck для разработки (brew install cppcheck)

Build

root@kitploit:~
make

Usage

root@kitploit:~
xspawn oneshot -l <label> [-o <stdout>] [-e <stderr>] [--] <program> [args...]
xspawn submit  -l <label> [-o <stdout>] [-e <stderr>] [--] <program> [args...]
xspawn remove  -l <label>
xspawn load    -p <plist>

Одноразовый запуск (RunAtLoad + LaunchOnlyOnce; 0 означает отсутствие паузы):

root@kitploit:~
./xspawn oneshot -l com.example.once -- /tmp/helloworld 0

Аргументы после -- — это ProgramArguments. Сюда входит и инлайновый код (python3 -c, perl -e). CrowdStrike Falcon для macOS записывает полный CommandLine, поэтому используйте инлайновый код с интерпретаторами с осторожностью.

root@kitploit:~
./xspawn oneshot -l com.example.py -o /tmp/py.out -- \
	/usr/bin/python3 -c "print('hello world')"

Задание KeepAlive — с тем же жизненным циклом, что и launchctl submit. Пауза 60, чтобы CrowdStrike Falcon для macOS и launchctl print по-прежнему видели процесс:

root@kitploit:~
./xspawn submit -l com.example.svc \
	-o /tmp/out.log -e /tmp/err.log -- /tmp/helloworld 60

Проверка с помощью launchctl print (только эталон; этот клиент его не вызывает):

root@kitploit:~
launchctl print gui/$(id -u)/com.example.svc

При успехе вывод содержит type = LaunchAgent (не Submitted), program — абсолютный путь, а также state = running или кратковременно xpcproxy. Submitted означает, что задание не пошло по пути bootstrap.

Удаление тестового задания:

root@kitploit:~
./xspawn remove -l com.example.svc

Загрузка plist, принадлежащего вызывающей стороне (не удаляется после ответа):

root@kitploit:~
./xspawn load -p /tmp/job.plist

<program> должен быть абсолютным путём. launchd не ищет в $PATH.

Для load -p требуется абсолютный путь, оканчивающийся на .plist.

-o / -e могут быть относительными. Они приводятся к абсолютным относительно текущего рабочего каталога перед записью в plist. Если -o и -e опущены, используется /dev/null.

oneshot и submit проверяют метку в gui и user (дескриптор 708) перед записью временного plist. При занятой метке происходит выход с сообщением label already loaded и без вывода в stdout. Эта проверка нужна, чтобы обречённый на неудачу 800 не записывал $TMPDIR/XXXXXX/XXXXXX.plist (артефакт DFIR; CrowdStrike Falcon сохраняет путь в ASEPFilePath) и не выводил XML-копию словаря задания. При свободной метке выводятся временный путь, затем этот XML, а затем отправляется 800. load -p выполняет такую же проверку занятости для Label из файла, затем выводит путь вызывающей стороны и XML. Временный каталог удаляется при любом завершении. remove работает по метке.

Exit codes

КодЗначение
0Операция bootstrap или bootout XPC выполнена успешно
1Ошибка использования, недопустимая метка, root или отказ launchd/XPC

Verify

Хост сборки (make и тестовое дерево; часто совмещён с gui-сессией). Эти проверки не являются доказательством чистоты рантайма:

root@kitploit:~
make
make test
make test-unit
make test-functional
root@kitploit:~
./xspawn oneshot -l com.example.once -- /tmp/helloworld 0

Limitations

  • Только gui/<uid> того же пользователя. Root отклоняется. Нацеливание на другой UID не поддерживается.
  • Эфемерно: не сохраняется после перезагрузки или выхода из системы.
  • Константы приватного XPC для load и bootout привязаны к macOS 26.6.1 build 25G76. Перепривяжите их, если изменится sw_vers -buildVersion (см. ARCHITECTURE.md).
  • Временный plist — это $TMPDIR/XXXXXX/XXXXXX.plist ($TMPDIR должен быть абсолютным, иначе используется /tmp). Каталог удаляется при любом завершении. При занятой метке этот файл не создаётся.
  • CrowdStrike Falcon для macOS записывает путь к временному plist в поле Auto-Start Extensibility Point (ASEPFilePath) в событии ProcessRollup2. Родительским процессом остаётся launchd.
  • Отдельный запуск xspawn виден как этот клиент: в истории оболочки и в событии EDR для этого бинарного файла. CrowdStrike Falcon для macOS записывает полный CommandLine, который включает путь к программе и её аргументы. Встраивайте клиент в другие инструменты, если такой образ и argv могут быть заметны. Встраивание не убирает ASEPFilePath и строку bootstrap в launchd.log (см. ARCHITECTURE.md, Parentage).

Private XPC protocol

launchd — это bootstrap-сервер Mach. Этот клиент не использует публичный XPC (xpc_connection_create). Он открывает приватный канал libxpc на унаследованном bootstrap_port с помощью xpc_pipe_create_from_port(bootstrap_port, 4) и затем отправляет _xpc_pipe_interface_routine. Эти символы находятся в libxpc и отсутствуют в заголовках SDK.

Идентификатор процедуры — это аргумент-дескриптор, а не ключ в словаре запроса. В macOS 26.6.1 build 25G76 load — это дескриптор 800, а bootout — 801. Флаги интерфейса равны 6. Требуется сессия gui/<uid>: унаследованный порт является gui-доменом launchd только внутри сеанса входа Aqua, и этот клиент отправляет только type 8 с handle = uid.

Load (800) — это XPC-словарь. Определение задания находится не в теле сообщения.

root@kitploit:~
handle   uid (uint64)
type     8 (gui)
paths    [absolute .plist]
by-cli   true

launchd выполняет stat для пути, разбирает plist и затем запускает xpcproxy через posix_spawn. xpcproxy выполняет exec программы в том же PID. Успех — это возврат канала 0, отсутствие xpc-fault, error 0, bootstrap-error 0.

Bootout (801) выполняется по метке: handle, type 8, name, no-einprogress, wait. Без plist.

Канал уже был описан ранее. Джонатан Левин (launjctl, 2015; Mac OS X and iOS Internals, том 1) показал, что launchctl общается с launchd через приватный XPC-канал, и задокументировал xpc_pipe_create_from_port / xpc_pipe_routine с ключами словаря type, handle, subsystem, routine и name. Патрик Уордл (The Art of Mac Malware, том 2) описал _xpc_pipe_interface_routine как более позднюю точку отправки. Чаба Фитцл и Брэндон Далтон (OBTS) описали то же семейство словарей и коды типов доменов (gui — это 8). В публичных сниппетах уже использовался xpc_pipe_create_from_port(bootstrap_port, 4).

Эти публикации описывают класс протокола. Они не содержат актуальных констант load для 25G76. В перехвате Левина 2015 года subsystem и routine находились внутри словаря, и использовался xpc_pipe_routine. На 25G76 этих ключей нет. launchctl bootstrap вызывает _xpc_pipe_interface_routine с идентификатором процедуры в качестве аргумента-дескриптора. Статический анализ arm64e для launchctl по-прежнему похож на старый путь со словарём и предполагает 703 в качестве идентификатора load. Живой lldb на x86_64 и запуск arm64-клиента используют 800 / 801 без этих ключей. Этот клиент поставляется с этой единственной формой в обоих слайсах.

Дампы регистров, рецепт перепривязки через lldb и заметки о родительском процессе находятся в ARCHITECTURE.md.

See also

  • ARCHITECTURE.md: путь запуска, родительский процесс, живой перехват, перепривязка
  • AGENTS.md: хост сборки и целевая система
Скачать инструмент
ПодкомандаЖизненный цикл
oneshotОдноразовый (RunAtLoad + LaunchOnlyOnce)
submitKeepAlive
loadplist вызывающей стороны как есть
removeВыгрузка по метке
  • launchd также записывает запуск в /private/var/log/com.apple.xpc.launchd/launchd.log (см. ARCHITECTURE.md, Parentage).
  • Не помещайте ключ XPCService в написанный вручную plist: тогда xpcproxy создаст форк, и CrowdStrike Falcon для macOS запишет родителем xpcproxy.