ESNetworkMonitor
A native macOS network monitoring tool that captures socket bind and network connection events using Apple's Endpoint Security framework. This tool leverages **undocumented ES events** (RESERVED_3 through RESERVED_6) introduced in macOS 26.4 to provide real-time visibility into network activity.
Install / Use
/learn @nietzschelab/ESNetworkMonitorREADME
ESNetworkMonitor
A native macOS network monitoring tool that captures socket bind and network connection events using Apple's Endpoint Security framework. This tool leverages undocumented ES events (RESERVED_3 through RESERVED_6) introduced in macOS 26.4 to provide real-time visibility into network activity.
Features
- Socket Bind Monitoring: Capture when processes bind to network addresses/ports
- Network Connection Monitoring: Track outbound connections with full endpoint details
- Rich Process Metadata: Full process information including code signing, audit tokens, executable paths
- JSON Output: eslogger-compatible JSON format for easy integration with SIEM/logging tools
- Human-Readable Output: Colored terminal output for interactive monitoring
- Reverse Engineering Mode: Raw hex dumps for further protocol analysis
Requirements
- macOS 26.4+ (events don't exist in earlier versions)
- System Integrity Protection (SIP) Disabled - Required because:
- The ES client entitlement (
com.apple.developer.endpoint-security.client) normally requires Apple's approval - With SIP disabled, ad-hoc signed binaries with this entitlement can run
- The ES client entitlement (
- Root privileges (
sudo)
Why SIP Must Be Disabled
Apple restricts the Endpoint Security entitlement to approved developers. On a SIP-enabled system, only Apple-notarized apps with the proper provisioning profile can use ES. Disabling SIP allows:
- Ad-hoc code signing (
codesign -s "-") - Self-assigned ES entitlements
- Running unsigned/locally-signed ES clients
Building
make release
This will:
- Compile with optimizations (
-O2) - Sign with ad-hoc signature and ES entitlements
- Output binary to
build/ESNetworkMonitor
Build Requirements
- Xcode Command Line Tools
- macOS 14.0+ SDK (for ES framework headers)
Usage
Basic Monitoring (Human-Readable)
sudo ./build/ESNetworkMonitor
Output:
[SOCKET BIND AUTH]
Process: /usr/sbin/mDNSResponder (PID: 197)
UID: 65 | Platform Binary: Yes
Socket Type: DGRAM (UDP)
Protocol: UDP (17)
Address Family: IPv6 (30)
Bind Address: ::
Bind Port: 5353 (0x14e9)
[NETWORK CONNECTION NOTIFY]
Process: /usr/bin/curl (PID: 1234)
UID: 501 | Platform Binary: Yes
Local: port 0 (IPv4)
Remote: 93.184.216.34:80 (IPv4)
Direction: Outbound
Protocol: TCP (6)
Hostname: example.com
JSON Output (eslogger-compatible)
sudo ./build/ESNetworkMonitor -j
Output (one JSON object per line):
{"schema_version":1,"version":10,"event_type":"network_connect","mach_time":147441608386,"seq_num":4,"global_seq_num":12,"time":"2026-04-01T14:18:45.636230Z","action_type":"auth","thread":{"thread_id":44438},"process":{"audit_token":[501,501,20,501,20,2273,100106,6120],"ppid":1795,"signing_id":"com.apple.curl","executable":{"path":"/usr/bin/curl","stat":{...}},...},"event":{"network_connect":{"remote":{"address":"104.18.27.120","port":80},"hostname":"example.com",...}}}
Pipe to jq for pretty printing:
sudo ./build/ESNetworkMonitor -j | jq .
Verbose/Debug Modes
# Field analysis (shows struct offsets and values)
sudo ./build/ESNetworkMonitor -v
# Hex dumps of event structures
sudo ./build/ESNetworkMonitor -d
# Raw dump mode for reverse engineering (no parsing)
sudo ./build/ESNetworkMonitor -r -s 2048
All Options
Usage: sudo ./ESNetworkMonitor [options]
Options:
-j, --json Output eslogger-compatible JSON (one event per line)
-d, --dump Enable hex dump of raw event data
-v, --verbose Enable verbose field analysis
-r, --raw Raw dump only (no parsing, for RE)
-s, --size <bytes> Set dump size in bytes (default: 512, max: 4096)
-h, --help Show this help
Event Types
This tool subscribes to four undocumented Endpoint Security events:
| Event ID | Name | Type | Description | |----------|------|------|-------------| | 151 | ES_EVENT_TYPE_RESERVED_3 | AUTH | Socket bind authorization request | | 152 | ES_EVENT_TYPE_RESERVED_4 | NOTIFY | Socket bind notification | | 153 | ES_EVENT_TYPE_RESERVED_5 | AUTH | Network connection authorization request | | 154 | ES_EVENT_TYPE_RESERVED_6 | NOTIFY | Network connection notification |
AUTH events require a response (allow/deny) - this tool always allows. NOTIFY events are informational only.
JSON Schema
The JSON output follows eslogger conventions:
{
"schema_version": 1,
"version": 10,
"event_type": "network_connect",
"mach_time": 147441608386,
"seq_num": 4,
"global_seq_num": 12,
"time": "2026-04-01T14:18:45.636230Z",
"action_type": "auth",
"thread": {
"thread_id": 44438
},
"process": {
"audit_token": [501, 501, 20, 501, 20, 2273, 100106, 6120],
"ppid": 1795,
"original_ppid": 1795,
"group_id": 2273,
"session_id": 1795,
"codesigning_flags": "0x26010801",
"is_platform_binary": true,
"is_es_client": false,
"cdhash": "ad8b678c04af7b1407ff718998e403342e75c1d4",
"signing_id": "com.apple.curl",
"team_id": "",
"executable": {
"path": "/usr/bin/curl",
"path_truncated": false,
"stat": { "st_dev": 16777231, "st_ino": 1152921500312571174, "..." }
},
"tty": { "path": "/dev/ttys003", "..." },
"start_time": "2026-04-01T14:18:45.587689Z",
"responsible_audit_token": [501, 0, 0, 0, 0, 1790, 100106, 4798],
"parent_audit_token": [501, 501, 20, 501, 20, 1795, 100106, 4804]
},
"action": {
"result": {
"result_type": "auth",
"result": { "auth": "pending" }
}
},
"event": {
"network_connect": {
"local": { "address_family": 2, "port": 0 },
"remote": { "address_family": 2, "address": "104.18.27.120", "port": 80 },
"direction": 0,
"protocol": 6,
"socket_type": 1,
"hostname": "example.com"
}
}
}
Reverse Engineering Notes
The event structures were reverse-engineered from:
- Phorion Blog: Reverse Engineering macOS 26.4's Undocumented Socket Bind Events
- Objective-See Blog: Network Connection Events
Socket Bind Event Structure (RESERVED_3/4)
es_event_socket_bind_t (+0x00)
└── inner_ptr (+0x00) -> es_socket_bind_inner_t
├── addr_ptr (+0x00) -> es_socket_bind_addr_t
│ ├── family (+0x00): uint32 (AF_INET=2, AF_INET6=30)
│ ├── addr (+0x04): 16 bytes (IPv4 or IPv6)
│ └── port (+0x14): uint32
├── socket_type (+0x0c): uint32 (0=STREAM, 1=DGRAM)
└── protocol (+0x10): uint32 (6=TCP, 17=UDP)
Network Connection Event Structure (RESERVED_5/6)
es_event_network_connect_t (+0x00)
├── local_family (+0x00): uint32
├── local_port (+0x14): uint32
├── remote_family (+0x18): uint32
├── remote_addr (+0x1c): 16 bytes
├── remote_port (+0x2c): uint32
├── direction (+0x30): uint32 (0=outbound)
├── protocol (+0x34): uint32
├── socket_type (+0x38): uint32
├── hostname_len (+0x40): uint32
├── hostname_ptr (+0x48): pointer
└── hostname (+0x50): char[256]
Security Considerations
- This tool requires SIP disabled and root access - use only on test/research systems
- AUTH events are auto-allowed - this tool is for monitoring, not firewalling
- JSON output may contain sensitive information (hostnames, IPs, process paths)
License
MIT License - See LICENSE file.
Acknowledgments
- Patrick Wardle / Objective-See for ES research
- Phorion Security for socket bind event documentation
Related Skills
tmux
345.9kRemote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.
diffs
345.9kUse the diffs tool to produce real, shareable diffs (viewer URL, file artifact, or both) instead of manual edit summaries.
terraform-provider-genesyscloud
Terraform Provider Genesyscloud
blogwatcher
345.9kMonitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.
