Python Network Programming for devs, hackers, and pentesters.
·
Report Bug
·
Request Feature
Table of Contents
Python is the "darling" language for cybersecurity lovers. As we explore the content of one of the biggest best-sellers on the subject (Black Hat Python), we will learn deeply about the technologies and even build our own pentesting tools with Python!
We will learn about chapters 2, 3, and 4 of Black Hat, covering networking themes. We will learn to make our own TCP client, UDP client, TCP server, create a Netcat substitute, a Proxy, SSH tunneling, IP layer decoding, email credential theft, Sniffing, and much more (let's stop being just "script kiddies"). So sit back comfortably and let's get hacking!
If you love programming, love Python, and love cybersecurity, this content is for you! I made it with lots of love 💜.
Let's start preparing the environment for programming. To begin, have a computer with internet access, a text editor or IDE (my advice is to use PyCharm free version). The book asks you to install Kali Linux in a VM, but it can be any Linux machine or even MacOS. If we need any specific package installation, I will mention it in each chapter, don't worry!
My tips are: practice each theme a lot, look closely at the examples and external references I will place in each subject, search for information on the discussed theme on the internet. If you have any specific doubt, you can send it to me via contact or post on StackOverflow. I hope you have the curiosity and commitment to absorb each topic deeply. If an example seems very difficult at first, calm down and look carefully and without haste, as I will comment on each topic step by step.
- Linux Machine (either VM or installed on HDD)
- Prior knowledge in Python (I won't go into syntax details but rather functionalities, so it's good to have intermediate knowledge of programming logic and Python)
- Kali-Linux (optional)
- Knowledge of Linux commands
- Python3 installed on your machine (preferably system-wide default)
- WingIDE (optional, book suggestion)
- Basic knowledge of computer networks (knowing how protocols work can help you a lot in this demo)
Note: I placed some cool links in the Acknowledgments at the end of the file with some cool content on Networks, Linux commands, PyCharm installation...
To install pip so we can install some tools we will use, run the following command:
sudo apt-get install python-setuptools python-pipTo install Python 3, first check if there is any Python version installed on your machine with the command:
$ which pythonor
$ which python3which should return something like /usr/bin/python. This means Python is installed at that address.
To install it with apt-get:
$ sudo apt-get install python3$ sudo apt-get install python3-pipTo install WingIDE use the command:
$ sudo dpkg -i wingide5_5.0.9-1_i386.deband
$ sudo apt-get -f installTo send some garbage data for testing, fuzzing, or any other task, we must have a TCP Client in a simple way.
- I import the socket library to use blocking sockets.
- I define my host and port to connect to.
- I create a socket object with the
AF_INETparameter which tells me we want a standard IPv4 address or a host name, andSOCK_STREAMwhich indicates this will be a TCP client. - I send some data to it.
- I receive some data.
Note: Making some serious assumptions about sockets that you should be aware of. The first assumption is that our connection will always succeed, the second is that the server will always be expecting us to send data first (as opposed to servers that expect to send data to you first and wait for your response), the third assumption is that the server will always send data back immediately.
Not being very different from the TCP client, we make only four small changes so that packets are sent in UDP format:
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)- Where
SOCK_DGRAMindicates a UDP client. - We do a
bind()to connect the socket to the destination address. - And with
sendto()we pass the data and the server to which you want to send the data. - The last step consists of calling
recvfrom()to receive the UDP data back.
Note: Since the UDP protocol is not connection-oriented, there is no call prior to connect(). You will observe that both data and details about the remote host and port are received, along with the UDP message which in our case was Hello, server UDP.
For more information about UDP connection with Python see the link UDP example
Creating a TCP server is as simple as creating a client, but attention is needed to configure your server to your client; maybe connection errors can occur because you might be trying to make this connection on reserved ports. Pay attention!
Here is a standard multithreaded TCP server:
- At the beginning, we do what we are already used to doing with
bind(), passing the IP address and the port we want the server to listen on. - Then with
server.listen(1)we tell the server to start listening with a maximum of connections defined at 1 (my choice, you can put as many as you want). - Then with the function
handle_client()the server enters its main loop, where it waits for an incoming connection. - When the client connects, we receive the client socket in the
clientvariable and the remote connection details in anaddrvariable inclient, addr = server.accept(). - Next, we create a new thread object pointing to our
handle_client()function and pass the socket object referring to the client as theclientargument. - We start the thread to handle the connection with the client, and our server's main loop will be ready to handle another incoming connection in
client_handler.start(). - The function
client_handler()executesrecv()and then sends a simple message to the client "ARK!".
For more information about the connection between TCP server and TCP client with Python see the link TCP connection example
Note: That's it, these codes will be extended (in new files, so as not to mess up versioning) in the next sections, where we will develop a substitute for Netcat and a TCP Proxy! So let's go!
The time has come to create our own Swiss Army knife of networking: NETCAT. Often "smart" network administrators remove it from the system, but python is present on the servers.
So for these cases, it is very useful to create a simple network client and server that can be used to send files or have a listener (process that checks for connection requests) that enables access.
If you hacked a web system, you certainly should leave a Python callback for secondary access before resorting to using one of your Trojans or backdoors.
1 - So let's start with our Netcat: Calm down! The file is large, I know! But let's go by parts, I'll start explaining from the beginning:
- First I do some imports.
- Then I define some global variables.
- Then create the main function
def usage():, it will be responsible for handling command line arguments by calling the rest of the functions.
Starting by reading all command line options:
try:
opts, args = getopt.getopt(sys.argv[1:], "hle:t:p:cu:",
["help", "listen", "execute=", "target=", "port=", "command", "upload="])
except getopt.GetoptError as err:
logging.error("%s", err)
usage()and defining the necessary variables according to the detected options. And if any command information does not meet our criteria, we will display information on how to use the script according to our usage() function.
And here we try to imitate netcat and send data from stdin over the network:
if not listen and len(target) and port > 0:
buffer = sys.stdin.read()
client_sender(buffer)And finally, we detect that it is necessary to set up a socket to listen to the network and process additional commands: server_loop() where we load a file, execute a command, start a command shell.
2 - From now on let's talk about the second part of our script def client_sender():, I already think you are getting familiar with all this.
- We start creating our TCP socket object and then we test it.
- Then we test it to know if we receive any input data from stdin.
if len(buffer):
client.send(buffer)
while True:
...- If everything is ok, we will send the data remotely and receive data back (
while recv_len:) - Then we prepare more input data (
buffer = input('')) from the user and will continue to send and receive data until the user terminates our script.
3 - Now let's create the main loop of our server, along with a stub function that will handle both the execution of our command and our full command shell.
Well at this stage of the championship you should already be very familiar with creating a complete TCP server with threading, so let's not go into details of our function server_loop():
- Something in the function
run_command():that we haven't talked about yet is subprocess, which is a library that provides an effective interface to create processes, providing several ways to start and interact with client programs. - Here
output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)we simply execute whatever command is passed, executing on the operating system and returning the command output to the client that connected to us.
4 - Finally, let's implement the logic to upload files, execute commands, and implement our shell: client_handler():
- This first portion of code (
if len(upload_destination):) is responsible for determining if our network tool is configured to receive a file when a connection is established. - Initially, we receive the file data in a loop (
while True: data = client_socket.recv(1024)) to ensure we receive everything. - Then, we process our execution functionality, where we call our function
run_command()and simply send the result back over the network. - Finally, we have the code that handles our command shell, it continues to execute commands as we send them and the output is kept back.
In a terminal or cmd.exe shell, run our script as follows:
kali-linux$ ./szynet.py -l -p 9999 -c
You can go to another terminal or cmd.exe and run our script as a client (just like we did with the TCP server and client). Remember that our script is reading from stdin and will do so until it receives the EOF (end-of-file) marker. To do this, press CTRL + D on your keyboard:
kali-linux$ ./szynet.py -l -p 9999 -c
<CTRL-D>
<BHP:#> ls -la
drwxr-xr-x 4 kali-linux staff 136 18 Dec 19:45 .
drwxr-xr-x 4 kali-linux staff 136 9 Dec 17:34 ..
-rwxrwxrwx 1 kali-linux staff 8498 19 Dec 06:14 szynet.py
-rw-r--r-- 1 kali-linux staff 844 10 Dec 10:45 listing-1-3.py
<BHP:#> pwd
/home/kali-linux/scripts/netcatNote: You will notice that the code and functions are not in the order of the comments, that's because I made the commentary according to importance and the script execution process.
Congratulations, you reached the end of a very difficult stage, I hope you understood what we did and are still willing to learn more, because this was just the beginning, we are just warming up for now. Now let's create a TCP PROXY that we will use in various attack scenarios.
The reasons for having a proxy in your toolbox are numerous; we can use it to forward traffic to be sent from a host, or to evaluate network-based software. When performing penetration tests in corporate environments, you will commonly encounter the fact that you cannot execute Wireshark, cannot load drivers to do sniffing on Windows loopback... So let's create our own TCP proxy!
Our script contains seven functions, I'll explain one by one, some you should already be used to.
1 - server_loop()
- You should already be very familiar with this function, we start by receiving some command line arguments.
- Then we fire a loop on the server that listens waiting for connections.
- When a new connection request arises, it will be passed to the next function
proxy_handler()
2 - main()
- The main function of our script capable of defining the data to be used and calling our function
server_loop()with the correct parameters to listen to our network.
3 - proxy_handler()
- Here is the function that will do all the work of sending and receiving data to either side of the data stream.
- We start by doing a check to ensure we won't need to start a new connection.
- Then we use our function
receive_from()which will be used on both sides of the communication. - Then we dump the packet content to inspect them and see if there is anything interesting.
- Next we pass the output to our function
response_handler(). - The rest is simple: we read continuously from the local host, process, send to the remote host, read the host, process, and send to the local host.
4 - hexdump()
- In the last part of the code.
- Initially we create our hex dumping function that will simply display the packet details showing both hexadecimal values and ASCII characters that can be displayed.
5 - receive_from()
- This function is used to receive both local and remote data and simply passes the socket object to be used.
6 - request_handler() & response_handler()
- The last two functions allow modifying any traffic destined for either side of the proxy.
Well, to test you can simply run a command with the parameters that we already predefined for our function to receive in the prompt, preceded by sudo because it may be that the port you chose might be privileged and requires root user authorizations (in my case I will put on port 21 to connect with my or our TCP client) like this:
kali-linux$ sudo python3 ./main.py 127.0.0.1 ftp.target.ca 21 TrueOr as I think you want to be a little more bold and comprehensive you can connect to an FTP server of your choice and that will really answer you, like google.com, facebook.com,... Just configure your browser's proxy on port 80 (HTTP access port) and run your code with the modifications as in the following example:
In the browser (in my case I will use Firefox):
Go to Preferences > Network Settings
Leave the configuration as follows and run the command:
kali-linux$ sudo python3.9 ./main.py 127.0.0.1 80 www.google.com 80 True And something like this will appear on your screen:
[*] Listening on 127.0.0.1:80
[==>] Received incoming connection from 127.0.0.1:41452
[<==] Received 201 bytes from localhost.
0000 43 4F 4E 4E 45 43 54 20 77 77 77 2E 67 6F 6F 67 C O N N E C T w w w . g o o g
0010 6C 65 2E 63 6F 6D 3A 34 34 33 20 48 54 54 50 2F l e . c o m : 4 4 3 H T T P /
0020 31 2E 31 0D 0A 55 73 65 72 2D 41 67 65 6E 74 3A 1 . 1 . . U s e r - A g e n t :
0030 20 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 58 31 M o z i l l a / 5 . 0 ( X 1
0040 31 3B 20 4C 69 6E 75 78 20 78 38 36 5F 36 34 3B 1 ; L i n u x x 8 6 _ 6 4 ;
0050 20 72 76 3A 37 38 2E 30 29 20 47 65 63 6B 6F 2F r v : 7 8 . 0 ) G e c k o /
0060 32 30 31 30 30 31 30 31 20 46 69 72 65 66 6F 78 2 0 1 0 0 1 0 1 F i r e f o x
0070 2F 37 38 2E 30 0D 0A 50 72 6F 78 79 2D 43 6F 6E / 7 8 . 0 . . P r o x y - C o n
0080 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61 6C n e c t i o n : k e e p - a l
331: [==>] Sent to remote.
332: [<==] Received 1775 bytes from remote.
333: 65 64 69 61 20 6F 6E 6C 79 20 73 0 } } @ m e d i a o n l y s
334: 0500 63 72 65 65 6E 20 61 6E 64 20 28 2D 77 65 62 6B c r e e n a n d ( - w e b k
335: 0510 69 74 2D 6D 69 6E 2D 64 65 76 69 63 65 2D 70 69 i t - m i n - d e v i c e - p i
336: 05A0 74 2D 62 61 63 6B 67 72 6F 75 6E 64 2D 73 69 7A t - b a c k g r o u n d - s i z
337: 05B0 65 3A 31 30 30 25 20 31 30 30 25 7D 7D 23 6C 6F e : 1 0 0 % 1 0 0 % } } # l o
338: 05C0 67 6F 7B 64 69 73 70 6C 61 79 3A 69 6E 6C 69 6E g o { d i s p l a y : i n l i n
339: 05D0 65 2D 62 6C 6F 63 6B 3B 68 65 69 67 68 74 3A 35 e - b l o c k ; h e i g h t : 5
340: 05E0 34 70 78 3B 77 69 64 74 68 3A 31 35 30 70 78 7D 4 p x ; w i d t h : 1 5 0 p x }
341: 05F0 0A 20 20 3C 2F 73 74 79 6C 65 3E 0A 20 20 3C 61 . < / s t y l e > . < a
342: 0600 20 68 72 65 66 3D 2F 2F 77 77 77 2E 67 6F 6F 67 h r e f = / / w w w . g o o g
343: 0610 6C 65 2E 63 6F 6D 2F 3E 3C 73 70 61 6E 20 69 64 l e . c o m / > < s p a n i d
344: 0620 3D 6C 6F 67 6F 20 61 72 69 61 2D 6C 61 62 65 6C = l o g o a r i a - l a b e l
345: 0630 3D 47 6F 6F 67 6C 65 3E 3C 2F 73 70 61 6E 3E 3C = G o o g l e > < / s p a n > <
346: 0640 2F 61 3E 0A 20 20 3C 70 3E 3C 62 3E 34 30 35 2E / a > .
347: [==>] Sent to localhost.
348: [*] No more data. Closing connections.Well that's all folks. Let's go to the next one!
The comments and codes were taken from the book: Black Hat Python by Justin Seitz published by Novatec. With some changes regarding the comments and the code adapted to Python version 3.9
Send me a message if you have any questions
João Lucas (Instagram) 🚀
