How to generate packet having sequence number as `first:last` with Scapy? - python

I am trying to generate pcap file from a tcpdump output, how can I generate those packets having sequence number as first:last?
Here is what my tcpdump input looks like:
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
1509471560.944080 MAC1 > MAC2, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 23237, offset 0, flags [DF], proto TCP (6), length 60)
IP1.port > IP2.port: Flags [S], cksum 0x6d2f (incorrect -> 0x0b4a), seq 1127096708, win 65535, options [mss 1460,sackOK,TS val 817985 ecr 0,nop,wscale 6], length 0
1509471561.042855 MAC2 > MAC1, ethertype IPv4 (0x0800), length 58: (tos 0x0, ttl 64, id 3107, offset 0, flags [none], proto TCP (6), length 44)
IP2.port > IP1.port: Flags [S.], cksum 0x85d8 (correct), seq 449984001, ack 1127096709, win 65535, options [mss 1460], length 0
1509471561.044008 MAC1 > MAC2, ethertype IPv4 (0x0800), length 54: (tos 0x0, ttl 64, id 23238, offset 0, flags [DF], proto TCP (6), length 40)
IP1.port > IP2.port: Flags [.], cksum 0x6d1b (incorrect -> 0x9d95), seq 1, ack 1, win 65535, length 0
1509471561.046607 MAC1 > MAC2, ethertype IPv4 (0x0800), length 191: (tos 0x0, ttl 64, id 23239, offset 0, flags [DF], proto TCP (6), length 177)
IP1.port > IP2.port: Flags [P.], cksum 0x6da4 (incorrect -> 0x98df), seq 1:138, ack 1, win 65535, length 137
1509471914.089046 MAC1 > MAC2, ethertype IPv4 (0x0800), length 82: (tos 0x0, ttl 64, id 54304, offset 0, flags [DF], proto UDP (17), length 68)
Following is the code I have prepared to process the TCP packets:
from scapy.all import *
import secrets
def generatePcapfromText(inputtxt,output):
with open (inputtxt,encoding='cp850') as input:
framenum=0
for line in input:
if line[0].isdigit(): # line one
framenum += 1
frametime=float(line[:16])
srcmac= line[18:34]
dstmac= line[38:54]
ethertype = int(line[line.find('(')+1:line.find(')')], 16)
frameLen=int(line[line.find('length')+7:line.find(': (')])
frameTos=int(line[line.find('tos')+4:line.find(', ttl')],16)
frameTtl=int(line[line.find('ttl')+4:line.find(', id')])
frameId=int(line[line.find('id')+3:line.find(', offset')])
frameOffset=line[line.find('offset')+7:line.find(', flags')]
frameFlags=line[line.find('[')+1:line.find(']')]
protocol = line[line.find('proto')+6:line.rfind('(')-1]
ipLen = int(line[line.rfind('length')+6:line.rfind(')')])
if frameFlags == "none":
frameFlags = ""
ether = Ether(dst=dstmac, src=srcmac, type=ethertype)
elif len(line)>5:
if line[5].isdigit(): # line two
srcinfo = line[4:line.find ( '>' )]
dstinfo = line[line.find ( '>' ) + 2:line.find ( ':' )]
ipsrc = srcinfo[:srcinfo.rfind ( '.' )]
ipdst = dstinfo[:dstinfo.rfind ( '.' )]
srcport = int(srcinfo[srcinfo.rfind ( '.' ) + 1:])
dstport = int(dstinfo[dstinfo.rfind ( '.' ) + 1:])
ip = ether/IP(src=ipsrc, dst=ipdst, len=frameLen, tos=frameTos, ttl=frameTtl, id=frameId, flags=frameFlags, proto=protocol.lower())
if protocol == "TCP":
frameFlag = line[line.find ( '[' ) + 1:line.find ( ']' )]
frameFlag=frameFlag.replace(".","A")
cksum = int(line[line.find ( 'cksum' ) + 6:line.find ( '(' )],16)
if ", ack" in line:
seq_n = line[line.find ( ', seq' ) + 6:line.find ( ', ack' )]
ack_n = int(line[line.find ( 'ack' ) + 4:line.find ( ', win' )])
else:
seq_n = line[line.find ( ', seq' ) + 6:line.find ( ', win' )]
ack_n = 0
if "options" in line:
win = int(line[line.find ( 'win' ) + 4:line.find ( ', options' )])
options= line[line.find ( 'options' ) + 8:line.find ( ', length' )]
else:
win = int(line[line.find ( 'win' ) + 4:line.find ( ', length' )])
options="[]"
pktlen = int(line[line.find ( ', length' ) + 9:])
if ":" in seq_n:
# ???
else:
pkt = ip / TCP(sport=srcport, dport=dstport , flags=frameFlag, seq=int(seq_n), ack=ack_n, chksum=cksum, window=win) / secrets.token_hex(pktlen)
pkt.time = frametime
wrpcap(output, pkt, append=True)
As TCP in Scapy need an integer for the sequence number I cannot pass it first:last as the sequence number, so It needs some modification which I am not familiar with as It is my first time working with Scapy. I have mark where this modification should be done via #??? in the code above.
For my purpose it is important that the packets have the same timestamp as the tcpdump input, so I have set the packet timestamp via pkt.time=timestamp.
PS: You can find history behind this question here.

According to the tcpdump manpage
The notation is first:last which means sequence numbers first up to but not including last
The first:last notation doesn't actually exist within the packet, therefore Scapy won't understand it. Tcpdump gives you this additional information based on the analysis of the TCP stream (and possible regrouping of the packets)
You should also note that your sequence numbers are probably relative, which means they don't really mean anything else than the order.
Using the first part of the number as a sequence number will probably be enough for your needs:
seq = int(seq_n.split(":")[0])

Related

How to set TCP options (Timestamp and SAckOk) via Scapy?

I have following information for each packet I want to generate via Scapy, it is tcpdump output:
1509472682.813373 MAC1 > MAC2, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 64271, offset 0, flags [DF], proto TCP (6), length 60)
IP1.port1 > IP2.port2: Flags [S], cksum 0x4a0b (incorrect -> 0xe5b4), seq 1763588570, win 65535, options [mss 1460,sackOK,TS val 1098453 ecr 0,nop,wscale 6], length 0
I have generated TCP packets as follow, but when I check them via wireshark it seems that the Timestamp option is not set at all and Sack is not set as I have expected.
for r in (("mss","MSS"), ("sackOK","SAck"), ("nop","NOP"), ("TS ", "Timestamps "), ("val", "TSval"), ("ecr", "TSecr"), ("wscale","WScale")):
opt = opt.replace(*r)
opt=opt.split(",")
for op in opt:
op = op.split()
if len(op) == 2:
options.append((op[0],int(op[1])))
elif op[0] == "Timestamps": ## Need some modification, so that Scapy do not ignore it.
options.append((op[0],(int(op[2]),int(op[4]))))
elif op[0] == "SAck": ## How to set SAck option to be SAck Permitted?
options.append((op[0], ''))
else: # NOP
options.append((op[0], ()))
ip = ether/IP(src=ipsrc, dst=ipdst, len=ipLen, tos=frameTos, ttl=frameTtl, offset=frameOffset, id=frameId, flags=frameFlags, proto=protocol.lower())
if ack_n is None:
pkt = ip / TCP(sport=srcport, dport=dstport , flags=frameFlag, seq=int(seq_n), chksum=cksum, window=win, options=options) / secrets.token_bytes(frameLen-54)
else:
pkt = ip / TCP(sport=srcport, dport=dstport , flags=frameFlag, seq=int(seq_n), ack=ack_n, chksum=cksum, window=win, options=options) / secrets.token_bytes(frameLen-54)
pkt.time = frametime
wrpcap(output, pkt, append=True)
Here is what is passed to options field for the packet I have provided its info at the beginning:
[('MSS', 1460), ('SAck', ''), ('Timestamps', (1098453, 0)), ('NOP', ()), ('WScale', 6)]
But when I check the packet via Wireshark the Timestamps option is not set, it seems that Scapy has ignored it, and the SAck option is not set as I have expected.
Here is how this packet options field looks like in Wireshark:
Here is what I have expected it to be:
So the question here are:
How to set timestamps, so that the Scapy does not ignore it?
How to set SAck, so that it is marked as permitted.
Edit 1:
I have solved the problem with SAck, I should pass it as ('SAckOK', '')
Finally I have find what I have set wrong:
As I mentioned in my first edit, to set Selective Acknowledgment Permitted, I should pass option a tuple as ('SAckOK', '').
To set timestamp I should pass option a tuple as ('Timestamp', (1098453, 0)) in the inner tuple the first argument is Val and the second one is Ecr.

How to parse DNS Question field with python raw sockets?

I'm trying to parse question field in a DNS packet where I can read domain and DNS response from a DNS server. I can extract a DNS header, but I'm having trouble to parse the question field because the size of the data is unknown.
I follow this example, but the part of extracting the question field is not working.
What I need is someone to show me the way to do it properly.
I have this code where everything is right...
This is my code:
#!/usr/bin/env python3
from socket import *
import struct
import binascii
def ethernet_frame(raw_data):
mac_dest, mac_src, protocol = struct.unpack('! 6s 6s H',
raw_data[:14])
return byte_to_hex_mac(mac_dest), byte_to_hex_mac(mac_src),
htons(protocol), raw_data[14:]
def byte_to_hex_mac(mac_bytes):
addr = binascii.hexlify(mac_bytes).decode("ascii")
return ":".join([addr[i:i+2] for i in range(0,12,2)])
def data_packet_udp(data):
tuple_data_udp = struct.unpack('! H H H H', data[:8])
port_src = tuple_data_udp[0]
port_dest = tuple_data_udp[1]
udp_len = tuple_data_udp[2]
udp_checksum = tuple_data_udp[3]
return port_src, port_dest, udp_len, udp_checksum, data[8:]
def data_packet_ipv4(data):
tuple_data_ipv4 = struct.unpack("!BBHHHBBH4s4s", data[:20])
version = tuple_data_ipv4[0]
header_len = version >> 4
type_service = tuple_data_ipv4[1]
length_total = tuple_data_ipv4[2]
identification = tuple_data_ipv4[3]
offset_fragment = tuple_data_ipv4[4]
ttl = tuple_data_ipv4[5]
protocols = tuple_data_ipv4[6]
checksum_header = tuple_data_ipv4[7]
ip_src = inet_ntoa(tuple_data_ipv4[8])
ip_dest = inet_ntoa(tuple_data_ipv4[9])
length_header_bytes = (version & 15) * 4
return version, header_len, type_service, + \
length_total, identification, offset_fragment, + \
ttl, protocols, checksum_header, ip_src, ip_dest,
data[length_header_bytes:]
def data_packet_dns(data):
tuple_data_dns = struct.unpack('!HHHHHH', data[:12])
identification = tuple_data_dns[0]
flags = tuple_data_dns[1]
number_queries = tuple_data_dns[2]
number_response = tuple_data_dns[3]
number_authority = tuple_data_dns[4]
number_additional = tuple_data_dns[5]
qr = (flags & 32768) != 0
opcode = (flags & 30720 ) >> 11
aa = (flags & 1024) != 0
tc = (flags & 512) != 0
rd = (flags & 256) != 0
ra = (flags & 128) != 0
z = (flags & 112) >> 4
rcode = flags & 15
return identification, flags, number_queries, number_response, + \
number_authority, number_additional, qr, opcode, aa, tc, + \
rd, ra, z, rcode
sock = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003))
while True:
raw_dados, addr = sock.recvfrom(65536)
mac_dest, mac_src, protocol, payload = ethernet_frame(raw_dados)
if protocol == 8:
( version, header_len, type_service,
length_total, identification, offset_fragment,
ttl, protocols, checksum_header,
ip_src, ip_dest, data ) = data_packet_ipv4(payload)
if protocols == 17:
port_src, port_dest, udp_len, udp_checksum, data =
data_packet_udp(data)
print("--------- HEADER UDP ----------")
print("Port Source : {}".format(port_src))
print("Port Dest : {}".format(port_dest))
print("UDP Length : {}".format(udp_len))
print("UDP Checksum : {}\n".format(udp_checksum))
if port_src == 53 or port_dest == 53:
(identification, flags, number_queries, \
number_response,number_authority,number_additional, \
qr, opcode, aa, tc, rd, ra, z, rcode) = data_packet_dns(data)
print("\t--------- HEADER DNS ----------")
print("\tidentification : {}".format(identification))
print("\tFlags : {}".format(flags))
print("\tnumber_queries : {}".format(number_queries))
print("\tnumber_response : {}".format(number_response))
print("\tnumber_authority : {}".format(number_authority))
print("\tnumber_additional : {}".format(number_additional))
print("\tQr : {}".format(qr))
print("\tOpcode : {}".format(opcode))
print("\tAA : {}".format(aa))
print("\tTC : {}".format(tc))
print("\tRD : {}".format(rd))
print("\tRA : {}".format(ra))
print("\tZ : {}".format(z))
print("\tRCODE : {}".format(rcode))

How to use raw socket in Python

i try to send a selfmade tcp-packet via raw socket in python3 (with windows 10). Inside the ip-header i want to set protocol to TCP (=6).
The paket i send is:
E\x00\x00\x00\xd41\x00\x00\xff\x06\x00\x00\xc0\xa8\xb29\xc0\xa8\xb2\x01
but I receive (and wireshark detects the same)
E\x00\x00FN\xe7\x00\x00\x80\xff\x00\x00\n\xac\x02e\n\xac
so now it is 255 (uknown)
srcport = 11001
def sendTCP(dest_ip, dest_port,fin, syn, rst, psh, ack, urg):
payload = b'[TESTING]\n'
ip = make_ip(socket.IPPROTO_TCP, source_ip, dest_ip)
tcp = make_tcp(srcport, dest_port, payload, 123, 0, fin, syn, rst, psh, ack, urg)
packet = ip + tcp + payload
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.sendto(packet, (dest_ip, 0))
ans = s.recv(1024)
print(ans)
def make_ip(proto, srcip, dstip, ident=54321):
saddr = socket.inet_aton(srcip)
daddr = socket.inet_aton(dstip)
ihl_ver = (4 << 4) | 5
return struct.pack('!BBHHHBBH4s4s' ,
ihl_ver, 0, 0, ident, 0, 255, proto, 0, saddr, daddr)
def make_tcp(srcport, dstport, payload, seq=123, ackseq=0,
fin=False, syn=False, rst=False, psh=False, ack=False, urg=False,
window=5840):
offset_res = (5 << 4) | 0
flags = (fin | (syn << 1) | (rst << 2) |
(psh <<3) | (ack << 4) | (urg << 5))
return struct.pack('!HHLLBBHHH',
srcport, dstport, seq, ackseq, offset_res,
flags, window, 0, 0)
if __name__ == '__main__':
sendTCP('10.172.2.101',80, False, True, False, False, False, False)

Fragmented TCP message in Python

I have an application which read live SIP Packets and decode information in real time.
when packet is small UDP/TCP is able to get the information, but when packet is large, it arrives in different segments:
The following is an extract from Wireshark:
3 Reassembled TCP Segments (3331 bytes): #1(1448), #3(1448), #5(435)
Frame: 1, payload: 0-1447 (1448 bytes)
Frame: 3, payload: 1448-2895 (1448 bytes)
Frame: 5, payload: 2896-3330 (435 bytes)
Segment count: 3
Reassembled TCP length: 3331
My application believes there is a new SIP Packet for each fragment and fails to decode info.
How can I do this? I need to read the packet, assemble all sip message if fragmented and pass the info to my control module. This is my current code:
s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
while (True):
packet = s.recvfrom(65565)
#packet string from tuple
packet = packet[0]
#parse ethernet header
eth_length = 14
eth_header = packet[:eth_length]
eth = unpack('!6s6sH' , eth_header)
eth_protocol = socket.ntohs(eth[2])
if eth_protocol == 8 :
#Parse IP header
#take first 20 characters for the ip header
ip_header = packet[eth_length:20+eth_length]
#now unpack them :)
iph = unpack('!BBHHHBBH4s4s' , ip_header)
version_ihl = iph[0]
version = version_ihl >> 4
ihl = version_ihl & 0xF
iph_length = ihl * 4
ttl = iph[5]
protocol = iph[6]
s_addr = socket.inet_ntoa(iph[8]);
d_addr = socket.inet_ntoa(iph[9]);
#TCP protocol
if protocol == 6 :
t = iph_length + eth_length
tcp_header = packet[t:t+20]
#now unpack them :)
tcph = unpack('!HHLLBBHHH' , tcp_header)
source_port = tcph[0]
dest_port = tcph[1]
sequence = tcph[2]
acknowledgement = tcph[3]
doff_reserved = tcph[4]
tcph_length = doff_reserved >> 4
if dest_port == sipLocatorConfig.SIP_PORT:
print
logging.info("------------------------------------------------------SIP Packet detected------------------------------------------------------")
h_size = eth_length + iph_length + tcph_length * 4
data_size = len(packet) - h_size
#get data from the packet
data = packet[h_size:]
ipInfo = {}
ipInfo['protocol'] = protocol
ipInfo['s_addr'] = str(s_addr)
ipInfo['source_port'] = source_port
ipInfo['d_addr'] = str(d_addr)
ipInfo['dest_port'] = dest_port
processSipPacket(data,ipInfo)
I believe this is what I wrote bufsock for:
http://stromberg.dnsalias.org/~strombrg/bufsock.html
It allows you to say "give me all the data until the next null" or "give me the next 64 bytes" and similar things. It deals intelligently with fragmented and aggregated packets.
Unlike many such tools, it does not require that you have bufsock at both the producer and the consumer - you can use it fine on one end and not the other. It is a little bit like stdio for sockets, in python.
It works on CPython 2.x, CPython 3.x, Pypy, Pypy3 (which is still beta at this time) and Jython.

PySNMP can not recognize response

i am using the following simple script:
from pysnmp.entity.rfc3413.oneliner import cmdgen
errorIndication, errorStatus, errorIndex, \
varBindTable = cmdgen.CommandGenerator().bulkCmd(
cmdgen.CommunityData('test-agent', 'public'),
cmdgen.UdpTransportTarget(('IP.IP.IP.IP', 161)),
0,
1,
(1,3,6,1,2,1,4,24,4,1,2,169,254)
)
if errorIndication:
print errorIndication
else:
if errorStatus:
print '%s at %s\n' % (
errorStatus.prettyPrint(),
errorIndex and varBindTable[-1][int(errorIndex)-1] or '?'
)
else:
for varBindTableRow in varBindTable:
for name, val in varBindTableRow:
print '%s = %s' % (name.prettyPrint(), val.prettyPrint())
Using snmpwalk from command line to this device returns expected result. But
script returns No SNMP response received before timeout. If i omit this OID then everything works fine.
So the problem is in this OID
Here tcpdump stats:
/usr/sbin/tcpdump -nn -vv -s0 -A host HOST and udp
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
12:15:31.494920 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto: UDP (17), length: 77) IP.IP.IP.IP.47911 > IP.IP.IP.IP.161: [bad udp cksum 4b7d!] { SNMPv2c { GetBulk(34) R=8993731 N=0 M=1 .1.3.6.1.2.1.4.24.4.1.2.169.254 } }
E..M..#.#.I..]<..]</.'...9.S0/.....public."....;.......0.0...+..........).~..
12:15:31.495666 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto: UDP (17), length: 98) IP.IP.IP.IP.161 > IP.IP.IP.IP.47911: [udp sum ok] { SNMPv2c { GetResponse(55) R=8993731 .1.3.6.1.2.1.4.24.4.1.2.169.254.0.0.0.0.255.255.0.0.0.0.0=[inetaddr len!=4]0.0.255.255.0.0.0.0 } }
E..b..#.#.I..]</.]<....'.N.\0D.....public.7....;.......0)0'..+..........).~.............#.........
12:15:32.500226 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto: UDP (17), length: 77) IP.IP.IP.IP.47911 > IP.IP.IP.IP.161: [bad udp cksum 4b7d!] { SNMPv2c { GetBulk(34) R=8993731 N=0 M=1 .1.3.6.1.2.1.4.24.4.1.2.169.254 } }
E..M..#.#.I..]<..]</.'...9.S0/.....public."....;.......0.0...+..........).~..
12:15:32.500624 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto: UDP (17), length: 98) IP.IP.IP.IP.161 > IP.IP.IP.IP.47911: [udp sum ok] { SNMPv2c { GetResponse(55) R=8993731 .1.3.6.1.2.1.4.24.4.1.2.169.254.0.0.0.0.255.255.0.0.0.0.0=[inetaddr len!=4]0.0.255.255.0.0.0.0 } }
E..b..#.#.I..]</.]<....'.N.\0D.....public.7....;.......0)0'..+..........).~.............#.........
As we can see, device returns response .1.3.6.1.2.1.4.24.4.1.2.169.254.0.0.0.0.255.255.0.0.0.0.0=[inetaddr len!=4]0.0.255.255.0.0.0.0, but nothing happens and pysnmp just continue to try the value of this OID again and again.. snmpwalk recognizes this response as IP ADDRESS 0.0.255.255
Can you guys help me? Thanks in advance and sorry my english.
Your SNMP Agent seems to produce broken SNMP messages. While IPv4 address is four-octets long, your Agent reports eight-octets value.
As per SNMP RFCs, pysnmp drops malformed SNMP messages and retries original request a few times in hope to get correct response.
To make pysnmp working with specifically malformed IP address values you could patch its IpAddress class at runtime to make it taking just the four leading octets from a possibly longer initializer:
>>> def ipAddressPrettyIn(self, value):
... return origIpAddressPrettyIn(self, value[:4])
...
>>> origIpAddressPrettyIn = v2c.IpAddress.prettyIn
>>> v2c.IpAddress.prettyIn = ipAddressPrettyIn
>>>
>>> msg, rest = decoder.decode(wholeMsg, asn1Spec=v2c.Message())
>>> print msg.prettyPrint()
Message:
version='version-2'
community=public
data=PDUs:
response=ResponsePDU:
request-id=6564368
error-status='noError'
error-index=0
variable-bindings=VarBindList:
VarBind:
name=1.3.6.1.2.1.4.24.4.1.2.169.254.0.0.0.0.255.255.0.0.0.0.0
=_BindValue:
value=ObjectSyntax:
application-wide=ApplicationSyntax:
ipAddress-value=0.0.255.255

Categories

Resources