Working without Netcat

Netcat is a great command line tool for working with TCP or UDP connections, in a previous post I went over a few Netcat tips and tricks. Unfortunately Netcat is often not available by default. This post is going to look at a few alternatives for situations where it's not possible to install Netcat.

Python and sockets

A number of fairly fundamental tools such as Yum are build on top of Python. As a result Python is often available on Linux based systems. Python's standard library is fairly extensive and includes a socket module. Carrying out simple tasks using the module is fairly straightforward, for example if you wanted to test a TCP connection you could use code similar to the following:

from __future__ import print_function

import socket

socket.setdefaulttimeout(10)

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    client.connect(('example.com', 22))
    client.close()
    print('Connection successful')
except socket.error as e:
    print(e)
    exit(1)

There are also several re-implementations of Netcat written in Python, for example PyCat and netcat.py. netcat.py is particularly useful because it can be used without installing any additional dependencies.

$ curl -sO https://raw.githubusercontent.com/obfusk/netcat.py/master/netcat.py
$ python netcat.py -v localhost 22
SSH-2.0-OpenSSH_6.6.1

Perl

Like Python, Perl is often installed on Linux systems and comes with a socket library (IO::Socket::INET). A very simple TCP listener script might look something like the following:

#!/usr/bin/env perl

use warnings;
use strict;

use IO::Socket::INET;

# Autoflush stdout
$| = 1;

my $listener = new IO::Socket::INET (Listen    => 1,
                                     LocalAddr => '0.0.0.0',
                                     LocalPort => '1234',
                                     Proto     => 'tcp');

my $client_socket = $listener->accept();

while (1) {
  my $data = "";
  $client_socket->recv($data, 1024);
  print "$data";
  if ($data eq "") {
    last;
  }
}
shutdown($client_socket, 1);

/dev/tcp and /dev/udp

It's actually possible to open TCP or UDP connections using bash. This is briefly mentioned in the man page for bash:

Bash handles several filenames specially when they are used in redirections, as described in the following table. If the operating system on which bash is running provides these special files, bash will use them; otherwise it will emulate them internally with the behavior described below.

/dev/tcp/host/port

If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.

/dev/udp/host/port

If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.

Using this feature is relatively straightforward. For example, if you wanted to connect to time.nist.gov using the Daytime Protocol, you could do the following:

$ cat < /dev/tcp/time.nist.gov/13

58123 18-01-05 07:55:00 00 0 0 923.6 UTC(NIST) *

It's also possible to make simple HTTP requests, for example:

$ exec 3<>/dev/tcp/example.com/80
$ echo -e 'GET / HTTP/1.1\nhost: example.com\n' >&3
$ cat <&3
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html
...

Telnet

While the telnet protocol is not strictly the same as a plain TCP connection, it's often similar enough to allow you to use the telnet client for other protocols, for example:

$ telnet time.nist.gov 13
Trying 132.163.96.1...
Connected to time.nist.gov.
Escape character is '^]'.

58123 18-01-05 08:06:58 00 0 0  49.1 UTC(NIST) *
Connection closed by foreign host.

Powershell

Modern Windows systems normally have Powershell available. This makes it easy to call the .NET TcpClient Class directly. For example if you wanted to verify you can connect to example.com:80 you could do the following:

PS > $connection = New-Object System.Net.Sockets.TcpClient('example.com', 80)
PS > $connection.Connected
True
PS > $connection.close()

Abusing curl

curl can be used to quickly verify if a port is open. Although this method is far from ideal for non-http protocols such as SSH, it's often sufficient to verify a TCP connection can be established:

$ curl --verbose --user-agent '' --max-time 1 -i localhost:22
* About to connect() to localhost port 22 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 22 (#0)
> GET / HTTP/1.1
> Host: localhost:22
> Accept: */*
>
SSH-2.0-OpenSSH_6.6.1
Protocol mismatch.
* Recv failure: Connection reset by peer
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer

Note: HTTP headers will be sent to the target which is often undesirable.