Jump to content
Byte-ul

TCP Packet Injection with Python

Recommended Posts

Packet injection is the process of interfering with an established network connection by constructing arbitrary protocol packets (TCP, UDP, ...) and send them out through raw sockets

it's used widely in network penetration testing such as DDoS, TCP reset attacks, port scanning...

A Packet is a combination of IP header, TCP/UDP header and data:

Packet = IP Header + TCP/UDP Header + Data

Most operating systems that implements socket API supports packet injection, especially those based on Berkeley Sockets. Microsoft limited raw sockets capabilities to packet sniffing, after Windows XP release. This tutorial is implemented on Unix-Like operating systems.

TCP Header

The TCP protocol is the most used transport protocol in the world wide web, it provides a reliable, ordered and error-checked delivery of a stream of bytes between programs running on computers connected to network.

0 1 2 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Source Port | Destination Port |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Sequence Number |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Acknowledgment Number |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Data | |U|A|P|R|S|F| |

| Offset| Reserved |R|C|S|S|Y|I| Window |

| | |G|K|H|T|N|N| |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Checksum | Urgent Pointer |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Options | Padding |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| data |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  • Sequence Number (32 bits): the sequence number of the first data byte in this segment. if the SYN flag is set, the sequence number should be the initial sequence number (ISN: usually 0), and the first data byte in the first data stream should be ISN+1 (1).
  • Acknowledgment Number (32 bits): If the ACK flag is set, this field contains the value of the next sequence number the destination machine is expecting to receive.for every packet contains data is sent, an acknowledgment packet should be received, to acknowledge that the last packet is successfully received.
  • Data Offset (4 bits): The length of TCP header by providing the number of 32-bit words. this indicates where the data begins.
  • Reserved (6 bits): Usually cleared to zero
  • Control Bits (6 bits):ACK: Acknowledgment packetSYN: Request to establish a connectionRST: Request to reset connectionFIN: Request to interrupt (close) a connectionPSH: Informs TCP that data should be sent immediately (Useful in real-time applications)URG: Urgent Pointer field is significant
  • Window: The number of data bytes you can send before you should stop and wait for acknowledgement
  • Checksum: used for error-checking of the header and data
  • Urgent Pointer: If the URG control flag is set, this field is an offset from the sequence number indicating the last urgent data byteThis feature is used when some information has to reach it's destination as soon as possible.

Creating Raw Socket

For creating a socket capable of injecting manually crafted packet, we pass "SOCK_RAW" as a type of socket, and "IPPROTO_RAW" as the used protocol. The "IPPROTO_RAW" tells the kernel that we will provide the network layer (Internet Protocol) and the transfer layer (TCP/UDP Protocol)

s = socket.socket(socket.AF_INET,

socket.SOCK_RAW,

socket.IPPROTO_RAW)

Constructing the IP Header

For this purpose, we can write a simple class to deal with the IP Header structure:

class ip(object):

def __init__(self, source, destination):

self.version = 4

self.ihl = 5 # Internet Header Length

self.tos = 0 # Type of Service

self.tl = 0 # total length will be filled by kernel

self.id = 54321

self.flags = 0 # More fragments

self.offset = 0

self.ttl = 255

self.protocol = socket.IPPROTO_TCP

self.checksum = 0 # will be filled by kernel

self.source = socket.inet_aton(source)

self.destination = socket.inet_aton(destination)

def pack(self):

ver_ihl = (self.version << 4) + self.ihl

flags_offset = (self.flags << 13) + self.offset

ip_header = struct.pack("!BBHHHBBH4s4s",

ver_ihl,

self.tos,

self.tl,

self.id,

flags_offset,

self.ttl,

self.protocol,

self.checksum,

self.source,

self.destination)

return ip_header

The "pack" method packs the ip header elements and return it.

ipobj = ip("127.0.0.1", "127.0.0.2") # Creating an ip object instance

ipobj.source = "localhost" # Changing IP element value

iph = ipobj.pack() # packing ip header elements

Constructing the TCP Header

This is a tcp class that allows us to easily manipulate the tcp header elements and pack them.

class tcp(object):

def __init__(self, srcp, dstp):

self.srcp = srcp

self.dstp = dstp

self.seqn = 0

self.ackn = 0

self.offset = 5 # Data offset: 5x4 = 20 bytes

self.reserved = 0

self.urg = 0

self.ack = 0

self.psh = 1

self.rst = 0

self.syn = 0

self.fin = 0

self.window = socket.htons(5840)

self.checksum = 0

self.urgp = 0

self.payload = ""

def pack(self, source, destination):

data_offset = (self.offset << 4) + 0

flags = self.fin + (self.syn << 1) + (self.rst << 2) + (self.psh << 3) + (self.ack << 4) + (self.urg << 5)

tcp_header = struct.pack('!HHLLBBHHH',

self.srcp,

self.dstp,

self.seqn,

self.ackn,

data_offset,

flags,

self.window,

self.checksum,

self.urgp)

#pseudo header fields

source_ip = source

destination_ip = destination

reserved = 0

protocol = socket.IPPROTO_TCP

total_length = len(tcp_header) + len(self.payload)

# Pseudo header

psh = struct.pack("!4s4sBBH",

source_ip,

destination_ip,

reserved,

protocol,

total_length)

psh = psh + tcp_header + self.payload

tcp_checksum = checksum(psh)

tcp_header = struct.pack("!HHLLBBH",

self.srcp,

self.dstp,

self.seqn,

self.ackn,

data_offset,

flags,

self.window)

tcp_header+= struct.pack('H', tcp_checksum) + struct.pack('!H', self.urgp)

return tcp_header

As we know, TCP is end-to-end reliable protocol. Reliability is provided by the checksum field which used to ensure that data reaches it's correct destination and is error-free. A checksum is 16 bits number calculated by summing the one's compliment of Pseudo header, tcp header and data.

The pseudo header is a combination of 5 different fields that includes the source ip and destination ip:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Source IP address |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Destination IP address |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Reserved | Protocol | Total Length |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  • Source IP address (32 bits): Sender's IP address
  • Destination IP address (32 bits): Receiver's IP address
  • Reserved (8 bits): Cleared to zeros
  • Protocol (8 bits): Transport protocol (6 for TCP, 17 for UDP)
  • Total Length (15 bits): size of tcp header and data

During the TCP header checksum calculation, the checksum field should be set to zero. Once it's value is calculated, it should be inserted into the field before sending the packet.

Constructing the pseudo-header fields:

#pseudo header fields

source_ip = source

destination_ip = destination

reserved = 0

protocol = socket.IPPROTO_TCP

total_length = len(tcp_header) + len(self.payload)

Packing the pseudo-header and combining it with TCP header and data:

# Pseudo header

psh = struct.pack("!4s4sBBH",

source_ip,

destination_ip,

reserved,

protocol,

total_length)

psh = psh + tcp_header + self.payload

The checksum function:

def checksum(data):

s = 0

n = len(data) % 2

for i in range(0, len(data)-n, 2):

s+= ord(data) + (ord(data[i+1]) << 8)

if n:

s+= ord(data[i+1])

while (s >> 16):

print("s >> 16: ", s >> 16)

s = (s & 0xFFFF) + (s >> 16)

print("sum:", s)

s = ~s & 0xffff

return s

Packet Injection

This is an example of using ip and tcp classes to construct headers and injecting it:

s = socket.socket(socket.AF_INET,

socket.SOCK_RAW,

socket.IPPROTO_RAW)

src_host = "10.0.2.15"

dest_host = socket.gethostbyname("www.reddit.com")

data = "TEST!!"

# IP Header

ipobj = ip(src_host, dest_host)

iph = ip_object.pack()

# TCP Header

tcpobj = tcp(1234, 80)

tcpobj.data_length = len(data) # Used in pseudo header

tcph = tcpobj.pack(ipobj.source,

ipobj.destination)

# Injection

packet = iph + tcph + data

s.send(packet, (dest_host, 0))

Pinject.py

Running the script:

python pinject.py --src=10.0.2.15 --dst=www.reddit.com

[+] Local Machine: 10.0.2.15

[+] Remote Machine: 198.41.209.142

[+] Raw scoket created

[+] Data to inject: TEST!!

[+] Constructing IP Header

[+] Constructing TCP Header

[+] Packet Injected!

Screenshot from Wireshark:

response.png

Fork me on github:

https://github.com/offensive-python/Pinject

Source: TCP Packet Injection with Python | Python for Pentesting

Edited by Byte-ul
  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...