Blog: How-Tos

Socat-fu lesson

Pedro Venda 19 Sep 2014

I’m going to show you how socat is used to solve real-life penetration testing problems. Socat is a fantastically versatile Swiss army knife for connecting all sorts of sockets, be they TCP, UDP, IPv4, IPv6, SSL-wrapped, unix sockets, raw sockets, etc.

There are six examples here, varying in complexity from a simple TCP connection to a remote service, through to transparent hijacking of traffic into a socat endpoint.

Basic:
Case 1: Tunnel a connection from a local TCP port to a remote service
Case 2: Tunnel a plain text connection to an SSL endpoint (and vice-versa)

Moderate:
Case 3: Man in the middle an SSL connection that you can control
Case 4: Modify HTTP traffic in transit to disable gzip/deflage encodings

Advanced:
Case 5: Transparent hijacking of traffic into a socat endpoint

Bonus:
Case 6: Help! There is no netcat or socat!

:Start

Case 1: Tunnel a connection from a local TCP port to a remote service

Difficulty: Easy
Usefulness: Low to moderate

$ socat -v tcp4-listen:8000,reuseaddr,fork tcp4:6.6.6.6:80

Connections to localhost:8000 will end up at port 80 of remote host 6.6.6.6. Socat forks a new process for every connection so multiple connections can be made to the local port without requiring restarting socat.

GOTO :End

Case 2: Tunnel a plain text connection to an SSL endpoint (and vice-versa)

Difficulty: Easy
Usefulness: High

$ socat -v tcp4-listen:9000,reuseaddr,fork ssl:6.6.6.6:443,verify=0

Plain text connections to localhost on port 9000 will terminate at the remote host 6.6.6.6 in an SSL tunnel. This is particularly useful when certain tools do not deal with SSL correctly or when certificates get in the way. Socat can be used to establish the SSL connection while presenting a plain text endpoint, thus masking or hiding the encryption layer. This model can be extended to enable the use of a client side certificate, which further helps to mask this layer of complexity (which some tools might not support).

$ socat -v tcp4-listen:9000,reuseaddr,fork
ssl:6.6.6.6:443,verify=0,cert=./provisional_prov.pem

The opposite is also possible (with or without client certificate authentication), with the added requirement that server-side certificates must exist for socat to be able to host SSL/TLS connections.

$ socat -v openssl-listen:7000,cert=cert.pem,verify=0,reuseaddr,fork
tcp4:6.6.6.6:6000

In this case an SSL/TLS connection to localhost on port 7000 is piped to the remote host 6.6.6.6 on port 6000.

GOTO :End

Case 3: Man in the middle an SSL connection that you can control

Difficulty: Moderate Usefulness: High (specific)

All the above cases can be combined in weird and complicated ways to confuse your colleagues and clients implement useful but complex network tunnels. Socat makes it very easy to terminate an SSL/TLS connection, pipe the data in plain text into another socat instance which forwards it through an SSL/TLS tunnel to the end service. The end result is visibility and ability to modify traffic in plain text which would otherwise be going through the SSL tunnel.

Traffic flows roughly as per the diagram below:

“Application” ==SSL==> socat #1 —plain—> socat #2 ==SSL==> Remote service

The commands below should be executed in reverse order in separate shells:

#1: $ socat -v openssl-listen:6000,cert=cert.pem,verify=0,reuseaddr,fork
tcp4:localhost:6500
#2: $ socat -v tcp4-listen:6500,reuseaddr,fork ssl:6.6.6.6:443,verify=0

NOTE: socat won’t connect the second socket until a connection is received on the first, therefore the order of the above commands is normally irrelevant. But for correctness, the second command should be started in first place.

GOTO :End

Case 4: Modify HTTP traffic in transit to disable gzip/deflage encodings

Difficulty: Moderate
Usefulness: Medium (specific)

Netsed is another very useful tool to play with plaintext network traffic. This is a very simple example where the keywords ‘gzip’ and ‘deflate’ are removed from HTTP headers whilst maintaining the request length. The commands below should be executed in reverse order in separate shells:

“Application” ==SSL==> socat #1 —plain—> netsed —plain—> socat #2 ==SSL==> Remote service

#1: $ socat -v openssl-listen:6000,cert=cert.pem,verify=0,reuseaddr,fork
tcp4:localhost:6500
#2: $ netsed tcp 6500 127.0.0.1 6750 ‘s/gzip/ ‘ ‘s/deflate/ ‘
#3: $ socat -v tcp4-listen:6750,reuseaddr,fork ssl:6.6.6.6:443,verify=0

The application connects to socat #1 on port 6000 over SSL. socat #1 pipes the traffic in plain text through netsed (#2) which modifies content on the fly and forwards it to a second instance of socat #2. Socat #2 in turn forwards the modified traffic via an SSL connection to the remote server.

Modifying the workstation’s ‘hosts’ file is a helpful tweak to help forward traffic from applications that use hard coded host names rather than IP addresses.

GOTO :End

Case 5: Transparent hijacking of traffic into a socat endpoint

Difficulty: Advanced
Usefulness: Medium (specific)

This is where it all comes together. Supposed you want to modify traffic coming from an application running on a Windows workstation that connects to a hard coded IP address over SSL on a hard coded port. [This precludes dealing with certificate validity which can in most cases be taken care of (technically all cases, but some are harder than others)].

A possible solution is to add a static route into a Linux virtual machine connected to the Internet via a bridged interface, setup iptables to hijack the traffic and forward to a local port (remember
that traffic is aimed at a remote address, so Linux would attempt to route it) and get the socat array of processes piping the traffic through the assembly line of hacking through towards the remote host as intended by the application.

A couple of definitions:
Remote service is 6.6.6.6 on port 9999;
VM address: 192.168.81.131 (eth0)
VM briged gateway address: 192.168.1.254 (eth1)

Step 1: redirect traffic to VM (Windows workstation)

$ route -p add 6.6.6.6 mask 255.255.255.255 192.168.81.131 metric 1 if 20

Traffic going to IP 6.6.6.6/32 should be delivered to host 192.168.81.131 (the Linux VM) on interface 0x20 (check with route print which interface index connects where) with full priority (metric 1).

At this point, the Linux VM will receive traffic coming from the test app towards the remote server on port 9999, but the traffic is either routed/forwarded or dropped.

Step 2: hijack application traffic (Linux VM)

# echo -n 1 > /proc/sys/net/ipv4/ip_forward # enable forwarding
# ip route del default … # normally the NATted interface
# ip route add default via 192.168.1.254 # route traffic out via a bridged interface rather than returning on a NATted interface

NOTE: The Linux VM should connect to the Internet via a bridged interface as opposed to connecting back via a NATted connection to the host Workstation. This is because once the traffic returns to the workstation, its target returns to the VM because of the static route created to forward the traffic, as originally intended.

# iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE # NAT/masquerade
outbound traffic
# iptables -t nat -A PREROUTING -p tcp –dport 9999 -d 6.6.6.6 -j
REDIRECT –to 6000 # redirect traffic aimed at 6.6.6.6 port 9999 to local port 6000

:Step3
GOTO :Case4;

Case 6: Help! There is no socat or netcat!

If you happen to be using a system with a bash shell available, then TCP connections can be done by interacting with a pseudo-filesystem provided by the shell itself in the shape of /dev/tcp// which uses the connect() system call. This facility makes it very easy to connect to a remote host and transfer data onto it. Listening on a port, however, is not something that bash facilitates (because there is no provided equivalent pseudo-filesystem that uses the bind() system call).

$ exec 8<>/dev/tcp/6.6.6.6/80

Open a file descriptor and point it at the virtual file
/dev/tcp/6.6.6.6/80, which is the same as connecting the file descriptor to the remote tcp port of host 6.6.6.6 (host names can be used too) on port 80. This is a read-write file descriptor as shown below.

$ echo -e “GET / HTTP/1.0\r\n\r\n” >&8

Write a basic HTTP request onto the file descriptor.

$ cat <&8

Finally read the response into standard output. Job done!

GOTO :End;

:End