Python3 Quickly Pull TCP Service Banner

Hello readers! I recently wanted to pull the banner that was being supplied when connecting to a TCP service. In this case, I was poking a ZyWall device. To do this, we can simply use python's socket lib and decode messages.

Let's dive into the code, shall we?

#!/usr/bin/env python3

import sys
import socket

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

host = sys.argv[1].rstrip()
port = sys.argv[2].rstrip()
port = int(port)

try:
	s.connect((host,port))
except Exception as e:
	print("[+]Error: {0}".format(e))
try:
	bdata = s.recv(128).decode('utf-8').strip()
	print(bdata)
except Exception as e:
	print("[+]Error: {0}".format(e))

s.close()

If you don't know entirely what's going on here with sockets, that's fine; we'll go over it.

As we will be connecting to a TCP service to send/receive messages, we are going to import the socket library.

Next step, we want to build a socket object. We know that we will be issuing our request over TCP. Thus, we will be using socket.AF_INET, socket.SOCK_STREAM)

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

AF_INET declares Address Family, Internet Protocol v4. As TCP does not use datagrams—like UDP does—it uses a continuous stream of bytes. This requires us to use SOCK_STREAM. If we were continuing on with creating a UDP socket, we would specify socket.SOCK_DGRAM.

We need to send a host/port pair over to our socket to connect to. This is where we send the .connect() method.

s.connect((host,port))

As some services will send a banner or data to clients just after a connection is established, we can simply listen/receive data from the server. To do this, we call:

bdata = s.recv(128).decode('utf-8').strip()

Per python socket manual, to receive data, we call socket.recv(bufsize[, flags]). As we're really only interested in retrieving banner data, we'll specify a relatively small buffer of 128 bytes. Of course, you may need to change this depending on what you are doing. 128 bytes is quite a lot of data, however, YMMV.

As our TCP client will, in many cases, receive a literal byte string, we'll decode the bytes to utf-8 and also strip() out carriage returns and new lines, else, we'll receive something like b'220 FTP Server (ZyWALL USG 20) [::ffff:192.168.66.11]\r\n'.

This allows us to retrieve a nicer looking banner:

synfinner@synbook ~/P/p/Packd> ./packd.py 192.168.66.11 21
220 FTP Server (ZyWALL USG 20) [::ffff:192.168.66.11]