I'm trying to parse a pcap file with scapy (in python), and getting raw data at the layer above TCP.
on wireshark, all the layers are shown correctly:
but on scapy all i'm seeing is just a Raw layer...
i'm thinking maybe it didn't parsed the packet well?
maybe the NetBIOS moduled did not load? or maybe i didn't import the module right?
(i tryied:
import scapy.all, import scapy, import scapy.layers.smb )
how do i make scapy load the layers of the packet correctly?
thanks!
If someone has a similar problem…
You need something like
packet[TCP].decode_payload_as(NBTSession)
And then you Will get the decoded layers by scapy:
packet[TCP].show()
[ TCP ]
sport = microsoft_ds
…
options = []
[ NBT Session Packet ]###
TYPE = Session Message
RESERVED = 0
LENGTH = 4873
[ SMBNegociate Protocol Request Header ]###
Start = '\xfeSMB'
Command = 64
Error_Class= 0
Reserved = 1
Error_code= 0
Flags = 0
Flags2 = 0
PIDHigh = 5
Signature = 0
Unused = 0
TID = 183
PID = 0
UID = 0
MID = 0
WordCount = 0
ByteCount = 0
[ SMB Negotiate Protocol Request Tail ]###
BufferFormat= 0
BufferData= '\x03'
Also you can try after that to decode the packet with the different clases:
packet.decode_payload_as(SMBNegociate Protocol Request Header)
Related
I am trying to set up a socket server on a linux machine using python , as a side project for my networking team.
the machine will listen to port 53, and will respond to dns queries using scapy.
Now, the asking part is all working great, atleast i think, i didnt really get to the part when i set the server as my dns server and try to use the internet yet, but when i try tcpdump it looks like a normal packet.
Anyway, when i respond from the server to the client, the packet is being sent with all the headers that i need, of DNS,DNSQR and DNSRR, as i saw in tcpdump.
But, when the client receive the packet, it only has IP,and UDP headers, as the rest is being sent as Raw data.
As i previously mentioned, i tried looking at wireshark and tcpdump and saw the difference that is being sent between the server and the client, and i saw that from the server side, the reply packet is being marked as malformed packet
Im not completely sure if this is the case, as i saw on the internet that its just because i didnt spoof it complettly.
code:
Client packet:
pkt = IP(dst='<Server IP>')/UDP(dport=53,sport=RandShort())/DNS(rd=1,qd=DNSQR(qname='dns.google'))
Server packet:
def build_dns_response(src_addr,og_sport,dns_qtype,dns_qname,response_type=True,response_name=None):
'''
a function that will build a DNS response packet back to the sender, according to the parameters that are recieved.
dns_qname = url/ip that the sender asked for.
og_sport = original source port, will be used as the dport of this packet
src_addr = source address of the original packet, will be used as the destination of this packet.
dns_qtype = the question type of the original request, 1 stands for type A, and 12 for PTR
response_type = will indicate if the server responds with an answer of 'ok' or 'name-error' on the 'rcode' field,True for ok,False for name-error
response_name = the response that the server will send as a result of the dns request, in case that a request matches to a database query.
'''
if response_type is True:
pkt = IP(src='<Server IP>',dst=src_addr)/\
UDP(sport=53,dport=og_sport)/\
DNS(opcode=0,rd=1,rcode='ok',nscount=1,ancount=1,qd=DNSQR(qname=dns_qname,qtype=dns_qtype),an=DNSRR(rrname=dns_qname,type=dns_qtype,rdata=response_name))
packet being sent by server:
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = None
src = <Server IP>
dst = <client IP >
\options \
###[ UDP ]###
sport = domain
dport = 25151
len = None
chksum = None
###[ DNS ]###
id = 0
qr = 0
opcode = QUERY
aa = 0
tc = 0
rd = 1
ra = 0
z = 0
ad = 0
cd = 0
rcode = ok
qdcount = 1
ancount = 1
nscount = 1
arcount = 0
\qd \
|###[ DNS Question Record ]###
| qname = 'dns.google.'
| qtype = A
| qclass = IN
\an \
|###[ DNS Resource Record ]###
| rrname = 'dns.google.'
| type = A
| rclass = IN
| ttl = 0
| rdlen = None
| rdata = 8.8.8.8
ns = None
ar = None
packet that is received by the client:
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 82
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = 0x6686
src = <Server IP>
dst = <Client IP>
\options \
###[ UDP ]###
sport = domain
dport = 25151
len = 62
chksum = 0xf99
###[ Raw ]###
load = '\x00\x00\x01\x00\x00\x01\x00\x01\x00\x01\x00\x00\x03dns\x06google\x00\x00\x01\x00\x01\x03dns\x06google\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x08\x08\x08\x08'
I guess its worth mentioning that the entire conversation is from Host PC to vm being hosted on it.
Thanks in advance for any help!
i created a new layer using scapy and i succeeded to send the new layer to another computer and show the new layer. My problen is when i send the layers all the data from all fields appear in the first fields and deleted from the another fields.
import sys
i, o, e = sys.stdin, sys.stdout, sys.stderr
from scapy.all import *
sys.stdin, sys.stdout, sys.stderr = i, o, e
from scapy.packet import *
from scapy.fields import *
from scapy.layers.inet import UDP, IP
from scapy.layers.dns import DNS
from scapy.layers.l2 import Ether
class ID(Packet):
name = "ID secret Traffic"
fields_desc = [StrField("ID",""),StrField("LastName",""),StrField("FirstName",""),StrField("FatherName",""),
StrField("MomName",""),StrField("BdayLo",""),StrField("BdayHi",""),StrField("BirthLocation",""),
StrField("Gender",""),StrField("LocationOfIssuingID",""),StrField("Image","")]
bind_layers(UDP,ID,sport=217,dport=217)
bind_layers(ID,Raw,sport=217,dport=217)
bind_layers(UDP,Raw,sport=217,dport=217)
p = IP(dst='192.168.1.28')/UDP()/ID(ID='111111111',LastName="Kazo Cohen",FirstName="Omer",FatherName="Yaqqov",MomName="Irit",BdayLo="6121999",BdayHi="Kislev",BirthLocation="RamatGan",Gender="M",LocationOfIssuingID=" Tel Aviv",Image="fvvsgsgsfzfszvsfvfs")/Raw("KEEPALIVE")
p.show2()
send(p)
OUTPUT
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 120
id = 1
flags =
frag = 0L
ttl = 64
proto = udp
chksum = 0xf6e9
src = 192.168.1.30
dst = 192.168.1.28
\options \
###[ UDP ]###
sport = 217
dport = 217
len = 100
chksum = 0xf332
###[ ID secret Traffic ]###
ID = '111111111Kazo CohenOmerYaqqovIrit6121999KislevRamatGanM Tel AvivfvvsgsgsfzfszvsfvfsKEEPALIVE'
LastName = ''
FirstName = ''
FatherName= ''
MomName = ''
BdayLo = ''
BdayHi = ''
BirthLocation= ''
Gender = ''
LocationOfIssuingID= ''
Image = ''
Your protocol is weird: how is the program (Scapy, or another program able to "talk" your protocol) supposed to guess that the ID field only contains "111111111", and that the rest is to be used for the next fields?
Usually, you have either a known "end-of-field" character (a null-byte can be an example), or a fixed length (it is known that the ID field is 9 characters long), or a field is used to get the length of the string.
Scapy can work with any of these options, using StrNullField or StrStopField for the first case, StrFixedLenField for the second, and StrLenField together with FieldLenField for the third.
I would like to get the ntp_monlist response of my NTP server.
Actually the packet is send but I don't receive anything.
Someone can tell me why ?
Code :
#!/usr/bin/env python
from scapy.all import *
import threading
import os
import sys
import socket
#Data to send
ntpip = "xxx.xx.xxx.xx"
packet = IP(dst=ntpip)/UDP(dport=123)/Raw(load=str("\x17\x00\x03\x2a")+ str("\x00")*4)
packet.show()
rep,non_rep = srp(packet)
rep.show()
Reponse :
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = None
src = xxx.xxx.xxx.xxx
dst = xxx.xxx.xxx.xxx
\options \
###[ UDP ]###
sport = domain
dport = ntp
len = None
chksum = None
###[ Raw ]###
load = '\x17\x00\x03*\x00\x00\x00\x00'
Begin emission:
Finished to send 1 packets.
......................................................................................................................
Received XXX packets, got 0 answers, remaining 1 packets
As you can see, I never receive any response.
You have multiple issues:
you're sending a layer 3 packet, so you need to use sr, not srp;
you need to specify a UDP source port;
your NTP payload is malformed.
There are two issues with your NTP payload. First of all, the first word of the NTP packet is defined as follows (RFC 5905):
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN |Mode | Stratum | Poll | Precision |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
You want LI to be 0, VN to be 3, and Mode to be 3, so this gives a first octet of 0 + (3 * 8) + 3, or 0x1b.
Second, the minimum NTP packet is 12*4 octets.
So you need to say:
packet = IP(dst=ntpip)/UDP(dport=123,sport=50000)/("\x1b\x00\x00\x00"+"\x00"*11*4)
rep,non_rep = sr(packet)
I am trying to measure the responses back from DNS servers. Making a sniffer for a typical DNS response that is less than 512 bytes is no big deal. My issue is receiving large 3000+ byte responses - in some cases 5000+ bytes. I haven't been able to get a socket working that can receive that data reliably. Is there a way with Python sockets to receive from a specific source address?
Here is what I have so far:
import socket
import struct
def craft_dns(Qdns):
iden = struct.pack('!H', randint(0, 65535))
QR_thru_RD = chr(int('00000001', 2)) # '\x01'
RA_thru_RCode = chr(int('00100000', 2)) # '\x00'
Qcount = '\x00\x01' # question count is 1
ANcount = '\x00\x00'
NScount = '\x00\x00'
ARcount = '\x00\x01' # additional resource count is 1
pad = '\x00' #
Rtype_ANY = '\x00\xff' # Request ANY record
PROtype = '\x00\x01' # Protocol IN || '\x00\xff' # Protocol ANY
DNSsec_do = chr(int('10000000', 2)) # flips DNSsec bit to enable
edns0 = '\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00' # DNSsec disabled
domain = Qdns.split('.')
quest = ''
for x in domain:
quest += struct.pack('!B', len(x)) + x
packet = (iden+QR_thru_RD+RA_thru_RCode+Qcount+ANcount+NScount+ARcount+
quest+pad+Rtype_ANY+PROtype+edns0) # remove pad if asking <root>
return packet
def craft_ip(target, resolv):
ip_ver_len = int('01000101', 2) # IPvers: 4, 0100 | IP_hdr len: 5, 0101 = 69
ipvers = 4
ip_tos = 0
ip_len = 0 # socket will put in the right length
iden = randint(0, 65535)
ip_frag = 0 # off
ttl = 255
ip_proto = socket.IPPROTO_UDP # dns, brah
chksm = 0 # socket will do the checksum
s_addr = socket.inet_aton(target)
d_addr = socket.inet_aton(resolv)
ip_hdr = struct.pack('!BBHHHBBH4s4s', ip_ver_len, ip_tos, ip_len, iden,
ip_frag, ttl, ip_proto, chksm, s_addr, d_addr)
return ip_hdr
def craft_udp(sport, dest_port, packet):
#sport = randint(0, 65535) # not recommended to do a random port generation
udp_len = 8 + len(packet) # calculate length of UDP frame in bytes.
chksm = 0 # socket fills in
udp_hdr = struct.pack('!HHHH', sport, dest_port, udp_len, chksm)
return udp_hdr
def get_len(resolv, domain):
target = "10.0.0.3"
d_port = 53
s_port = 5353
ip_hdr = craft_ip(target, resolv)
dns_payload = craft_dns(domain) # '\x00' for root
udp_hdr = craft_udp(s_port, d_port, dns_payload)
packet = ip_hdr + udp_hdr + dns_payload
buf = bytearray("-" * 60000)
recvSock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0800))
recvSock.settimeout(1)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sendSock.settimeout(1)
sendSock.connect((resolv, d_port))
sendSock.send(packet)
msglen = 0
while True:
try:
pkt = recvSock.recvfrom(65535)
msglen += len(pkt[0])
print repr(pkt[0])
except socket.timeout as e:
break
sendSock.close()
recvSock.close()
return msglen
result = get_len('75.75.75.75', 'isc.org')
print result
For some reason doing
pkt = sendSock.recvfrom(65535)
Recieves nothing at all. Since I'm using SOCK_RAW the above code is less than ideal, but it works - sort of. If the socket is extremely noisy (like on a WLAN), I could end up receiving well beyond the DNS packets, because I have no way to know when to stop receiving packets when receiving a multipacket DNS answer. For a quiet network, like a lab VM, it works.
Is there a better way to use a receiving socket in this case?
Obviously from the code, I'm not that strong with Python sockets.
I have to send with SOCK_RAW because I am constructing the packet in a raw format. If I use SOCK_DGRAM the custom packet will be malformed when sending to a DNS resolver.
The only way I could see is to use the raw sockets receiver (recvSock.recv or recvfrom) and unpack each packet, look if the source and dest address match within what is supplied in get_len(), then look to see if the fragment bit is flipped. Then record the byte length of each packet with len(). I'd rather not do that. It just seems there is a better way.
Ok I was stupid and didn't look at the protocol for the receiving socket. Socket gets kind of flaky when you try to receive packets on a IPPROTO_RAW protocol, so we do need two sockets. By changing to IPPROTO_UDP and then binding it, the socket was able to follow the complete DNS response over multiple requests. I got rid of the try/catch and the while loop, as it was no longer necessary and I'm able to pull the response length with this block:
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
recvSock.settimeout(.3)
recvSock.bind((target, s_port))
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
#sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendSock.settimeout(.3)
sendSock.bind((target, s_port))
sendSock.connect((resolv, d_port))
sendSock.send(packet)
pkt = recvSock.recvfrom(65535)
msglen = len(pkt[0])
Now the method will return the exact bytes received from a DNS query. I'll leave this up in case anyone else needs to do something similar :)
I'd need build up an UDP packet with Python setting a particular value of its TTL. Could anyone show me the minimal-length code to do this?
Using PyIP.
Not tested, but shows the idea:
import ip
import udp
import socket
# build UDP
udp_packet = udp.Packet()
udp_packet.sport = 1024;
udp_packet.dport = 3024;
udp_packet.data = "\xFF\xFF\xFF\xFFrcon \"test\" test\0"
udp_data = udp.assemble(udp_packet, 0)
# build IP packet
ip_packet = ip.Packet()
ip_packet.src = "1.1.1.1"
ip_packet.dst = "2.2.2.2"
ip_packet.ttl = 10
ip_packet.data = udp_data
packet = ip.assemble(ip_packet, 0)
# send the packet here