Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
xspawn — 通过 launchd 的私有 XPC 接口生成 macOS 程序,而无需 exec 它们,使 EDR 将 launchd 记录为父进程。支持一次性 (one-shot)、KeepAlive 和基于 plist 的任务。 | Kitploit
工具/GitHubGitHub/cenobyte-vincit/xspawn
持久化机制IDS/IPS规避后渗透利用红队对抗性攻击
GitHubcenobyte-vincit/xspawn

xspawn

通过 launchd 的私有 XPC 接口生成 macOS 程序,而无需 exec 它们,使 EDR 将 launchd 记录为父进程。支持一次性 (one-shot)、KeepAlive 和基于 plist 的任务。

查看仓库
2122天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

xspawn

xspawn 通过 launchd 在 macOS 上启动程序,并且自身从不 exec 该程序。目标是让 EDR 将 launchd 记录为父进程,而不是此工具或调用它的 shell。

作者:cenobyte [email protected] 2026

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

摘要

xspawn 打开 xpc_pipe_create_from_port(bootstrap_port),并通过 _xpc_pipe_interface_routine 引导程序的执行——这与 launchctl 使用的私有 XPC 管道相同——并且自身从不 exec 该程序。

  • 拒绝以 root 运行。不存在其他 UID 模式。
  • 一次调用提交或移除一个作业。
  • 客户端从不 exec /bin/launchctl。

要求

运行时主机

  • 具有 gui/<uid> 会话的 macOS (Darwin)

构建主机

  • 装有 Xcode Command Line Tools 或 Xcode 的 macOS (Darwin)
  • C17 编译器(cc)
  • make
  • cppcheck 用于开发(brew install cppcheck)

构建

root@kitploit:~
make

用法

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 for 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 for 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 在写入临时 plist 之前,会先在 gui 和 user(描述符 708)中探测该标签。标签已被占用时,程序退出并提示 label already loaded,且不输出到 stdout。这一检查的存在是为了让注定失败的 800 不会写入 $TMPDIR/XXXXXX/XXXXXX.plist(一种 DFIR 痕迹;CrowdStrike Falcon 会将该路径保存在 ASEPFilePath 中),也不会打印作业字典的 XML 副本。标签可用时会先打印临时路径,然后打印该 XML,最后发送 800。load -p 对文件的 Label 执行相同的占用检查,然后打印调用方路径和 XML。临时目录在每次退出时都会被删除。remove 按标签操作。

退出码

代码含义
0Bootstrap 或 bootout XPC 成功
1用法错误、标签无效、以 root 运行,或 launchd/XPC 拒绝

验证

构建主机(make 和测试树;通常与 gui 会话位于同一处)。这些检查并非干净运行时的证明:

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

限制

  • 仅限同用户 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 for macOS 会在 ProcessRollup2 事件的自启动扩展点字段(ASEPFilePath)中记录临时 plist 路径。进程父进程仍为 launchd。
  • 独立运行 xspawn 会以本客户端的形式可见:shell 历史记录,以及针对该二进制的 EDR 进程事件。CrowdStrike Falcon for macOS 会记录完整的 CommandLine,其中包含程序路径及其参数。当该镜像和 argv 具有辨识度时,请将客户端编译进其他工具中。嵌入并不会消除 ASEPFilePath 或 launchd.log 中的 bootstrap 行(参见 ARCHITECTURE.md 的 Parentage 部分)。
  • launchd 还会在 /private/var/log/com.apple.xpc.launchd/launchd.log 中记录该 spawn(参见 ARCHITECTURE.md 的 Parentage 部分)。

私有 XPC 协议

launchd 是一个 Mach bootstrap 服务器。本客户端不使用公开的 XPC(xpc_connection_create)。它通过 xpc_pipe_create_from_port(bootstrap_port, 4) 在继承的 bootstrap_port 上打开一条私有 libxpc 管道,然后发送 _xpc_pipe_interface_routine。这些符号位于 libxpc 中,不在 SDK 头文件里。

例程 ID 是描述符参数,而不是请求字典中的键。在 macOS 26.6.1 build 25G76 上,load 是描述符 800,bootout 是 801。接口标志为 6。需要 gui/<uid> 会话:只有在 Aqua 登录会话内,继承的端口才是 gui launchd 域,且本客户端仅发送 type 8 且 handle = uid。

Load(800)是一个 XPC 字典。作业定义不在消息主体中。

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

launchd 对路径执行 stat,解析 plist,然后通过 posix_spawn 启动 xpcproxy。xpcproxy 在同一 PID 内 exec 该程序。成功的标志是管道返回 0、无 xpc-fault、error 为 0、bootstrap-error 为 0。

Bootout(801)按标签进行:handle、type 8、name、no-einprogress、wait。不涉及 plist。

该通道已有先例。Jonathan Levin(launjctl,2015 年;Mac OS X and iOS Internals 第 1 卷)表明 launchctl 通过私有 XPC 管道与 launchd 通信,并记录了带有字典键 type、handle、subsystem、routine 和 name 的 xpc_pipe_create_from_port / xpc_pipe_routine。Patrick Wardle(The Art of Mac Malware 第 2 卷)记录了作为后来发送入口的 _xpc_pipe_interface_routine。Csaba Fitzl 和 Brandon Dalton(OBTS)梳理了相同的字典族和域类型码(gui 为 8)。公开代码片段已经使用了 xpc_pipe_create_from_port(bootstrap_port, 4)。

这些文章描述的是该协议的大类,并未提供 25G76 上实时的 load 常量。Levin 2015 年的抓包将 subsystem 和 routine 放在字典内并使用 xpc_pipe_routine。在 25G76 上这些键不存在。launchctl bootstrap 调用 _xpc_pipe_interface_routine,例程 ID 作为描述符参数。对 launchctl 的 arm64e 静态分析看起来仍是旧的字典路径,并暗示 703 为 load ID。实时的 x86_64 lldb 和一次 arm64 客户端运行均使用 800 / 801,且这些键不存在。本客户端在两个架构切片上都使用这一种形态。

寄存器转储、lldb 重新固定步骤以及父进程字段说明均位于 ARCHITECTURE.md。

另见

  • ARCHITECTURE.md:spawn 路径、父进程关系、实时线路、重新固定
  • AGENTS.md:构建主机与目标
下载工具
子命令
生命周期
oneshot一次性(RunAtLoad + LaunchOnlyOnce)
submitKeepAlive
load按原样使用调用方拥有的 plist
remove按标签卸载
  • 请勿在手工编写的 plist 中放置 XPCService 键:否则 xpcproxy 会 fork,且 CrowdStrike Falcon for macOS 会将父进程记录为 xpcproxy。