Home

Python for Cyber - Part Two

martin reato - July 9, 2021

Introduction 

This article is the second in the series that serves as an introduction to Python scripting for Cyber. This series does assume the reader has a basic understanding of Python and networking. 

A recommended pre-requisite pathway before beginning this series is getting familiar with the basics of programming and knowing the Python fundamentals; which can be achieved by doing WYWM’s awesome Software Development Pathway. This will teach you the basics of programming and Python fundamentals.

Furthermore, an understanding of networking terminology is also recommended. You guessed it… WYWM has got you covered with their Networking Fundamentals Course.

I have also included a challenge for those who enjoy those ! Full details at the end of the article.

Who is this series useful for ?

This series will benefit python enthusiasts, cyber security analysts, penetration testers and anyone who wants to familiarize themselves with exploit writing. It will also benefit those who just enjoy python and love to “geek” out and have the opportunity to write their own code when faced with a challenge either in the workplace or when doing a machine on TryHackMe or HackTheBox. 

As an example, recently I was doing a challenge where I had to go through PDF files saved in a sub-directory. I didnt have the ability to see the total number of pdf files. All I could see was one file at a time by requesting the valid name of the pdf file in a browser. However, one thing was very clear, they all had the same nomenclature format. Yes, I used a tool like Burp’s intruder to figure out which files existed; however, I also then went on to write my own python code to reproduce that. I just love programming and automating tasks. It’s a great skill to have and it comes in handy. Some tools will not always be available depending on the scope, context and rules of engagement. Knowledge of Python can go a long way when it's all you have.

TCP Server

This second entry into the series will focus on demonstrating how we can create a simple TCP server. It is as easy as creating the TCP Client in part one. At the end we will be able to have the TCP client created in part one communicate with our newly created server. Part one can be viewed by pressing the below button:

Step 1

First we are going to create a file called “udp_client.py” using a text editor. Feel free to use nano, VIM or any text editor. For our first TCP server our script starts with the importing of the socket module. This module provides us with the necessary functions to create a server.

import socket

The socket module provides many useful functions and methods when it comes to networking. For our TCP server we will use the below: 

  • socket()
  • bind()
  • listen()
  • accept()
  • recv()
  • send()
  • close()

For those who are interested in investigating the module more in-depth  they can find the full documentation of the socket module here: https://docs.python.org/3/library/socket.html

Step 2

The next step will be to add two variables to our script which will be named “IP” and “PORT”. The “IP” variable will hold the ip address or range of addresses that the server will listen to.  The “PORT” variable will hold the port number our server will listen on. 

In this example we are using an IP of “127.0.0.1”. This is used so our TCP server accepts incoming client connections from our localhost; however, we could always put in a specific address or even “0.0.0.0”. The ip address “0.0.0.0” would instruct our server to accept clients coming from any inbound IP address.

IP = “127.0.0.1”

PORT = 1337

Step 3

This next step creates a socket object called “server” and specifies the type of socket we want to use. Our next line will be the following:

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

  • server - is the name we give our object. It can be anything besides reserved names. Ideally you would want to write something that describes the function of the object you are creating.
  • socket.socket() - is the object we are creating from our imported module
  • socket.AF_INET is a constant. This parameter is typically what you will most often use when creating a client or server. When passed to socket() indicates we are going to use ipv4.
  • socket.SOCK_STREAM is a constant. This parameter is passed to socket() and indicates we will be using TCP.

Step 4

Our next line of code will use the bind() function from the socket module. The module takes two parameters. We need to pass to the function an IP address and a port number. Using the two parameters then the bind() function assigns the port number and IP address to the server object. 

server.bind((IP,PORT))

  • server - refers to the object we created in the previous step where we basically identified that our server will be using ipv4 and TCP.
  • bind() - sets the server to use the specified “IP” and “PORT” variables we created in step 2.

Step 5

The previous step we attached an IP address and PORT to our server. In this step we will use the listen() function; this will instruct the server to start listening on the PORT we passed in the previous step with the bind() function. Furthermore, it knows to listen for the loopback address (127.0.0.1).

server.listen()

  • server - refers to the object we created in step 3 where we basically identified that our client will be using ipv4 and TCP. 
  • listen() - is the method by which we instruct our server how many concurrent connections it can handle. We don’t need to provide any parameters to the function as we are only using it to initiate one connection from our TCP client. (This does create problems in python2, more to follow further below).

Step 6

Our next line of code will use the accept() method which starts a listener on the specified port of the localhost. Furthermore, it is important to know that when a client connects to the server the accept() method stores the data in a tuple. The IP and Port number used by the incoming client is saved into this tuple so the program can keep in memory where to send data back to the client.

client,(ip,port) = server.accept()

  • client, (ip,port) - tuple "client" which holds/saves an incoming connections details as IP and PORT
  • server.accept() - this opens a port and listens for incoming connections.

At this point we can verify if our server does bind itself to port 1337. Even in its unfinished state we can confirm that everything is working as intended so far. For clarity and ease of use, we will add a simple print statement indicating that the server is listening.

We can run our server in one terminal and verify that indeed the server is listening on port 1337 in another terminal by typing:

sudo lsof -i:<port number>

Great! Our server is listening on port 1337. 

Step 7

In order to send data, a client has to connect to the server. Once that is done, the client variable(which is a tuple) we created in the previous step will have a return address composed of the IP and PORT number it originated from. Therefore, in order to send information to the client we use the following line of code: 

client.send(b’You have successfully connected to the TCP server’)

  • client.send() - instructs our program to send data to the client. Here we send a message, simply letting the user who connected with the TCP client, know that he is successful.

Step 8 

Once we have sent a confirmation message to the client, we will have our TCP client send a message to the server also. For this we will create another variable called “data” where the received data will be stored. We will also add  a print statement to output the data.

  data = client.recv(1024)

print(f’[*] Received message: {data.decode(“utf-8”)}’)

  • Data - is our newly created variable.
  • Client - is the tuple created by accept() previously 
  • recv() - is the function responsible to receive data from the client in this instance. (client.recv()) We also assign a buffer memory of 1024 bytes for the message being sent. This can be adjusted, for our purposes this is more than enough.
  • print() - prints the received data to the terminal.

Step 9 

Now that the TCP server can send and receive information; we can close the connection. We will add two lines of code as we close the client connection and the server.

client.close()

server.close()

  • client.close() - closes the connection with the client.
  • server.close() - closes the server. Unbinds the port attributed during connection.

Step 10

For this next step we will have to use the TCP client we created in Part 1. If you haven't done or completed your TCP client, I suggest giving part one a try by pressing the below button:

That said, we could also connect to our server with another tool such as netcat. If using Netcat skip this step and go to step 11.

In part 1 of the series, we made our TCP client make a “GET” request to a HTTP address. For this step we are going to change the TCP send() function to just include a simple “Hello” message. The below images of the TCP client might differ slightly if the argparse module was imported.

Original TCP client

Modified TCP client

Step 11

Our TCP client is ready (or netcat) and the TCP server is also finished. Let's make a connection! In one terminal we start our server.

Python3 tcp_server.py

Then we run the client on the loopback address and the port we binded our server::

Python3 127.0.0.1 1337

The connection is made and the TCP client sends the message “Hello”. As mentioned earlier, we can achieve the same result with Netcat; however with Netcat the message must be typed out by the user and the ‘enter’ key pressed for the message to be sent.

Handling a Common Error

An error that is often encountered when creating a server is the error observed in the following image :

This occurs when the port combination is already in use. If you chose a port in use by another service this error will happen; however, this will most likely happen to your TCP server when you close the connection and try to restart the server too quickly. It might take a few seconds for the port to be free again because it is not available for reuse. We can fix this with one additional line of code that uses the setsockopt() function.

server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

There will be no errors when trying to restart the server immediately following a closed connection or a crash of the server.

For more information on the setsockopt() function please refer to the following resource:

https://docs.python.org/3/library/socket.html

Functionality Upgrade

Great, our server works. Let’s make it so that our server can receive information until the client closes the connection and not only receive one string of data followed by a close of the connection.

For this we will instruct our script to run our accept() function to listen until someone closes the server. We also nest another while loop for the recv() function. This second loop waits for data to be sent by the client until the client closes the connection.  In addition, we insert an IF statement so that if the client does close the connection we exit our nested while loop and return to our initial loop which listens for any incoming connections. 

Now we can verify that everything works as intended. For this example we will use Netcat to send the data as our TCP client was net set-up to send more than one line of data.

We can observe the received messages from the Netcat client and when Netcat is closed the server remains operational and listens for other connections. To close the server we can simply press CTRL+C.

Python2 vs Python3

In part 1, I mentioned a few differences between Python2 and 3. There are a few differences we can observe in our server also. In python2, there are errors produced when running the TCP server. The first one being the listen() function that takes an argument. In Python3 we don't need to supply one; it defaults to 1. This was sufficient for our needs. If we wanted to listen to numerous connections this would have to be modified. That said, in Python2 we have to provide a parameter. 

Simple addition of a parameter will fix this issue.

In our TCP server I use the “f” prefix in the print() statement. This is called an f-string and the prefix must be included because of the embedded “data” variable that is being used inside the braces.

Removing the “f” prefix would print the braces and its contents literally.

When running the script in Python2 we get an error:

This happens because the f-string was introduced in Python3.6. In Python2, in order to achieve our desired result we could change our print() statement to the following:

With the changes done to the script our Server works with Python2:

Challenge and Incentive

As an extra challenge. Can you modify the TCP client created in Part 1 to send more than one line of data with the created server? Just like Netcat did.

Instructions:

The TCP client created in part 1 sends one line of data and closes the connection. Make it so that it can send data to the server until the connection is intentionally closed by the client. Send me your code snippet or link to a github repo to my email (martin.reato@withyouwithme.com) or on Discord (mreato#5413). Among the participants I will randomly select one individual and give them a prize.

Challenge end date: 16 July 2021

Prize: 3-month TryHackMe Voucher

Conclusion

This marks the end of part 2 of the Python for Cyber series. Our TCP server is complete and we were able to have our TCP client and Netcat connect with it. There are many ways to configure our server and even the client from part 1 to improve the functionality. Feel free to experiment and learn something new, be creative! 

Feel free to reach out for any questions/comments/suggestions.

Cheers ! 

martin.reato@withyouwithme.com

If you want to break into the tech industry then sign up to our platform and begin your training today.

Leave a Reply

Your email address will not be published. Required fields are marked *

Join our community

We have a Discord server where you’ll be able to chat with your instructors and cohort. Stay active in your learning!
Join discord