I am using Scapy sniff function to track incoming traffic on local interface. I would like to isolate and print just specific packets. In order to do so, I have to match values in specific fields of TCP/UDP/IP headers. Is there detailed document that explains usage of Scapy sniff filters? How can I set filter to refer to some packet filed value?
For example, I would need filter that shows just and only SYN+ACK packets. For some reason this is not working as expected:
sniff(iface="Intel(R) Ethernet Connection (4) I219-LM",
filter="ip src x.x.x.x and tcp-syn !=0 and tcp-rst !=1",
prn=lambda x: x.summary)
The filter is written in standard BPF syntax, as documented here https://www.wireshark.org/docs/man-pages/pcap-filter.html
For your use case (only SYN-ACK packets), I think it would be something like this:
filter = "host x.x.x.x and (tcp[tcpflags] & (tcp-syn|tcp-ack)) == (tcp-syn|tcp-ack)"
sniff(iface="Intel(R) Ethernet Connection (4) I219-LM",
filter=filter, prn=lambda x: x.summary)
Related
I have achieved sniffing IP packets. I use the following code.
def print_summary(pkt):
print("SRC:", pkt[scapy.IP].src, "\t\tDEST:", pkt[scapy.IP].dst)
scapy.sniff( iface=interface, filter="ip", prn=print_summary)
If I try to ping a domain name say 'youtube.com' or open it using a web browser, how can I show the domain name using the above print_summary function. I have tried searching for what filters to use for dns but found nothing.
Okay, I get it now. What I did was to remove the filter argument altogether from the sniff() method.
scapy.sniff( iface=interface, prn=print_summary)
Then in the print_summary() function, I filtered according to the layer each packet had (IP or DNS).
def print_summary(pkt):
if scapy.IP in pkt:
print("SRC:", pkt[scapy.IP].src, "\t\tDEST:", pkt[scapy.IP].dst)
if scapy.DNS in pkt:
print("Domain:", pkt.[scapy.DNS].qd.qname.decode("utf-8"))
decode("utf-8") because the qname is a utf encoded byte string.
I'm tring to understand the meaning of the python socket address info output.
import socket
rawSocket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))
pkt = rawSocket.recvfrom(2048)
print pkt[1]
('ens33', 2048, 1, 1, 'HE \xfd\x12h')
ens33 is the interface sending the data.
I guess that 2048 is the buffer size.
I have no idea what the first "1" is. Sometimes it's "0".
I noticed the second "1" relates to the interface (i.e. "772" for "lo")
'HE \xfd\x12h' : Reverting the converted hex values, we get '\x48\x45\x20\xfd\x12\x68', it gives the mac address of host machine in a VM bridged connection.
So, the main question is for #3. What 1 or 0 means here ?
In short, the third 1 means it's a broadcast packet. 0 would mean it was a packet addressed to the machine running the Python code. Details follow.
This is based on Python 3.6, but the answer should be similar for other Py2 or Py3 versions. The answer is split between the source for the socket module, the packet(7) man page, and the Linux source.
The Python library includes function makesockaddr(). For PF_PACKET sockets (same as AF_PACKET), the relevant portion gives you the following order of fields. Explanations from the man page are italicized.
ifname (e.g., ens33) (the interface name, as you noted)
sll_protocol (e.g., 2048) Physical-layer protocol
sll_pkttype (e.g., 1) Packet type
sll_hatype (e.g., 1) ARP hardware type
sll_addr (e.g., the MAC address, as you noted above) Physical-layer address
The Linux source gives the various values for packet type. In that list, 1 is PACKET_BROADCAST. 0 is PACKET_HOST, explained as "To us".
I am trying to filter packets of a particular website in Python (using Scapy). I have a list of possible IPs (used for load balancing) of the website. I want to filter packets for all those IPs. How can I do that?
For a single IP, I am using the following code:
bpf_filter = "ip and host " + addr
sniff(timeout=10, prn=pkt_callback, store=0)
Since you're using cBPF (classic BPF), the only way to filter a set of IP addresses is to list them all:
bpf_filter = "ip and ("
for addr in addresses[:-1]:
bpf_filter = "%shost %s or " % (bpf_filter, addr)
bpf_filter = "%shost %s)" % (bpf_filter, addresses[-1])
Which, for a set of IP addresses [10.0.0.1, 10.0.0.2, 10.0.0.3], will return:
ip and (host 10.0.0.1 or host 10.0.0.2 or host 10.0.0.3)
Note: You need at least one IP address in your set for the above to work.
Why is this a limitation of cBPF?
The filter expression you give Scapy is then compiled to BPF bytecode. BPF bytecode disallows backward jumps (and therefore loops). This restriction is a simple way to ensure the filter will eventually halt when executed in the kernel.
If backward jumps were allowed, you could write some smarter lookup through your set of IP addresses. You could for example store them in a hash table and check the packet against the hash table in O(1). This is actually possible with eBPF, which, as far as I know, isn't supported by Scapy yet.
I am using srp1() of Scapy to replay pcap file to a device as follows:
for p in rdpcap(pcapfile):
...
rcv = srp1(p, 'eth0')
print rcv[IP].len
print rcv[TCP].seq
...
When the device sends 1 packet I can get its IP.len and TCP.seq, but when it sends 2 packets, I can get only the information of the first packet while I need the information of the second one.
Where did I go wrong?
Both Scapy's user manual and Scapy's API documentation state that srp1() is a variant of srp() that returns just the first packet that constitutes an answer for the sent packet/s.
Therefore, try using srp() instead of srp1(), as follows:
for p in rdpcap(pcapfile):
...
answers, unanswered = srp(p, 'eth0')
last_request, last_answer = answers[-1]
print last_answer[IP].len
print last_answer[TCP].seq
...
How do I check for the presence of a particular layer in a scapy packet? For example, I need to check the src/dst fields of an IP header, how do I know that a particular packet actually has an IP header (as opposed to IPv6 for instance).
My problem is that when I go to check for an IP header field, I get an error saying that the IP layer doesn't exist. Instead of an IP header, this particular packet had IPv6.
pkt = Ether(packet_string)
if pkt[IP].dst == something:
# do this
My error occurs when I try to reference the IP layer. How do I check for that layers existence before attempting to manipulate it?
Thanks!
You should try the in operator. It returns True or False depending if the layer is present or not in the Packet.
root#u1010:~/scapy# scapy
Welcome to Scapy (2.2.0-dev)
>>> load_contrib("ospf")
>>> pkts=rdpcap("rogue_ospf_hello.pcap")
>>> p=pkts[0]
>>> IP in p
True
>>> UDP in p
False
>>>
root#u1010:~/scapy#
For completion I thought I would also mention the haslayer method.
>>> pkts=rdpcap("rogue_ospf_hello.pcap")
>>> p=pkts[0]
>>> p.haslayer(UDP)
0
>>> p.haslayer(IP)
1
Hope that helps as well.