How to calculate a packet checksum without sending it? - python

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 |>

Related

Unpacking TCP-fragment give incorrect result

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:]
}

get the udp packets payload readable in python

I am developing a python sniffer which sniff the udp packets. I use this code to receive and get the payload of the received packet in human readable format, but the printed payload is not human readable! I searched about converting this format to human readable, but I got nothing.
packet = recv_socket.recvfrom(65565)
packet = packet[0]
ip_header = packet[0:20]
iph = struct.unpack('!BBHHHBBH4s4s', ip_header)
data = packet[28:]
payload = ":".join("{:02x}".format(ord(c)) for c in data)
print payload.decode("utf-8", "ignore")
print payload
Right now, the out put of print commands are as follow:
.*###[ DNS ]###
id = 1382
qr = 0L
opcode = 12L
aa = 0L
tc = 0L
rd = 1L
ra = 0L
z = 1L
ad = 1L
cd = 0L
rcode = 12L
qdcount = 30062
ancount = 867
nscount = 28525
arcount = 0
qd = ''
an = ''
ns = ''
ar = None
###[ Raw ]###
load = '\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x00<\x00\x04\x80w\xf3\x86'
05:66:61:6c:75:6e:03:63:6f:6d:00:00:01:00:01:c0:0c:00:01:00:01:00:00:00:3c:00:04:80:77:f3:86

FCfield Attribut error while sniffing packets using Scapy

I tried to sniff DNS request packets on mon0 interface, using scapy.
I wanted to send back a spoofed IP.
But I get an error:
AttributeError: 'Ether' object has no attribute 'FCfield'
Code:
def send_response(x):
x.show()
req_domain = x[DNS].qd.qname
logger.info('Found request for ' + req_domain)
# First, we delete the existing lengths and checksums..
# We will let Scapy re-create them
del(x[UDP].len)
del(x[UDP].chksum)
del(x[IP].len)
del(x[IP].chksum)
response = x.copy()
response.FCfield = '2L'
response.addr1, response.addr2 = x.addr2, x.addr1
# Switch the IP addresses
response.src, response.dst = x.dst, x.src
# Switch the ports
response.sport, response.dport = x.dport, x.sport
# Set the DNS flags
response[DNS].qr = '1L'
response[DNS].ra = '1L'
response[DNS].ancount = 1
response[DNS].an = DNSRR(
rrname = req_domain,
type = 'A',
rclass = 'IN',
ttl = 900,
rdata = spoofed_ip
)
#inject the response
sendp(response)
logger.info('Sent response: ' + req_domain + ' -> ' + spoofed_ip + '\n')
def main():
logger.info('Starting to intercept [CTRL+C to stop]')
sniff(prn=lambda x: send_response(x), lfilter=lambda x:x.haslayer(UDP) and x.dport == 53)
Your interface is probably not configured in monitor mode, that's why you get an ethernet (Ether) layer, and not a WiFi (Dot11) layer.

Scapy fails to filter certain packets

I have got a simple sniff function set up with scapy which forwards the packet to a handshake function (I have a webserver set up on port 102. However some weird errors have come by, then I decided to print pkt.show(), what I discovered was that some packages DID come through the filter somehow.
My sniff function:
a=sniff(filter="port 102", count=10, prn=handshake)
This packet manages to come through:
###[ Ethernet ]###
dst = 84:8f:69:f5:fe:ac
src = b8:27:eb:92:a3:3b
type = 0x800
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 44
id = 1
flags =
frag = 0L
ttl = 64
proto = tcp
chksum = 0xe6c6
src = 192.168.137.178
dst = 192.168.137.1
\options \
###[ TCP ]###
sport = iso_tsap
dport = 2426
seq = 605952828
ack = 605952829
dataofs = 6L
reserved = 0L
flags = SA
window = 8192
chksum = 0x5b7c
urgptr = 0
options = [('MSS', 1460)]
As you can see the destination port is 2426, which is definetely not port 102.
Have I done something dumb?
The source port in the enclosed packet is iso_tsap which is 102. If you want to filter by the destination port try the filter "dst port 102". If you need something a bit more sophisticated, here is the syntax of BPF, which is used by scapy.

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.

Categories

Resources