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
Persistence MechanismsIDS/IPS EvasionPost-ExploitationRed TeamingAdversarial Attack
GitHubcenobyte-vincit/xspawn

xspawn

launchd의 비공개 XPC 인터페이스를 통해 macOS 프로그램을 스폰하며 exec를 사용하지 않으므로, EDR이 launchd를 부모 프로세스로 기록하게 됩니다. 일회성(one-shot), KeepAlive, plist 기반 작업을 지원합니다.

저장소 보기
113일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

xspawn

xspawn은 macOS에서 launchd를 통해 프로그램을 시작하며, 그 프로그램 자체를 exec하지 않습니다. 목적은 EDR이 이 도구나 호출한 셸이 아닌 launchd를 부모 프로세스로 기록하게 하는 것입니다.

by cenobyte [email protected] 2026

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

요약

xspawn은 xpc_pipe_create_from_port(bootstrap_port)를 열고 launchctl이 사용하는 것과 동일한 비공개 XPC 파이프인 _xpc_pipe_interface_routine을 통해 프로그램 실행을 부트스트랩하며, 프로그램 자체를 exec하지 않습니다.

  • 루트는 거부됩니다. 다른 UID 모드는 없습니다.
  • 한 번의 호출로 하나의 작업(job)을 제출하거나 제거합니다.
  • 클라이언트는 /bin/launchctl을 절대 exec하지 않습니다.

요구 사항

런타임 호스트

  • 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은 sleep 없음을 의미):

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')"

launchctl submit과 동일한 수명 주기를 가진 KeepAlive 작업. CrowdStrike Falcon for macOS와 launchctl print가 여전히 프로세스를 볼 수 있도록 60초 동안 sleep합니다:

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는 작업이 부트스트랩 경로를 거치지 않았음을 의미합니다.

테스트 작업 정리:

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사용법 오류, 잘못된 레이블, 루트, 또는 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>만 가능합니다. 루트는 거부됩니다. 다른 UID 대상은 없습니다.
  • 휘발성: 재부팅 또는 로그아웃 후에도 유지되지 않습니다.
  • 비공개 XPC 로드 및 bootout 상수는 macOS 26.6.1 빌드 25G76에 고정되어 있습니다. sw_vers -buildVersion이 변경되면 다시 고정하세요(ARCHITECTURE.md 참조).
  • 임시 plist는 $TMPDIR/XXXXXX/XXXXXX.plist입니다($TMPDIR은 절대 경로여야 하며, 그렇지 않으면 /tmp). 디렉터리는 모든 종료 시 제거됩니다. 이미 사용 중인 레이블은 해당 파일을 절대 생성하지 않습니다.
  • CrowdStrike Falcon for macOS는 ProcessRollup2 이벤트의 Auto-Start Extensibility Point 필드(ASEPFilePath)에 임시 plist 경로를 기록합니다. 프로세스 부모는 여전히 launchd입니다.
  • 독립 실행형 xspawn 실행은 이 클라이언트로 표시됩니다: 셸 기록 및 이 바이너리에 대한 EDR 프로세스 이벤트. CrowdStrike Falcon for macOS는 프로그램 경로와 인자를 포함한 전체 CommandLine을 기록합니다. 해당 이미지와 argv가 특징적일 때 클라이언트를 다른 도구에 컴파일하세요. 임베딩은 ASEPFilePath나 launchd.log 부트스트랩 라인을 제거하지 않습니다(ARCHITECTURE.md, Parentage 참조).
  • Launchd는 스폰을 /private/var/log/com.apple.xpc.launchd/launchd.log에도 기록합니다(ARCHITECTURE.md, Parentage 참조).

비공개 XPC 프로토콜

launchd는 Mach 부트스트랩 서버입니다. 이 클라이언트는 공개 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 빌드 25G76에서 load는 디스크립터 800이고 bootout은 801입니다. 인터페이스 플래그는 6입니다. gui/<uid> 세션이 필요합니다: 상속된 포트는 Aqua 로그인 세션 내에서만 gui launchd 도메인이며, 이 클라이언트는 handle = uid와 함께 type 8만 보냅니다.

Load(800)는 XPC 사전입니다. 작업 정의는 메시지 본문에 없습니다.

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

launchd는 경로를 stat하고 plist를 구문 분석한 다음 xpcproxy를 posix_spawn합니다. 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 Vol. 1)은 launchctl이 비공개 XPC 파이프를 통해 launchd와 통신한다는 것을 보여주었고, type, handle, subsystem, routine, name 사전 키와 함께 xpc_pipe_create_from_port / xpc_pipe_routine을 문서화했습니다. Patrick Wardle(The Art of Mac Malware Vol. 2)은 _xpc_pipe_interface_routine을 이후의 전송 진입점으로 문서화했습니다. Csaba Fitzl과 Brandon Dalton(OBTS)은 동일한 사전 계열과 도메인 유형 코드(gui는 8)를 매핑했습니다. 공개 스니펫은 이미 xpc_pipe_create_from_port(bootstrap_port, 4)를 사용했습니다.

이러한 문서는 프로토콜의 클래스를 설명합니다. 라이브 25G76 로드 상수를 제공하지는 않습니다. Levin의 2015년 캡처는 subsystem과 routine을 사전 안에 넣고 xpc_pipe_routine을 사용했습니다. 25G76에서는 해당 키가 없습니다. launchctl bootstrap은 루틴 ID를 디스크립터 인자로 사용하여 _xpc_pipe_interface_routine을 호출합니다. launchctl의 arm64e 정적 분석은 여전히 이전 사전 경로처럼 보이며 로드 ID로 703을 제안합니다. 라이브 x86_64 lldb와 arm64 클라이언트 실행 모두 해당 키 없이 800 / 801을 사용합니다. 이 클라이언트는 두 슬라이스 모두에서 해당 단일 형태를 제공합니다.

레지스터 덤프, lldb 재고정 레시피, 부모 관계 필드 메모는 ARCHITECTURE.md에 있습니다.

참고

  • ARCHITECTURE.md: 스폰 경로, 부모 관계, 라이브 와이어, 재고정
  • AGENTS.md: 빌드 호스트 대비 타깃
도구 다운로드
하위 명령어
수명 주기
oneshot일회성 (RunAtLoad + LaunchOnlyOnce)
submitKeepAlive
load호출자가 소유한 plist를 그대로 로드
remove레이블로 언로드
  • 손으로 작성한 plist에 XPCService 키를 넣지 마세요: 그러면 xpcproxy가 포크하고 CrowdStrike Falcon for macOS는 부모를 xpcproxy로 기록합니다.