Scapy - Removing Dot1q (801.2 Q) from a pcap - python

I am trying to send TCP traffic to a device using a python script.
My script reads a particular pcap for processing packets.
In my packet capture file there is Dot1Q VLAN tagging and I would like to remove it using scapy in my script.
Output contains the following :
Ether / Dot1Q / IP / TCP >: port > : port
Please tell how I can remove the vlan tagging in my script using scapy.

You should walk through all layers and find Ether. Then set its payload to the payload of 802.1q layer. The code looks like (it's not the exact code by just the sketch):
while not isinstance(layer, NoPayload):
if (type(layer) is Ether):
payload_inside_dot1q = layer.payload.payload
layer.add_payload(payload_inside_dot1q)
layer.type = 0x0800 # IPv4
layer = layer.payload
You probably should also correct layer.chksum for Ether layer.
UPD: You can also remove 802.1q layer tcprewrite (https://linux.die.net/man/1/tcprewrite) when preparing pcap file:
tcprewrite --enet-vlan=del --infile=input.cap --outfile=output.cap

Related

How to filter packets using IP source and destination addresses with Pyshark

I've just try to use PYSHARK and filtering using BPF_filter = 'tcp' packets, however I am looking for filtering by source and destination IP addresses. The spript is shown below:
import pyshark
capture = pyshark.LiveCapture(interface=r'\Device\NPF_{707B2864-16B6-4E63-A44E-18BA00FC87EA}', bpf_filter='tcp and src.address=192.168.74.253 and dst.address=172.16.0.121', output_file=('../logs_capture/20221013_cap1.xdoc'))
capture.sniff(timeout=5)
capture
capture[3]
#<UDP/HTTP Packet>
for packet in capture.sniff_continuously(packet_count=5):
print ('Just arrived:', packet)
Can anyone give me an hint?
BPF filters are specified using pcap-filter syntax. So in your case you'd use 'ip and tcp and src host 192.168.74.253 and dst host 172.16.0.121'.

How to get TCP-Timestamp (TSval) using python

I've searched in several places, but I didn't find a simple answer to this question -
I have a .pcap file, generated using Wireshark, with several packets in it, and I wish to extract from each packet it's TCP-Timestamp (TSval).
I've managed to open each packet using scapy
packets = rdpcap('pcap_file.pcap')
for packet in packets:
print(packet.payload.id)
but I can't find the TSval of the packet (even though I can see the TSval field in the packet when I open it with Wireshark as shown in the picture below).
Packets can be accessed like dictionaries whose keys are protocols and values are payloads. For instance you can print the TCP payload in a packet like this:
if TCP in packet:
packet[TCP].show()
Now to get the TSval of the payload you have to look in TCP options. Each TCP option is encoded by scapy as a couple (option name, option value). For the timestamp option, the option value is itself a couple (TSval, TSecr). So you can basically get what you want doing the following:
from scapy.all import TCP, rdpcap
packets = rdpcap('packets.pcapng')
for packet in packets:
if TCP in packet: # ignore packets without TCP payload
for opt, val in packet[TCP].options: # consider all TCP options
if opt == 'Timestamp':
TSval, TSecr = val # decode the value of the option
print('TSval =', TSval)

Creating RTP packet with Python

I'm working on project at university. What I'm trying to do is that I want my program to be able to change bits in RTP packets' payload. I'm trying to create a simple tool that would apply steganography on RTP packets' payload - first it would "catch" rtp packets sent from my computer, then change payload section and send packets further to destination. First thing (while learning) what I'm trying to do is to create RTP packet stream. And i thought I managed, until I checked with Wireshark:
All that we see is only UDP packets (python socket.SOCK_DGRAM)
And just simple data stream of bits..
I'm trying to create packet by the RCF 3550:
Version - 2 bits;
padding - 1 bit;
extension - 1 bit;
csrc_count - 4bits;
marker - 1 bit;
payload_type - 7 bits;
sequence_number - 16 bits;
timestamp - 32 bits;
ssrc - 32 bits;
payload - varies;
I'm creating a list of bits:
rtp_packet[0:2] = format(packet_data["version"], "b").zfill(2)
rtp_packet[2:3] = format(packet_data["padding"], "b")
rtp_packet[3:4] = format(packet_data["extension"], "b")
rtp_packet[4:8] = format(packet_datas["csrc_count"], "b").zfill(4)
rtp_packet[8:9] = format(packet_data["marker"], "b")
rtp_packet[9:16] = format(packet_data["payload_type"], "b").zfill(7)
rtp_packet[16:32] = format(packet_data["sequence_number"], "b").zfill(16)
rtp_packet[32:64] = format(packet_datas["timestamp"], "b").zfill(32) # turi dideti po 160
rtp_packet[64:96] = format(packet_data["ssrc"], "b").zfill(32)
rtp_packet[96:128] = format(packet_data["csrc_list"], "b").zfill(32)
rtp_packet[128:] = bin(int.from_bytes(packet_data["payload"].encode(), "big"))[2:]
And then send those bytes through created socket. Didin't work (the result is above). Then I found this PyRTP library and the result is the same as above.
I'm new in Python and all this packet thing, so maybe someone could help me with what I'm doing wrong or what I didn't do that Wireshark still sees those packets as only UDP, but not RTP? Image below is what I hope to do (this was done with a SIP server at home). Thank you in advance!
RTP is an application protocol and it's encapsulated into UDP (network protocol), you can verify that your packets are RTP packets using WireShark with the following: https://en.wikiversity.org/wiki/Wireshark/IPv4_multicast
Wireshark actually understands audio/video packets as UDP packets.
You can convert those UDP packets to RTP using the menu option - https://support.hpe.com/hpesc/public/docDisplay?docId=c03617954&docLocale=en_US
You could create that packet using the scapy library's rtp functionality and save it as pcap file (as done in question):
from scapy.all import *
from scapy.all import wrpcap
rtp = { "marker": 1,
"payload_type": 0,
"sequence": 27675,
"timestamp": 3157624494,
"sourcesync": 0x3f622544
}
pkt= Ether()/IP(src="10.0.0.10", dst="10.0.0.100")/UDP(sport=18529, dport=23570)/RTP(**rtp)
wrpcap("rtp_pkt.pcap",pkt)
You can then open the resulting pcap file in Wireshark on the command line and tell it to decode port 23570 as RTP:
wireshark -d udp.port==23570,rtp rtp_pkt.pcap
There also other dedicated Python based RTP libraries such as rtp.

Why am I obtaining this strange output using the scapy.sniff() function trying to sniff traffic of the opened website?

I am very new in Pyhon (I mainly came from Java) and I am following a Pytohon course applied to security on Udemy in which it is presented an example of packet sniffer implementation using scapy module. I am using Python 3 and this is the structure of my Python project with the scapy version highlighted:
It seems to work but I have some doubts related to the output of this application.
This is my source code of my script:
#!usr/bin/env python
# INSTALL THE FOLLOWING PYTHON MODULES:
# - pip3 install scapy
# - pip3 install scapy_http
import scapy.all as scapy
from scapy.layers import http
#
def sniff(interface):
scapy.sniff(iface=interface, store=False, prn=process_sniffed_packet)
def process_sniffed_packet(packet):
#print(packet)
# Check if our packet has HTTP layer. If our packet has the HTTP layer and it is HTTPRequest.
# In this way I am excluding some garbage information in which I am not interested into.
if packet.haslayer(http.HTTPRequest):
print(packet)
sniff("eth0")
So this script sniff the traffic on the eth0 port and by the content of this if statement:
if packet.haslayer(http.HTTPRequest):
print(packet)
it print only the packet related to the HTTP layer avoiding to print other garbage information in which I am not interested.
So I execute the script launching this command into my Linux shell:
python3 packet_sniffer.py
and the script wait until I open a website into the browser and I obtain an output like this:
root#kali:~/Documents/PycharmWS/packet_sniffer# python3 packet_sniffer.py
b"\x00PV\xfd\xa9B\x00PV)\x97\xc7\x08\x00E\x00\x01\x9d\xdb\x84#\x00#\x06\x18*\xc0\xa8\xdf\x85\xd8:\xcdC\x90$\x00Pe\xa7\xb3\x8eM\xf9Y\xd6P\x18\xf9\x8aG<\x00\x00POST /gts1o1 HTTP/1.1\r\nHost: ocsp.pki.goog\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0\r\nAccept: */*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application/ocsp-request\r\nContent-Length: 83\r\nConnection: keep-alive\r\n\r\n0Q0O0M0K0I0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14BF0\xc2'\x19\xdb\xdep\xf0\x8f\xfcs\xe5\xa6_f8\x17\xbc\x04\x14\x98\xd1\xf8n\x10\xeb\xcf\x9b\xec`\x9f\x18\x90\x1b\xa0\xeb}\t\xfd+\x02\x10Qn\xe3\x01\xd1(\xfa$\x08\x00\x00\x00\x002\n\x81"
Here I have some doubt:
1) Why am I obtaining this strange string as output? On the Udemy tutorial there is a more understandable output showing information of the package as such as refereres, User-Agent, Host in a more clear way. What is all these \x.. value in my output? It seems to me that it is coded in some way but I am absolutly not sure about this.
2) Opening different web sites sometimes it provide me the output after that a specific website was opened but some other time it give me no output. How is it possible?
3) Is it working only over HTTP or it is sniffing also over HTTPS?
What is wrong? What am I missing? How can it ry to fix it?
Question 1
What are Python byte arrays?
That's the byte array of the packet. "\x55" means 01010101. In python, a bytes object is like a string, but prepending with b like b"bytes" or b'bytes'.
As an example, if we take the first 4 bytes of the bytearray that was printed,
and write it to a file, we can see the bytes representations according to xxd.
$ python -c 'f=open("temp", "wb");f.write(b"\x00PV\xfd");f.close()'
$ xxd temp
00000000: 0050 56fd .PV.
Here,
b"\x00P" => 0050
b"V\xfd" => 56fd
The 2nd char, P in the byte array, is the hex representation of the ASCII char P.
So you are getting this output because you are printing the bytes of the packet. If you want to print a different representation.
How to print packets in scapy
Use packet.show() instead of print(packet) to have scapy analyze it for you.
Output will look like this:
$ python script.py
###[ Ethernet ]###
dst = cc:65:ad:da:39:70
src = 6c:96:cf:d8:7f:e7
type = IPv6
###[ IPv6 ]###
version = 6
tc = 2
fl = 131466
...
###[ HTTP 1 ]###
###[ HTTP Request ]###
Method = 'GET'
Path = '/online'
Http_Version= 'HTTP/1.1'
...
You could use print(packet.summary()) instead to get something like this per packet:
Ether / IPv6 / TCP / HTTP / 'GET' '/online' 'HTTP/1.1'
Scapy packet object documentation is here, and to see the methods/attributes of an object in Python, use dir(Object).
Question 2
HTTP is a different protocol than HTTPS. Scapy listens for HTTP with that filter and drops HTTPS.
Question 3
Correct. See 2.
Ultimately, if you want to capture all of the packets, don't use a filter. There are ways to decrypt HTTPS packets, but that's for a different question ;)

Sniffing UDP packets using scapy in Mac

I am trying to sniff UDP packets using scapy sniff function, I send the packets in the Looback interface, the sending code is simple as follows:
from socket import *
IPv4 = "127.0.0.1"
Port = 45943
ClientSock = socket(AF_INET, SOCK_DGRAM)
while True:
MESSAGE = raw_input()
ClientSock.sendto(MESSAGE, (IPv4, Port))
However when i run (in another terminal tab after importing scapy):
a = sniff(iface="lo0", count = 5)
I get the following result:
>>> a.nsummary()
0000 Raw
0001 Raw
0002 Raw
0003 Raw
0004 Raw
whereas i am supposed to get UDP packets!, can any one point out to anything that i am missing here.
thanks
Unable to guess datalink type (interface=lo0 linktype=0)
That message translates as "Scapy doesn't understand the DLT_NULL link-layer header type, as used on the loopback device in *BSD and OS X, so it doesn't support the loopback device on *BSD and OS X".
So you're out of luck if you want to use Scapy on OS X to capture on the loopback device, unless and until Scapy is enhanced to handle DLT_NULL. (DLT_NULL is not that hard to handle, so presumably the only reason it's not handled is that most of the people using it on a loopback device are doing so on Linux, where the link-layer header type value on the loopback device is DLT_EN10MB, i.e. Ethernet, so nobody's bothered to fix it. I'll see if I can get it working and, if so, send them a patch.)
Some suggestions.
Instead of a.nsummary(), you can print out more information on individual packets using something like
a[1].show()
a[1].show2()
hexdump(a[1])
to examine the first packet.
2) You can force the protocol decoding to a particular type of packet format. For instance, a RAW_IP packet capture (link layer header type = 101) can be forced to be IPv6 using
conf.l2types.register(101, IPv6)
If you want to add a new layer on top of UDP, you can add a new dissector based on the port used.

Categories