Skip to content

Packets manipualtion

If you are here it's mean that you have tested your proxy and you liked it, I know me too 🙄

Now we are talking about packet manipulations, where the fun part begin, for this example we will leverage our previous article about proxy and it's code base, to refactor the request_handler and response_handler functions to manipulate FTP commands and responses

We are doing it for educational purposes OF COURSE no need to write long ethical speeches.

Packet manipulation is a fundamental concept in networking and cybersecurity that involves intercepting, modifying, and sometimes injecting network packets as they transit between a client and a server. Understanding packet manipulation can help you analyze protocols, test network security, and develop a deeper comprehension of how data is transmitted over networks.

General knowledge on packet manipulation

What is a network packet

  • Definition: In networking, a packet is a unit of data formatted for transmission over a network. It contains both control information (like source and destination addresses) and user data (payload).
  • Structure: Packets are structured according to the protocols in use (e.g., TCP/IP, HTTP, FTP), with headers and footers that wrap the actual data. See more about packet structure on wikipedia here 👀

Why manipulate packets

  • Testing and Debugging: To test how applications handle unexpected or malformed data.
  • Security Assessments: To discover vulnerabilities by injecting malicious payloads or altering data.
  • Protocol Analysis: To understand how proprietary or undocumented protocols function.
  • Educational Purposes: To learn about network communications and protocol behavior, that's us here 😇

Well known techniques for packet manipulation

  • Interception: Capturing packets as they pass through the network using tools like Wireshark or tcpdump.

  • Modification: Altering packet headers or payloads before forwarding them. Examples include changing HTTP headers, modifying FTP commands, or altering binary data.

  • Injection: Introducing new packets into the communication stream. This can simulate certain conditions or test how an application handles unsolicited data.

  • Reassembly and Fragmentation: Breaking packets into smaller fragments or reassembling them differently. Useful for testing how systems handle fragmented packets.

  • Replay Attacks: Capturing and retransmitting packets to observe how the server responds. Often used in security testing to check for vulnerabilities.

Tools for Packet Manipulation

  • Proxies:
  • MITMProxy, Burp Suite: Interactive proxies that allow for real-time modification of HTTP and HTTPS traffic.
  • Custom Proxies: Like the one we're developing here, tailored to specific protocols or needs 🥷🏼

  • Packet Crafting Tools:

  • Scapy: A powerful Python library for crafting and manipulating packets.
  • Hping3, Nemesis: Command-line tools for crafting TCP/IP packets.

  • Network Sniffers:

  • Wireshark: Allows you to capture and analyze packets but not modify them in transit.

Improving request_handler and response_handler functions

To enhance your proxy's capabilities and educational value, you can implement more advanced packet manipulation techniques. Below are some ideas, along with code examples.

Protocol agnostic modifications

We can begin to design our handlers to detect and manipulate data based on the protocol in use. We can parse the data to identify the protocol and apply appropriate modifications.

Example: Automatically Detect and Modify HTTP and FTP Traffic

def request_handler(buffer):
    # Attempt to decode buffer as UTF-8 text
    try:
        buffer_str = buffer.decode('utf-8', errors='ignore')
    except UnicodeDecodeError:
        return buffer  # Non-text data; return unmodified

    # Check if it's an HTTP request
    if buffer_str.startswith('GET') or buffer_str.startswith('POST'):
        # Modify HTTP requests
        buffer_str = modify_http_request(buffer_str)
    elif buffer_str.strip().upper().startswith('USER') or buffer_str.strip().upper().startswith('PASS'):
        # Modify FTP commands
        buffer_str = modify_ftp_request(buffer_str)
    else:
        # Other protocols or data
        pass

    return buffer_str.encode('utf-8')

def response_handler(buffer):
    # Similar logic for responses
    try:
        buffer_str = buffer.decode('utf-8', errors='ignore')
    except UnicodeDecodeError:
        return buffer

    # Check for HTTP response
    if buffer_str.startswith('HTTP/'):
        buffer_str = modify_http_response(buffer_str)
    elif buffer_str.startswith('220') or buffer_str.startswith('230'):
        buffer_str = modify_ftp_response(buffer_str)
    else:
        pass

    return buffer_str.encode('utf-8')

Implementing Specific Modifications:

def modify_http_request(request):
    # Add headers, modify paths, etc.
    request = request.replace('User-Agent: ', 'User-Agent: MyCustomAgent')
    return request

def modify_ftp_request(request):
    # Log credentials, alter commands
    if request.strip().upper().startswith('PASS'):
        print(f"[Intercepted Password]: {request.strip()}")
    return request

def modify_http_response(response):
    # Inject content, modify status codes
    if '<body>' in response:
        response = response.replace('<body>', '<body><h1>Modified by Proxy</h1>')
    return response

def modify_ftp_response(response):
    # Change server messages
    if '230' in response:
        response = response.replace('230', '230-Welcome to the Proxy Server!')
    return response

Binary Data Manipulation

For protocols that use binary data (e.g., images, file transfers), we can manipulate the bytes directly.

Example: Modify Binary Content

def request_handler(buffer):
    # Let's say you want to corrupt the data being sent
    buffer = corrupt_data(buffer)
    return buffer

def corrupt_data(data):
    # Flip some bits in the data
    corrupted = bytearray(data)
    for i in range(len(corrupted)):
        corrupted[i] ^= 0xFF  # Invert all bits
    return bytes(corrupted)

Note: Be cautious with this, as it can lead to unexpected behavior or crashes.

Delay and Drop packets

Simulate network latency or packet loss to test how applications handle such conditions.

Example: Introduce Artificial Delay

import time

def request_handler(buffer):
    # Introduce a 2-second delay
    time.sleep(2)
    return buffer

def response_handler(buffer):
    # Randomly drop packets
    import random
    if random.randint(0, 10) < 2:  # 20% chance to drop
        print("[Dropped Packet]")
        return b''  # Return empty buffer to simulate packet drop
    return buffer

Implement SSL/TLS interception

We can also upgrade our proxy to handle encrypted traffic by acting as a man-in-the-middle for SSL/TLS connections. This will be an other story 😈

This requires generating and installing root certificates and has significant ethical and legal implications. Use only in controlled environments.

Advanced Content Manipulation**

Use regular expressions or parsing libraries to perform complex modifications.

Example: Using Regular Expressions

import re

def response_handler(buffer):
    try:
        buffer_str = buffer.decode('utf-8', errors='ignore')
    except UnicodeDecodeError:
        return buffer

    # Remove sensitive information using regex
    buffer_str = re.sub(r'password=.*?(&|\s)', 'password=******\\1', buffer_str, flags=re.IGNORECASE)

    return buffer_str.encode('utf-8')

Protocol Parsing Libraries

Use existing libraries to parse and reconstruct protocol data structures.

Example: Parsing HTTP with http Module

from http.server import BaseHTTPRequestHandler
from io import BytesIO

def request_handler(buffer):
    class HTTPRequest(BaseHTTPRequestHandler):
        def __init__(self, request_text):
            self.rfile = BytesIO(request_text)
            self.raw_requestline = self.rfile.readline()
            self.error_code = self.error_message = None
            self.parse_request()

    request = HTTPRequest(buffer)
    # Now you can access request.method, request.path, etc.
    if request.command == 'GET':
        print(f"Intercepted GET request to {request.path}")
    # Modify the request as needed
    # Reconstruct the request back to bytes
    # (You'll need to manually rebuild the request string)
    return buffer  # Placeholder

The above example is non-trivial and requires careful handling to rebuild the request. This also will be for an other story 😇

Encryption and Decryption

If you're dealing with custom protocols that implement encryption like in our previous example with FTP, we can implement decryption and encryption within our proxy. Below a simple example of decrypting custom encrypted payloads.

Example: Decrypting custom encrypted payloads

from Crypto.Cipher import AES

def request_handler(buffer):
    # Decrypt the payload
    decrypted_data = decrypt_payload(buffer)
    # Modify the decrypted data
    modified_data = decrypted_data.replace('secret', '******')
    # Re-encrypt the data
    encrypted_data = encrypt_payload(modified_data)
    return encrypted_data

def decrypt_payload(data):
    # Placeholder for decryption logic
    return data

def encrypt_payload(data):
    # Placeholder for encryption logic
    return data

Note: Handling encryption requires knowledge of the encryption method and keys, and should only be done with authorization.

Logging and alerting

We can also use this proxy by implement detailed logging and alerting mechanisms, like for preventing an SQL injection.

Example: Log Suspicious Activity

def request_handler(buffer):
    buffer_str = buffer.decode('utf-8', errors='ignore')
    # Check for SQL injection patterns
    if re.search(r"(\%27)|(\')|(\-\-)|(\%23)|(#)", buffer_str):
        print("[Alert] Potential SQL Injection detected")
        log_alert(buffer_str)
    return buffer

def log_alert(data):
    with open('alerts.log', 'a') as f:
        f.write(f"{datetime.now()} - {data}\n")

Refactoring our request_handler and response_handler functions

Let's update our functions and perform basic packet manipulations specific to FTP traffic like we're talking.

Update request_handler

The request_handler will now manipulate the FTP commands sent from the client to the server

def request_handler(buffer):
    # Decode the buffer to a string for manipulation
    try:
        buffer_str = buffer.decode('utf-8', errors='ignore')
    except UnicodeDecodeError:
        return buffer  # Return unchanged if decoding fails

    # Log the FTP command
    print(f"[Request] {buffer_str.strip()}")

    # Example 1: Block certain commands (e.g., DELE to prevent file deletion)
    if buffer_str.strip().upper().startswith('DELE '):
        # Replace 'DELE' with 'NOOP' to prevent deletion
        buffer_str = 'NOOP\r\n'
        print("[Modified Request] Blocked DELE command")

    # Example 2: Modify a command parameter (e.g., change directory path)
    if buffer_str.strip().upper().startswith('CWD '):
        # Change the directory to '/home/user'
        buffer_str = 'CWD /home/user\r\n'
        print("[Modified Request] Changed directory to '/home/user'")

    # Example 3: Inject a custom command (e.g., send a NOOP before the actual command)
    buffer_str = 'NOOP\r\n' + buffer_str
    print("[Modified Request] Injected NOOP command before the actual command")

    # Encode the string back to bytes
    return buffer_str.encode('utf-8')

Like you can see above we have put many comments in the script in order to facilitate the understunding. In summary we observe the ftp commands sent by the client, block DELE command.

If the client tries to delete a file using DELE, the command is replaced with NOOP (No Operation), preventing the deletion. If the client changes the working directory using CWD, the directory is changed to /home/user, regardless of what the client requested.

We also prepends a NOOP command before each command sent by the client because this is a fun injection 🤓

Update response_handler

def response_handler(buffer):
    # Decode the buffer to a string for manipulation
    try:
        buffer_str = buffer.decode('utf-8', errors='ignore')
    except UnicodeDecodeError:
        return buffer  # Return unchanged if decoding fails

    # Log the FTP response
    print(f"[Response] {buffer_str.strip()}")

    # Example 1: Modify the welcome message
    if buffer_str.startswith('220'):
        # Append a custom message to the welcome banner
        buffer_str = buffer_str.strip() + '\r\n220-This is a proxy server for educational purposes.\r\n'
        print("[Modified Response] Added custom welcome message")

    # Example 2: Mask server information
    if 'vsftpd' in buffer_str.lower():
        # Replace server software information with generic text
        buffer_str = buffer_str.replace('vsftpd', 'FTP Server')
        print("[Modified Response] Masked server software information")

    # Example 3: Inject a custom response after the original response
    buffer_str += '230-Note: This session is being monitored.\r\n'
    print("[Modified Response] Injected custom response message")

    # Encode the string back to bytes
    return buffer_str.encode('utf-8')
Here we have modify the welcome message, informing the client that they are connected through a proxy and replaces specific server software names (e.g., vsftpd) with a generic term to obscure server details. Then injecting a custom message to the server's response, notifying the client that the session is being monitored.

In summary

Packet manipulation is a powerful technique that can greatly enhance your understanding of network protocols and security. By improving our request_handler and response_handler functions, we can now simulate real-world scenarios, test application resilience, and explore the intricacies of network communication.

⚠️ Always remember to use these tools ethically and responsibly, ensuring that you respect privacy and legal boundaries ! ⚠️

Hope you learn some new things and enjoyed one more step to be a hacker shinobi 🥷🏼