Unpacking TCP-fragment give incorrect result - python

I have a problem with my packet sniffer. The destination port and source port seems to be wrong in my sniffer. In wireshark the ports is totally different from my sniffers. No result contains port 443 expected from TLS. (The whole tcp-fragment might be wrong.)
Does it have to do something to do with the router?
I also know that there is some problems doing sniffing in windows. Or is my unpacking code just wrong? Am i missing some offset between ip-header and tcp-fragment ?
Socket code: https://pastebin.com/tMuHgz0R
Unpacking code: https://pastebin.com/9ZVfYNEE (full code)
# Unpack tcp fragment
def tcp_fragment(raw_data):
tcp_header = struct.unpack('!HHLLBBHHH', raw_data[:20])
source_port = tcp_header[0]
destionation_port = tcp_header[1]
sequence_number = tcp_header[2]
acknowledgement_number = tcp_header[3]
offset = tcp_header[4] >> 4
reserved = tcp_header[4] & 0xF
flags = get_tcp_flags(tcp_header[5])
window = tcp_header[6]
checksum = tcp_header[7]
pointer = tcp_header[8]
return {
TCP_SOURCE_PORT: source_port,
TCP_DESTINATION_PORT: destionation_port,
TCP_SEQUENCE_NUMBER: sequence_number,
TCP_ACKNOWLEDGEMENT_NUMBER: acknowledgement_number,
TCP_OFFSET: offset,
TCP_RESERVED: reserved,
TCP_FLAGS: flags,
TCP_WINDOW: window,
TCP_CHECKSUM: checksum,
TCP_POINTER: pointer,
TCP_PAYLOAD_DATA: raw_data[20:]
}
TCP header result: https://pastebin.com/7xhaEGer
Wireshark result for same packets:
Thanks in advance for any help you can provide.

Okay so i managed to solve it. It was a quite stupid error. I forgot to account for the ip-header bits when unpacking tcp.
Fixed code would look something like this:
# Unpack tcp & ip
def ip_tcp(raw_data):
iph = ip_header(raw_data)
iph_length = iph[IP_IHL] * 4
tcp = tcp_fragment(raw_data, iph_length)
return (iph, tcp)
# Unpack tcp fragment
def tcp_fragment(raw_data, iph_length):
tcp_header = struct.unpack('!HHLLBBHHH', raw_data[iph_length:iph_length + 20])
source_port = tcp_header[0]
destionation_port = tcp_header[1]
sequence_number = tcp_header[2]
acknowledgement_number = tcp_header[3]
offset = tcp_header[4] >> 4
reserved = tcp_header[4] & 0xF
flags = get_tcp_flags(tcp_header[5])
window = tcp_header[6]
checksum = tcp_header[7]
pointer = tcp_header[8]
return {
TCP_SOURCE_PORT: source_port,
TCP_DESTINATION_PORT: destionation_port,
TCP_SEQUENCE_NUMBER: sequence_number,
TCP_ACKNOWLEDGEMENT_NUMBER: acknowledgement_number,
TCP_OFFSET: offset,
TCP_RESERVED: reserved,
TCP_FLAGS: flags,
TCP_WINDOW: window,
TCP_CHECKSUM: checksum,
TCP_POINTER: pointer,
TCP_PAYLOAD_DATA: raw_data[iph_length + 20:]
}

Related

"OSError: [WinError 10022] An invalid argument was supplied" when trying to send TCP SYN packet (python)

Currently trying to make handshake process on python using raw sockets but for some reason I can't send any packet with TCP protocol receiving OSError: [WinError 10022] An invalid argument was supplied. Here is my code:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.sendto(packet, ('8.8.8.8', 80))
As packet I have tried to use scapy's TCP packet, TCP bytes from wireshark after successful sendings with other libraries and also hand-made byte strings:
def chksum(packet: bytes) -> int:
if len(packet) % 2 != 0:
packet += b'\0'
res = sum(array.array("H", packet))
res = (res >> 16) + (res & 0xffff)
res += res >> 16
return (~res) & 0xffff
class TCPPacket:
def __init__(self,
src_host: str,
src_port: int,
dst_host: str,
dst_port: int,
flags: int = 0):
self.src_host = src_host
self.src_port = src_port
self.dst_host = dst_host
self.dst_port = dst_port
self.flags = flags
def build(self) -> bytes:
packet = struct.pack(
'!HHIIBBHHH',
self.src_port, # Source Port
self.dst_port, # Destination Port
0, # Sequence Number
0, # Acknoledgement Number
5 << 4, # Data Offset
self.flags, # Flags
8192, # Window
0, # Checksum (initial value)
0 # Urgent pointer
)
pseudo_hdr = struct.pack(
'!4s4sHH',
socket.inet_aton(self.src_host), # Source Address
socket.inet_aton(self.dst_host), # Destination Address
socket.IPPROTO_TCP, # PTCL
len(packet) # TCP Length
)
checksum = chksum(pseudo_hdr + packet)
packet = packet[:16] + struct.pack('H', checksum) + packet[18:]
return packet
So literally no idea why my socket doesn't like any packet
I found out what is wrong. Windows doesn't allow to send TCP packets with raw sockets so this code will never work. Probably it is possible to write the same with scapy or using other libraries but that's not what I need so the only way to make it work is to run on linux. Still not sure if the packet creation is correct but TCP protocol with raw sockets sure works fine on linux.

Python - Sending Packets issuing error - Minecraft Packets

I'm using the following script:
import socket
import struct
username = "username_value"
verification_key = "verification_key"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # boilerplate
s.connect(("example.com", 1234)) # adjust accordingly
# now for the packet
# note that the String type is specified as having a length of 64, we'll pad that
packet = ""
packet += struct.pack("B", 1) # packet type
packet += struct.pack("B", 7) # protocol version
packet += "%-64s" % username # magic!
packet += "%-64s" % verification_key
packet += struct.pack("B", 0) # that unused byte, assuming a NULL byte here
# send what we've crafted
s.send(packet)
and getting a response of:
packet += struct.pack("B", 1) # packet type
TypeError: Can't convert 'bytes' object to str implicitly
I am almost brand-new to Python, and just started, but I understand the language. I read up and found something about Python 3 changing the way you use packets. I feel kind of hopeless. Help? Thank you
In python 3 you have to implicitly define your string packet as a bytes
packet = b""
instead of packet = ""

Sending low level raw tcp packets python

I have been working on a program lately for raw packets. We recently had a lecture about raw packets so I have been trying to learn and do exactly what my professor told me. I have a problem with my program it comes up with an error saying destination address required, its raw so I don't want to do socket.connect(destaddr) even though that will fix the error. Here is my code:
Here is the class and function:
#not real mac address to protect privacy also removed preamble
class packet(object):
b = ""
def __init__(self, payload):
self.payload = payload
def ether(self):
#preamble = "55555555555555D5"
macdest = "123456789101" #my mac address - needed to remove colons
macsource = "123456789101" #router mac address without colons
ethertype = "0800" #removed 0x because it is not needed
fcs = "" #frame check sequence none so far
frame = macdest+macsource+ethertype
return frame
def ip(self): #in hexadecimal
version = "4" #ipv4 hex
ihl = "5" #header length hex
dscp = "00" #default
ecn = "00" #default
length = "36" #ether-24 + ip-20 + tcp-30 = 54 to hexa = 35
idip="0000" #random id
flags = "40" #dont fragment flag is 2 to hex is 4
offset = "00" #space taker
ttl = "40"#hex(64) = 40
protocol = "06" #for tcp
checksum = "0000"
ipaddrfrom = "c0a8010a"
ipaddrto = "c0a80101"
datagram = version+ihl+dscp+ecn+length+idip+flags+offset+ttl+protocol+checksum+ipaddrfrom+ipaddrto
return datagram
def tcp(self):
portsrc = "15c0" #5568
portdest = "0050" #80
syn = "00000000"
ack = "00000000"
nonce = "80"
fin = "10"
windowscale = "813b"
checksum = "0000"
segment = portsrc+portdest+syn+ack+nonce+fin+windowscale + checksum
return segment
def getpacket(self):
frame = self.ether()
datagram = self.ip()
segment = self.tcp()
payload = self.payload
packet = frame+datagram+segment+payload
a = 0
b = ""
for char in packet:
a = a+1
b = b + char
if a == 4:
b = b + " "
a=0
self.fmtpacket = b
return packet
def raw():
s = socket(AF_INET, SOCK_RAW, IPPROTO_IP)
s.bind(('192.168.1.10', 0))
pckt = packet("")
netpacket = pckt.getpacket()
print "Sending: " + pckt.fmtpacket
print ""
s.sendall(netpacket)
data = s.recv(4096)
print data
If your professor is okay with it, you may find Scapy a lot easier to work with in creating raw packets in python.
From their website:
Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. It can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery (it can replace hping, 85% of nmap, arpspoof, arp-sk, arping, tcpdump, tethereal, p0f, etc.)
Is there a reason for binding to '0.0.0.0'? When you create a raw socket, you'll need to bind it to an interface.
One thing I notice is that you'll need the '\x' prefix for hex.
Right now, you're stringing together chars.
For example, in ip(), version + ihl = '45'. That's a string, not a hex value. When you're sending this along, as a raw packet, that's two bytes instead of the one that you want. You want to send '\x45', not '45'.
packet to be sent should contain the actual bytes and not the string.

How to calculate a packet checksum without sending it?

I'm using scapy, and I want to create a packet and calculate its' checksum without sending it. Is there a way to do it?
Thanks.
I've also tried to avoid show2() because it print the packet.
I've found in the source a better solution:
del packet.chksum
packet = packet.__class__(bytes(packet))
This code regenerate the packet with the correct checksum without any print and actually is what show2() run in the background before printing.
You need to delete the .chksum value from the packet after you create it; then call .show2()
>>> from scapy.layers.inet import IP
>>> from scapy.layers.inet import ICMP
>>> from scapy.layers.inet import TCP
>>> target = "10.9.8.7"
>>> ttl = 64
>>> id = 32711
>>> sport = 2927
>>> dport = 80
>>> pak = IP(dst=target, src = "100.99.98.97", ttl=ttl, flags="DF", id=id, len=1200, chksum = 0)/TCP(flags="S", sport=sport, dport=int(dport), options=[('Timestamp',(0,0))], chksum = 0)
>>> del pak[IP].chksum
>>> del pak[TCP].chksum
>>> pak.show2()
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 1200
id = 32711
flags = DF
frag = 0L
ttl = 64
proto = tcp
chksum = 0x9afd
src = 100.99.98.97
dst = 10.9.8.7
\options \
###[ TCP ]###
sport = 2927
dport = www
seq = 0
ack = 0
dataofs = 8L
reserved = 0L
flags = S
window = 8192
chksum = 0x2c0e
urgptr = 0
options = [('Timestamp', (0, 0)), ('EOL', None)]
>>>
Add this patch to scapy/packet.py:
+ def checksum_silent(self):
+ """
+ Internal method that recalcs checksum without the annoying prints
+ **AFTER old checksums are deleted.**
+ """
+
+ for f in self.fields_desc:
+ if isinstance(f, ConditionalField) and not f._evalcond(self):
+ continue
+ fvalue = self.getfieldval(f.name)
+ if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and type(fvalue) is list):
+ fvalue_gen = SetGen(fvalue,_iterpacket=0)
+ for fvalue in fvalue_gen:
+ fvalue.checksum_silent()
+ if self.payload:
+ self.payload.checksum_silent()
Then instead of calling pkt.show2(), just call this function
pkt.checksum_silent(). (Remember to first do del pkt[IP].chksum and del pkt[UDP].chksum, etc.) as shown in the previous answer.
This function should be faster and be silent. (There may be additional things to trim as well; I hacked this code together and only tested to make sure it was silent with correct checksum.)
Indeed, the show2() function calculates the checksum for you, but it also prints the contents of the packet once it is finished with its work. However, show2() has a helpful little parameter named dump. The source describes it as such:
:param dump: determine if it prints or returns the string value
So by setting dump=True, you can avoid the pesky output that the function provides by default, and still get the calculations that you want.
You can also use packet.build() which returns raw bytes with correct checksum. Then convert the bytes to a packet.
>>> import scapy.all as sp
>>> packet = sp.IP(src='127.0.0.1', dst='8.8.8.8')
>>> packet
<IP src=127.0.0.1 dst=8.8.8.8 |>
>>> sp.IP(packet.build())
<IP version=4 ihl=5 tos=0x0 len=20 id=1 flags= frag=0 ttl=64
proto=hopopt chksum=0xebd8 src=127.0.0.1 dst=8.8.8.8 |>

Unable to send ICMP packets from a raw socket in Python

I have a raw Python socket initialized like so:
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
mySocket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
My problem is that I can't send any packet from the socket where the protocol field in my IP header is specified as '1' (ICMP). If this field is anything else it'll work just fine, including '17' (UDP) for example. I don't get any errors, the packet just doesn't display in Wireshark, and I can't receive it on my server.
I'm generating the IP header like so:
def getIPHeader(src, dst, proto):
version = 4
IHL = 5
DSCP = 0
ECN = 0
totalLength = 40
identification = 333
flags = 0
fragmentOffset = 0
timeToLive = 128
protocol = proto
headerChecksum = 0
sourceIP = socket.inet_aton(src)
destIP = socket.inet_aton(dst)
options = 0
version_IHL = (version << 4) | IHL
DSCP_ECN = (DSCP << 2) | ECN
flags_fragmentOffset = (flags << 13) | fragmentOffset
# The '!' ensures all arguments are converted to network byte order
header = struct.pack("!BBHHHBBH4s4s", version_IHL, DSCP_ECN, totalLength, identification, flags_fragmentOffset, timeToLive, protocol, headerChecksum, sourceIP, destIP)
return header
And sending like so:
icmpHeader = struct.pack("!bbHHh", 8, 0, 0, 666, 0)
packet = getIPHeader("192.168.2.15", "8.8.8.8", 1) + icmpHeader
mySocket.sendto(packet, ("8.8.8.8", 0))
Sending with UDP protocol set works (obviously malformed):
icmpHeader = struct.pack("!bbHHh", 8, 0, 0, 666, 0)
packet = getIPHeader("192.168.2.15", "8.8.8.8", 17) + icmpHeader
mySocket.sendto(packet, ("8.8.8.8", 0))
The worst part is if I send a ping from the command line, then copy the exact hex from Wireshark of the IP and ICMP data into my code, it STILL doesn't work, like so:
packet = b'\x45\x00\x00\x3c\x24\xad\x00\x00\x80\x01\x43\x4d\xc0\xa8\x02\x0f\x08\x08\x08\x08\x08\x00\x4d\x44\x00\x01\x00\x17\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x61\x62\x63\x64\x65\x66\x67\x68\x69'
mySocket.sendto(packet, ("8.8.8.8", 0))
However, as before, if I change the 10th byte of the hard coded data to '\x11' (17 decimal, UDP), I can then see it in Wireshark (obviously with a malformed UDP section as the rest of the packet is ICMP).
I'm running Windows 10, Python 3.4.1, and my firewall is off. Any ideas?
UPDATE: I've tried this on two different computers, a Windows 8 and a Windows 10 machine, both worked, so it's something to do with my computer, the plot thickens...

Categories

Resources