socat
is one of my favorite Linux commands. It's basically a super-powered netcat
, giving you options way beyond TCP and stdin/out. If you need to pipe one thing into another thing, socat
can do it. It'll speak TCP, UDP, UNIX domain sockets, open/exec, and even raw IP/ethernet packets. While it has a lot of features, the interface is relatively simple - you supply two addresses, and socat
will create a bidirectional (or unidirectional if you choose) link between them. For example, here's a simple TCP echo server:
socat tcp-listen:5555,fork,reuseaddr pipe
tcp-listen
is the "address type keyword", which decides what type of connection we want to work with. In this case, we're listening for a TCP connection. :5555
is an "address parameter" - what exactly these mean is specific to the address type, but in this case it's the port we're listening on. fork
and reuseaddr
are "options" - sometimes these are specific to a particular address type, but usually they come from larger "option groups" that are shared between similar address types. fork
, from the CHILD
option group, causes socat
to fork when a connection is established and then continue to wait for connections. Without this option, socat
will only accept a single connection and terminate when it closes. reuseaddr
is part of the SOCKET
option group, and allows multiple processes to listen on the same address. It isn't strictly necessary, but I've found that sometimes it can take a second for the port to become available again after socat
closes, and this reduces your turnaround time.
pipe
creates an unnamed pipe, where the read and write halves are connected together. In total, this will accept a TCP connection, fork, create a pipe, then attach the read and write ends of the two streams, creating our echo server. You can verify this behavior with netcat
, but socat
can do the same thing:
socat tcp-connect:localhost:5555 stdio
We'll come back to socat
in a bit.
TLS Interception
TLS is ubiquitous these days, and it makes it a bit annoying to inspect the traffic going in and out of your computer. There are commercial tools that can help you strip it, but socat
can do it for us provided we can get in the middle of the communication. You'll also need to disable certificate validation in whatever TLS client you're using.
We'll need to trick the client into connecting to us instead of whatever server it wants to talk to. The simplest way to do this is with the /etc/hosts
file, which allows us to force most DNS resolvers to use a particular IP address for a domain. If your client isn't fooled by this, you can use iptables
rules to redirect the traffic itself instead of the domain. Set up a rule in your hosts file:
# Redirect example.com to localhost
127.0.0.1 example.com
Now anybody wanting to talk to example.com will talk to you instead:
$ ping example.com
PING example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.207 ms
As-is, whatever client you're trying to intercept won't work correctly - there's nothing listening for connections on the port it expects. We'll use socat
to handle that.
Set up socat
There's two address types that will be useful here: openssl-listen
and openssl
. These listen for or establish a TLS-encrypted TCP connection, respectively. We'll have socat
listen for a connection, then redirect traffic back to the actual origin:
socat openssl-listen:443,fork,reuseaddr openssl:93.184.216.34:443
Note the use of example.com
's IP address instead of its domain - if we'd used the domain, it would've just pointed back to 127.0.0.1
again.
That's the principle, but it won't work as is. We need to give the listener half a certificate to use, and the client half will fail to connect because the certificate covers the domain, not the IP. We can use openssl
to generate a certificate and a private key. If you don't care about the specifics, here's a working command:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=localhost'
Once we have these, we'll provide them to socat
and turn off certificate verification:
socat -v openssl-listen:443,fork,reuseaddr,cert=cert.pem,key=key.pem,verify=0 openssl:93.184.216.34:443,verify=0
Now we can make our request (note the -k
option, which disables certificate validation):
curl -k https://example.com
Because we used the -v
switch on socat
, it'll copy the data that passes through the sockets to stderr, allowing us to see the request. You can pipe this to a file and use a hex editor to inspect the data more thoroughly. There's a way to take this to the next level though - split the pipe in half and take a trip through the loopback interface:
socat openssl-listen:443,fork,reuseaddr,cert=cert.pem,key=key.pem,verify=0 tcp-connect:localhost:8888
socat tcp-listen:8888,fork,reuseaddr openssl:93.184.216.34:443,verify=0
Now that the request is travelling over the network in cleartext, tools like Wireshark can parse it and give you way more detail.