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

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

Related

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

Using nslookup to find domain name and only the domain name

Currently I have a text file with mutiple IP's I am currently attempting to pull only the domain name from the set of information given using nslookup (code below)
with open('test.txt','r') as f:
for line in f:
print os.system('nslookup' + " " + line)
This works in so far that it pulls all the information from the first IP's. I can't get it passed the first IP but I'm currently attempting to clean up the information recived to only the Domain name of the IP. Is there any way to do that or do I need to use a diffrent module
Like IgorN, I wouldn't make a system call to use nslookup; I would also use socket. However, the answer shared by IgorN provides the hostname. The requestor asked for the domain name. See below:
import socket
with open('test.txt', 'r') as f:
for ip in f:
fqdn = socket.gethostbyaddr(ip) # Generates a tuple in the form of: ('server.example.com', [], ['127.0.0.1'])
domain = '.'.join(fqdn[0].split('.')[1:])
print(domain)
Assuming that test.txt contains the following line, which resolves to a FQDN of server.example.com:
127.0.0.1
this will generate the following output:
example.com
which is what (I believe) the OP desires.
import socket
name = socket.gethostbyaddr(‘127.0.0.1’)
print(name) #to get the triple
print(name[0]) #to just get the hostname

scapy PcapReader cut short

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)

Python ciscoconfparse - build a basic config file

I am trying to create a cisco basic config based in a full config file.
Following some examples of the full file configuration:
!
policy-map QOS
class GOLD
priority percent 10
class SILVER
bandwidth 30
random-detect
class default
!
interface Loopback1
description Management
ip address 9.9.9.9 255.255.255.255
!
interface FastEthernet0/0
description LAN
ip address 6.6.6.6 255.255.255.0
!
ip access-list standard customer_internal
permit 1.1.1.1
permit 2.2.2.2
permit 3.3.3.3
!
I found out this ciscoconfparse library (https://pypi.python.org/pypi/ciscoconfparse). and was able to grab the configuration lines blocks to another file, but don´t know how to exclude the blocks to a basic config file.
from ciscoconfparse import CiscoConfParse
full_config_for_parse = file('./fullconfig.txt')
basic_config_file = open('./basic_config.txt', 'w') # This file needs to contain only basic config, like interface, IP, etc...
security_config_file = open('./security_config_file.txt', 'w') # This is the file that was able to send the line blocks
parse = CiscoConfParse(full_config_for_parse)
class_map = parse.find_all_children('class-map')
access_list = parse.find_all_children('ip access-list')
if class_map != ' ': # If class_map is not empty, it means that the full config file has class-map configuration, so, it needs to be removed or not copied to basic config file
for line in class_map:
security_config_file.write(line) # separating the blocks to another file
#basic_config_file.write(remove_this_line) I need to delete/not write this line to basic_config_file
if access_list != ' ':
for line in access_list:
security_config_file.write(line)
#basic_config_file.write(remove_this_line)
# There is another code part that is copying all the rest of the basic configuration to basic_config_file, that is working OK
files.close()
Anyone knows a better way for deleting or not copying these config blocks to a basic config file?
As far as I can tell, you want to delete policy-map and ip access-list from the configuration at the bottom of my answer, and save the result as basic_config.txt.
This is the most efficient way to do it...
from ciscoconfparse import CiscoConfParse
parse = CiscoConfParse('fullconfig.txt')
objs = list()
objs.extend(parse.find_objects(r'^policy-map')) # Append policy-map obj
objs.extend(parse.find_objects(r'ip\saccess-list'))# Append ip access-list obj
for obj in objs:
# When you delete a parent obj, it recurses through all children
obj.delete()
parse.commit()
parse.save_as('basic_config.txt') # Save the new configuration
When you look at basic_config.txt, it will have the following lines...
!
!
interface Loopback1
description Management
ip address 9.9.9.9 255.255.255.255
!
interface FastEthernet0/0
description LAN
ip address 6.6.6.6 255.255.255.0
!
!
File name: fullconfig.txt...
!
policy-map QOS
class GOLD
priority percent 10
class SILVER
bandwidth 30
random-detect
class default
!
interface Loopback1
description Management
ip address 9.9.9.9 255.255.255.255
!
interface FastEthernet0/0
description LAN
ip address 6.6.6.6 255.255.255.0
!
ip access-list standard customer_internal
permit 1.1.1.1
permit 2.2.2.2
permit 3.3.3.3
!
After a lot of hours of researches and tries, got what I want, editing the answer with new code that is now working:
from ciscoconfparse import CiscoConfParse
def main():
# 2nd Part - Begin
p = CiscoConfParse("./basic_config.txt")
basic_config_NEW = open('./basic_config_NEW.txt', 'w') # Need to open a new file as using the same one was getting empty file
for obj in p.find_objects(r'^class-map'):
obj.delete('class')
for obj in p.find_objects(r'^ip access-list'):
obj.delete('ip access-list')
for line in p.ioscfg:
basic_config_NEW.write(line) # This was adding the lines without break lines on the file
basic_config_NEW.write('\n') # Have to add it not sure if there is another way of doing it in one single line
basic_config_NEW.close()
# 2nd Part - End
if __name__ == "__main__":
main()

Show protocols of packets captured and saved in a .pcap with scapy on python

I am capturing live air WiFi traffic and saving only the headers of the packets captures in a .pcap file.
Is it possible to find out what protocols have been used on the whole capture? If yes, how can I keep track of the number of packets under every protocol found?
I've found a lot of info on injecting packets with Scapy but not on analyzing.
So far I've tried:
from scapy.all import * # import scapy package
from scapy.utils import rdpcap # import module for loading pcaps
pkts = rdpcap("./traffic/capture20131120-001.pcap") # load pcap
pkts.summary(lambda(r): r.sprintf("%Dot11.proto%")) # protocol?
print -(256-ord(pkts[24].notdecoded[-4:-3])) # signal strength of packet 24
Seems like pkts.summary(lambda(r): r.sprintf("%Dot11.proto%")) returns 0L and I don't understand that.
Currently, Scapy does not support very many protocols, so it's great for some tasks, but not others. Using pyshark instead (a Python wrapper for Wireshark), there are many more supported protocols.
Using Scapy:
from scapy.all import *
def process_with_scapy(fileName):
protocol_count = {}
pcap_data = rdpcap(fileName)
sessions = pcap_data.sessions()
for session in sessions:
for packet in sessions[session]:
for i in range(len(packet.layers())):
layer = packet.getlayer(i)
protocol = layer.name
# Count the number of occurences for each protocol type
if protocol not in protocol_count: protocol_count[protocol] = 1
else: protocol_count[protocol] += 1
# Sort the dictionary in descending order
protocol_count = dict(sorted(protocol_count.items(), key=lambda item: item[1], reverse=True))
# Print the output
for protocol in protocol_count:
print(f'{protocol_count[protocol]} packets have layer "{protocol}"')
process_with_scapy('./traffic/capture20131120-001.pcap')
Documentation:
https://readthedocs.org/projects/scapy/downloads/pdf/latest
Using PyShark (slower but more supported):
import pyshark
def process_with_pyshark(fileName):
protocol_count = {}
pcap_data = pyshark.FileCapture(fileName)
for packet in pcap_data:
for layer in packet:
protocol = layer.layer_name
# Count the number of occurences for each protocol type
if protocol not in protocol_count: protocol_count[protocol] = 1
else: protocol_count[protocol] += 1
# Sort the dictionary in descending order
protocol_count = dict(sorted(protocol_count.items(), key=lambda item: item[1], reverse=True))
# Print the output
for protocol in protocol_count:
print(f'{protocol_count[protocol]} packets have layer "{protocol}"')
process_with_pyshark('./traffic/capture20131120-001.pcap')
For information on a specific protocol:
https://www.wireshark.org/docs/dfref/
The source code for a specific protocol dissector can also sometimes be useful:
https://github.com/wireshark/wireshark/tree/master/epan/dissectors

Categories