scapy PcapReader cut short - python

I'm trying to rewrite a pcap file with different IP and IPv6 addresses. But after I extract a packt by PcapReader and change its IP adresses, the packets in the output pcap file is cut short (that is to say the payload of the packet is lost).
Here's the example code:
from scapy.all import PcapReader
from scapy.all import PcapWriter
def test():
f = "input.pcap"
writers = PcapWriter("output.pcap")
with PcapReader(f) as pcap_reader:
for pkt in pcap_reader:
# somehow change the IP address
writers.write(pkt=pkt)
test()
When I open the .pcap file with WireShark, it shows "The capture file appears to have been cut short in the middle of a packet".
Is there any solution in scapy to maintain the payload or is there any other python packets to recommand?
here I did not change anything and the results are like this:
input file:
enter image description here
output file:
enter image description here

I think the problem must be in the code you use to modify the packet (and which you did not show) or that your source file already had short packets (i.e. snaplen less than packet len). The following code works for me without problems:
from scapy.all import PcapReader,PcapWriter,IP
writer = PcapWriter('output.pcap')
for pkt in PcapReader('input.pcap'):
# somehow change the IP address
pkt[IP].dst = '1.2.3.4'
pkt[IP].src = '5.6.7.8'
writer.write(pkt=pkt)

Related

How to replace the IP addresses within a PCAP file using Scapy

I want to replace the IP addresses in a given PCAP file with Scapy.
The PCAP file (e.g. eth0.pcap) contains captures by Wireshark.
My message is not code, it shows the data flow between 2 IP addresses. I want to replace the original 2 addresses with another 2 addresses.
Example
Given before:
message1: 192.168.10.10-->192.168.20.20
message2: 192.168.20.20-->192.168.10.10
I want to replace for all packages within the file:
192.168.10.10 (source of first package) with 8.8.8.8
and 192.168.20.20 (destination of first package) with 1.1.1.1
So that afterwards there are:
message1: 172.10.10.10-->172.10.20.20
message2: 172.10.20.20-->172.10.10.10
How can I do this with?
hc_dev's answer guided me towards the solution.
I tried to understand it and make it work in my issue.
Below is my code:
from scapy.all import *
from scapy.utils import PcapWriter
packets = rdpcap('ftp.pcap')
new_cap = PcapWriter("ftp_new.pcap")
srcIp=packets[0][IP].src
dstIP=packets[0][IP].dst
#define new ip address I want use
ip1='8.8.8.8'
ip2='1.1.1.1'
#replace orinigal address
for p in packets:
if(p[IP].src == srcIp):
p[IP].src = ip1
elif(p[IP].src == dstIP):
p[IP].src = ip2
if(p[IP].dst == srcIp):
p[IP].dst = ip1
elif(p[IP].dst == dstIP):
p[IP].dst = ip2
new_cap.write(p)
wrpcap("ftp_new.pcap", packets)
So you want to modify packet capture output, like in PCAP file format.
This format is used by libpcap library to record captured packets to a file.
Scapy can read and write PCAP files, see the Scapy docs for PCAP.
Read, write, print, replace
To replace the IP addresses within this file, you have to read its packets into an object-model using the rdpcap() function.
Then you can print the IP addresses for each packet (with desired replacement).
Or you can also replace the IP within the object-model in memory.
Then write the complete model back using the wrpcap() function.
Example to print IP addresses
I used the example PCAP file dhcp.pcap from PCAP to Mermaid parser on GitHub:
from scapy.all import *
# load the PCAP file using rdpcap
packets = rdpcap('dhcp.pcap')
# Let's iterate through every packet
for packet in packets:
source_ip = packet.getlayer(IP).src
destination_ip = packet.getlayer(IP).dst
print(f"{source_ip} --> {destination_ip}")
# TODO: replace in model and write to PCAP file using wrpcap
See also
IncognitJoe's tutorial on Reading Pcap files with Scapy.
Getting source IP of packet using Scapy for getting the IP fields of a packet.
Update:
How to replace in your case
Refined talentldk's solution with some simplification and debug-prints:
no separate import needed (all imports all)
no writer needed, just use wrpcap to write the read model
use a replacement-dict with string's replace function where dict entry (a tuple) is passed to function using the unpack-operator * as prefix resulting in 2 separate arguments (key is replaced by value)
use iter over the dict's items to process all replacements (here 2 entries) where the next item can be drawn by next function
from scapy.all import *
# rdpcap loads in our pcap file
packets = rdpcap('dhcp.pcap')
# define search
first_src_ip = packets[0][IP].src
first_dst_ip = packets[0][IP].dst
# define new ip address to use as replacement
ip_replacement = {f"{first_src_ip}" : '8.8.8.8', f"{first_dst_ip}" : '1.1.1.1'}
print(f"replacement: {ip_replacement}")
# Let's iterate through every packet
for i, packet in enumerate(packets):
source_ip = packet.getlayer(IP).src
destination_ip = packet.getlayer(IP).dst
print(f"[{i:3}] original: {source_ip} --> {destination_ip}")
# replace in model
replacement = iter(ip_replacement.items())
source_ip = source_ip.replace(*next(replacement))
destination_ip = destination_ip.replace(*next(replacement))
print(f"[{i:3}] replaced: {source_ip} --> {destination_ip}")
wrpcap("dhcp_replaced.pcap", packets)
Prints:
replacement: {'0.0.0.0': '8.8.8.8', '255.255.255.255': '1.1.1.1'}
[ 0] original: 0.0.0.0 --> 255.255.255.255
[ 0] replaced: 8.8.8.8 --> 1.1.1.1
[ 1] original: 192.168.0.1 --> 192.168.0.10
[ 1] replaced: 192.168.0.1 --> 192.168.0.10
[ 2] original: 0.0.0.0 --> 255.255.255.255
[ 2] replaced: 8.8.8.8 --> 1.1.1.1
[ 3] original: 192.168.0.1 --> 192.168.0.10
[ 3] replaced: 192.168.0.1 --> 192.168.0.10

Python Scapy RTP header manipulation - how can I decode RTP in scapy?

I need to edit 2 RTP header fields in a PACP file.
I want to edit the RTP timestamp field and the SSRC field. This is so that I can manipulate some capture for replay using tcpreplay for testing end device for a couple of reasons.
Using the Python REPL i am able to load the file:
from scapy.all import rdpcap
pkts_list = rdpcap("foo.pcap")
the I get the length of the file
len(pkts_list)
then I can inspect a packet
pkts_list[xxx].show()
How / Can I decode / see RTP header information? Sorry scapy docs are a bit think for me on this one.
Thanks in advance
Neil
RTP(pkts_list[xxx]['UDP'].payload).timestamp did the job with a bit of trial and error
my other question is more python related I will ask that somewhere else.
Update:
Posting my full code for manipulating RTP headers as it took a lot of trial and error and very small titbits of info to sort it ... its a hack but it works, might help someone else.
### Python 2.7 Script by Neil Bernard neil#themoog.org
## This script is for editing RTP header information in PCAP files to manipulate RTP timestamp and SSRC
## for testing hardware transport stream devices and testing SMPTE 2022-2 handling
## There is some error checking for scapy network layers but try to keep capture as clean as possible
## also try to keep captures under 260Mb for performance, can take a good 20mins on an intel i7 / 16GB ram
#### Basic Scapy Tutorial
# *https://www.youtube.com/watch?v=ADDYo6CgeQY
#### Scapy cheat sheet
# https://blogs.sans.org/pen-testing/files/2016/04/ScapyCheatSheet_v0.2.pdf
#### Scapy RTP Library https://fossies.org/linux/scapy/scapy/layers/rtp.py
# import scapy
from scapy.all import rdpcap
from scapy.all import wrpcap
from scapy.all import RTP
from scapy.all import *
infile = "test_rtp.pcap"
outfile = "modified_" + infile
dest_port = 2000 # usefull to make sure you only action packets that are RTP
# load packet capture
print "Loading Packet Capture Keep <200Mb - Might take a few mins....."
pl = rdpcap(infile)
print "Loading complete!"
# print number of packets
print(len(pl))
# # print rtp timestamp
# print(RTP(pl[0][UDP].payload).timestamp)
numberofpckts = len(pl)
print numberofpckts
for pkt in range(numberofpckts):
# You cant use the [RTP] layer on a list index so you have to put it in a
# variable first. Also need to make sure its a UDP packet with .haslayer(UDP):
# https://stackoverflow.com/questions/48763072/scapy-getting-trailer-field-in-the-dissector
if pl[pkt].haslayer(UDP):
packet = pl[pkt][UDP]
else:
print "Probably Not a UDP / RTP Packet# {0}".format(pkt)
# You need to do the line below to force RTP detection and manipulation
# https://stackoverflow.com/questions/44724186/decode-rtp-over-udp-with-scapy
if pl[pkt].haslayer(UDP):
if packet["UDP"].dport==2000: # Make sure its actually RTP
packet["UDP"].payload = RTP(packet["Raw"].load)
#### un-commment and change lines below to manipulate headers
# packet[RTP].version = 0
# packet[RTP].padding = 0
# packet[RTP].extension = 0
# packet[RTP].numsync = 0
# packet[RTP].marker = 0
# packet[RTP].payload_type = 0
# packet[RTP].sequence = 0
# packet[RTP].timestamp = 0
packet[RTP].sourcesync = 0
# packet[RTP].sync = 0
### Calculate UDP Checksum or they will now be wrong!
#https://scapy.readthedocs.io/en/latest/functions.html
checksum_scapy_original = packet[UDP].chksum
# set up and calculate some stuff
packet[UDP].chksum = None ## Need to set chksum to None before starting recalc
packetchk = IP(raw(packet)) # Build packet (automatically done when sending)
checksum_scapy = packet[UDP].chksum
packet_raw = raw(packetchk)
udp_raw = packet_raw[20:]
# in4_chksum is used to automatically build a pseudo-header
chksum = in4_chksum(socket.IPPROTO_UDP, packetchk[IP], udp_raw) # For more infos, call "help(in4_chksum)"
# Set the new checksum in the packet
packet[UDP].chksum = checksum_scapy # <<<< Make sure you use the variable in checksum_scapy
# needed below to test layers before printing newts/newsourcesync etc to console
if pl[pkt].haslayer(UDP):
newts = RTP(pl[pkt][UDP].payload).timestamp
newsourcesync = RTP(pl[pkt][UDP].payload).sourcesync
else:
newts = 999
newsourcesync = 999
print("Changing packet {0} of {3} to new timestamp {1} SSRC {2} Old UDP chksum {4} >> New UDP chksum ???").format(pkt+1,newts,newsourcesync,numberofpckts,hex(checksum_scapy_original))
else:
print "Probably Not a UDP / RTP Packet# {0}".format(pkt)
# Write out new capture file
wrpcap(outfile, pl)
you can use from this command for write a rtp header packet. but whet you trying to write this packet in a pcap. you will see this error:
TypeError: clone_with() got multiple values for keyword argument
'payload'
therefor you before write packet must change scapy/packet.py lib. this link explain better:
https://github.com/CJTozer/scapy/commit/d0367f2efa86e65fa2b0a82a864b7fe89222f153

Intercepting with Scapy

I try to develop a MITM attack tool, first tried arp poisoning, then sniffing and filter specific packet (both of them with scapy)
if there is a match with my filter I need to alter it on the fly so, create a copy of this packet then manipulate TCP data and send it.
But the last step doesn't work. Does it correct manipulate for copying packet?
from netfilterqueue import NetfilterQueue
from scapy.all import *
from scapy.error import Scapy_Exception
import os
import sys
import threading
import signal
def Inspector (packet):
if packet[TCP].payload :
tcp_data = str(packet[TCP].payload)
if 'Open' in tcp_data:
packet_data = tcp_data.split('(')
a= copy.deepcopy(packet) # First Copy The packet
packet.drop() # Second Drop it
if 'Up' in tcp_data :
payload_O = packet_data[0] + '(' + 'Down//inject'
a[TCP].data = payload_O
send(a)
else :
packet.accept()
def main():
try:
print '[*] Starting Queue '
nfqueue.run_socket()
sniff(filter="tcp port 2626", prn=inspector, store=0)
if __name__ == "__main__":
main()
It is not possible to intercept a packet using scapy. What your code might do is send another packet following the original one.
The best way to create a MITM tool is by ARP poisoning. You can have a look at scapy tutorials such as in:
The scapy official doc
This good tutorial at medium.com
Of course, your tool will only be used in a security purpose, as you know that such pratice is illegal

Filter protocols in python

I'm trying to filter certain packets with protocols using user input from a given pcap file and than move the packets to a new pcap file.
That the code I made so far:
# ===================================================
# Imports
# ===================================================
from scapy.all import *
from scapy.utils import PcapWriter
"""
your going to need to install the modules below
"""
from Tkinter import Tk
from tkFileDialog import askopenfilename
# ===================================================
# Constants
# ===================================================
#OS commands:
#~~~~~~~~~~~~~
if "linux2" in sys.platform:
"""
linux based system clear command
"""
CLEAR_COMMAND = "clear"
elif "win32" in sys.platform:
"""
windows based system clear command
"""
CLEAR_COMMAND = "cls"
elif "cygwin" in sys.platform:
"""
crygwin based clear command
"""
CLEAR_COMMAND = "printf \"\\033c\""
elif "darwin" in sys.platform:
"""
mac OS X based clear command
"""
CLEAR_COMMAND = "printf \'\\33c\\e[3J\'"
#Usage string:
#~~~~~~~~~~~~~~
FILE_STRING = "please choose a pcap file to use"
BROWSE_STRING = "press any key to browser files\n"
BAD_PATH_STRING = "bad file please try agien\n"
BAD_INPUT_STRING = "bad input please try agien\n"
PROTOCOL_STRING = "please enter the protocol you wish to filter\n"
NAME_STRING = "please enter the new pcap file name\n"
# ===================================================
# Code
# ===================================================
def filter_pcap():
"""
filtering from the given pcap file a protocol the user chooce (from any layer in the OSI model)
and than asks for a new pcap file name, than filters the given protocol to a new pcap file
:param none
:return nothing:
"""
path = file_browse()
i = 0
filtertype = raw_input(PROTOCOL_STRING)
name = raw_input(NAME_STRING)
packs = rdpcap(path)
for i in range(len(packs)):
if filtertype in packs[i]:
wrpcap(name +".pcap", packs[i])
def file_browse():
"""
Purpose: It will allow the user to browse files on his computer
than it will check if the path is ok and will return it
:returns - the path to the chosen pcap file
"""
path = "test"
while ".pcap" not in path:
print FILE_STRING
raw_input(BROWSE_STRING)
os.system(CLEAR_COMMAND)
Tk().withdraw()
path = askopenfilename()
if ".pcap" not in path:
print BAD_PATH_STRING
return path
filter_pcap()
Now the problem is that I'm failing to filter the packets correctly.
The code need to filter protocols from any layer and any kind.
I have checked that thread: How can I filter a pcap file by specific protocol using python?
But as you can see it was not answered and the user added the problems I had in the edit, if any one could help me it would be great
Example for how it should work:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lets say i use the file "sniff" as my first pcap file and it has 489 packets when 200 from those packets are http packets.
now there is that print:
please enter the protocol you wish to filter
'http'
and than there is the print:
please enter the new pcap file name
'new'
the user input was 'http' now the program will search for every packet that run on http protocol and will create a new pcap file called 'new.pcap'.
the file 'new.pcap' will contain 200 http packets.
now that thing should work with any protocol on the OSI model including protocols like IP, TCP, Ethernet and so on (all the protocols in the ascii model).
I have found out that wireshark command line has the option -R and tshark has .protocols, but it dont really work... Can any one check that?
edit: i found pyshark but i dont know how to write with it
I don't believe that scapy has any functions or methods to support application layer protocols in the way that you are after. However, using sport and dport as a filter will do the trick (provided you are going to see/are expecting default ports).
Try something like this:
def filter_pcap(filtertype = None):
..... snip .....
# Static Dict of port to protocol values. Like so:
protocols = {'http': 80, 'ssh': 22, 'telnet': 21}
# Check to see if it is in the list
while filtertype not in protocols:
filtertype = raw_input(PROTOCOL_STRING)
# Name for output file
name = raw_input(NAME_STRING)
# Read Input File
packs = rdpcap(path)
# Filters to only TCP packets
for packet in packs[TCP]:
# Filter only the proto (aka Port) that we want
if protocols[filtertype] in packet.sport or protocols[filtertype] in packet.dport :
# Write to file
wrpcap(name +".pcap", packet)

Accessing the data field in a pcap dump file in python

I am using the following code:
import pyshark
cap = pyshark.FileCapture('/home/my_location/python_parse/my_file.pcap')
count = 0;
for caps in cap:
print caps.pretty_print();
print "Count is " + str(count)
count+=1;
My pcapfile is located here https://1drv.ms/u/s!Aj1_HY10QdBTa5zNMcZadSK3qAM
If someone has any alternative hosting site, I am ready to put it up at that location also.
I am unable to access the data field using pyshark. When I open up the pcap file in wireshark , I can see the data in this field:
Can anyone offer any suggestion?
You can use the usb_capdata field of the DATA layer.
import pyshark
caps = pyshark.FileCapture('/home/my_location/python_pars/my_file.pcap')
print caps[10].layers[1].usb_capdata
Will output the same as wireshark :
'80:08:0b:82:00:00:85:97:8a:86:00:00'

Categories