How to create new layer or new protocol with scapy? - python

I want to create a new layer using scapy. I created a new layer but when I sent it to another computer it got lost, Wireshark also couldn't recognize it. How can I solve this problem?
class OMER(Packet):
name = "OMER"
fields_desc = [StrLenField("Omer", "", None)]

When you create a new protocol or a new layer with scapy, other network tools like wireshark (and others) since they are not aware of your protocol's specifics will not be able to automatically parse it correctly.
If you want to experiment with a new protocol you will have to create your own local decoder. The following example even its minimal, it demonstrates all of the above:
#!/usr/bin/env python
from scapy.all import *
# create a simple protocol
# (example similar with the one in the scapy docs...)
class Exmpl(Packet):
name = "myprotocol"
fields_desc=[ ShortField("fld1",5),
XByteField("fld2",3) ]
from scapy.utils import PcapWriter
# write data in a pcap file to examine later with
# 1 -> scapy
# 2 -> wireshark
print '\n[+] Writing net.pcap file...'
cap = PcapWriter("net.pcap", append=True, sync=True)
for i in range(10):
packet = Exmpl(fld1=i)
cap.write(packet)
# read the data and examine them with scapy
# scapy is aware of the "Exmpl" protocol (e.g. its fields etc...)
# and how to decode it, while wireshark is not
print '[+] Examining net.pcap file...\n'
packets = rdpcap('net.pcap')
for p in packets:
Exmpl(str(p)).show()
The output of the above script will be like:
[+] Writing net.pcap file...
[+] Examining net.pcap file...
###[ myprotocol ]###
fld1 = 0
fld2 = 0x3
###[ myprotocol ]###
fld1 = 1
fld2 = 0x3
###[ myprotocol ]###
fld1 = 2
fld2 = 0x3
...skip...
As you can see scapy is aware of the protocol and thus can parse the data correctly. Now if you try to examine the "net.pcap" file with wireshark you will see the following:
wireshark is not aware of your protocol and as a result it can't parse it correctly.
Notice: As you can understand, even if you send those packets in another device (to actually do that you'll have to implement some other stuff also) then that device must also be aware of your protocol, otherwise it won't be able to parse it correctly. That is why when you tried to send the packets from one computer to another, the receiver couldn't successfully decode them.

Thinks of it as learning a foreign language. Let's say your family is speaking only english.
You learn chinese and now are able to say sentences in chinese (that's what you did by creating the layer in scapy and building packets)
Your family can hear the chinese sentences (wireshark can receive the packets)
But your family cannot understand the sentences since they will still just speak english (wireshark cannot dissects the packets as it does not know your protocol)
If you abolutely want your family to understand you when speaking chinese, then you have to teach them (implement the dissector in wireshark)
Hope that's clear :-)

Did you bind the new layer/protocol to the previous layer/protocol? E.g. bind_layers(Ether, OMER) if the OMER layer comes right after the Ether layer.

You have made your custom layer. However you shoukd keep in your mind that this custom layer wouldnt be recognized by a operating system untill you write networking at kernel level and that must be present in every operating system. So on the other host, you write another python script that tells operating system to disect the packet containing your custom layer accordingly. Write your own sniffer packet and then initliaze your custom layer class so that while sniffing you actually make use of that class while disecting the packet and then you can view the packet with your custom layer.
Note that you need to bind the layer, your new layer with the layer in froth of it. It appears as if you have constructed right packet, however when it reaches the other host it simply cannot decode untill you bind the layer. Only when you bind it, your operating system knows to disect the packet excatly after your ip layer or udp layer etc... Have been dissected .
Also in oder to squeeze your custom layer between two well known protocols, say for example udp with port 53, operating by default binds dns to udp port 53. If you ever wanted to inject your new layer between udp and dns say, u need to firstly unbind udp and dns and then say to udp that I want my custom layer to be followed instead of dns layer. Just use split_layers to unbind udp and dns and then bind your custom layer to udp and also bind your dns to your new custom layer.

Related

Custom IP protocol with ACK

I would like to explorer the possibilites of creating a custom IP procotol.
Perhaps with scapy in Python.
I've got a tunnel where I receive all packets, so would like to see if I can dismiss destination IP, ports etc. and keep an absolut minimum - but with ACK.
Anyway this is possible with scapy or similar?
I've tried some simple stuff like:
p = IP(dst="192.168.0.2")/"My payload"
But it seems not doable in that manner.
What are you trying to achieve? Forgoing transport layer port numbers limits communication to a single process on each node.
You'll need to register a protocol (number) with the IP stack to make it pass everything with that protocol to your handler. Most often, it's much easier to use UDP transport and build your own stuff on top. You can also simply number your UDP datagrams and ACK them on the application level.

generating a spoofed UDP packet in python

how can i create a spoofed UDP packet using python sockets,without using scapy library.
i have created the socket like this
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.sendto(bytes('', "utf-8"), ('192.168.1.9', 7043))# 192.168.1.9dest 7043 dest port
This is one of the first results for google searches like "spoofing udp packet python" so I am going to expand #Cukic0d's answer using scapy.
Using the scapy CLI tool (some Linux distributions package it separately to the scapy Python library ):
pkt = IP(dst="1.1.1.1")/UDP(sport=13338, dport=13337)/"fm12abcd"
send(pkt)
This sends a UDP Packet to the IP 1.1.1.1 with the source port 13338, destination port 13337 and the content fm12abcd.
If you need to a certain interface for some reason (like sending over a VPN that isn't your default route) you can use send(pkt, iface='tun0') to specify it.
One difference to #Cukic0d's answer is that this solution is more flexible by sending a layer 3 packet with send instead of a layer 2 packet with sendp. So it isn't necessary to prepend the correct Ethernet header with Ether() which can cause issues in some scenarios, e.g.:
WARNING: Could not get the source MAC: Unsupported address family (-2) for interface [tun0]
WARNING: Mac address to reach destination not found. Using broadcast.
I think you mean changing the source and destination addresses from the IP layer (on which the UDP layer is based).
To do so, you will need to use raw sockets. (SOCK_RAW), meaning that you have to build everything starting from the Ethernet layer to the UDP layer.
Honestly, without scapy, that’s a lot of hard work. If you wanted to use scapy, it would take 2 lines:
pkt = Ether()/IP(src=“...”, dst=“...”)/UDP()/...
sendp(pkt)
I really advice you to use scapy. The code itself is quite small so I don’t see a reason not to use it. It’s defiantly the easiest in python

Sending custom frame / packet in Python

I read many articles and found how to send custom packet based on IP using socket(AF_INET, SOCK_RAW, IPPROTO_RAW). But I want to send completely custom packet starting from Ethernet header. I can't send ARP packet if I can't form Ethernet header cause ARP don't based IP. Please, help!
P.S. I am on Windows 7, not Linux :(
In python, the easiest way is to use the cross-platform scapy library. It’s well known for that
Scapy
You can sniff, send.... lots of packets, add your own protocols, use existing ones... and it works on nearly all platforms. (On windows, it uses Npcap/Winpcap)
You can then build an ARP packet using
from scapy.all import *
pkt = ARP()
pkt.show()
sendp(Ether(dst=..., src=...)/pkt)
Which will create such packets
###[ ARP ]###
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:50:56:00:1e:3d
psrc= 212.83.148.19
hwdst= 00:00:00:00:00:00
pdst= 0.0.0.0
To build the packet, use the / operator
ether = Ether()
ether.src = “00:00:00:00:00:00”
ether.dst = “ff:ff:ff:ff:ff:ff”
arp = ARP()
[edit arp.psrc, arp.pdst, arp.hwsrc, arp.hwdst]
packet = ether/arp
sendp(packet) # sens packet on layer 2
Have a look at its Scapy documentation
There's no cross-platform way to do what you want, of course.
Python is just passing these values through to the underlying C API. So, on a platform with a complete BSD sockets API including the packet interface, you can just use AF_PACKET and the other appropriate flags. (I think you'd want ETH_P_ALL or ETH_P_802_2 rather than IPPROTO_RAW, or you might want SOCK_DGRAM… anyway, read your platform's man packet and figure it out based on what you actually need to do.) On Linux, at least most of these flags should be available on the SOCKET module; on other Unixes, they often don't get picked up, so you have to manually look them up in the system headers and use hardcoded constant ints in your code.
Unfortunately, if you're on Windows, this doesn't do any good. While WinSock has a feature they call TCP/IP Raw Sockets, accessed via SOCK_RAW, and recent versions of Python do expose this, it's just an emulation of a small subset of what actual BSD sockets implementations can do, and doesn't offer any way to go below the IP level (hence the name of the feature).
Microsoft's solution to this used to be that you'd write a TDI provider with the DDK, which would implement whatever protocol you wanted to expose as another WinSock protocol, and then your application-level code could just use that protocol the same way it would use, e.g., TCP. From the linked document above, it looks like this is obsolete, but the replacement seems like the same idea but with different acronyms (and, presumably, different APIs).
On the other hand, I'm pretty sure Windows already comes with protocols for ARP, ICMP, and anything other protocols needed for its usermode tools (because they obviously can't be written around raw packets). I'm just not sure how to access them.
As far as I know, the usual alternative is to use WinPcap.
While this was originally designed to be a packet capture library, it also implements a complete link-level socket interface that you can use for sending and receiving raw frames.
And there are Python wrappers for it, like WinPcapy.
So, as long as you can require that the WinPcap driver be installed, you can write ARP code, etc., on Windows in Python. It's just different from doing it on Unix.
In fact, one of the examples on the front page of WinPcapY, "Easy Packet sending", should get you started:
from winpcapy import WinPcapUtils
# Build a packet buffer
# This example-code is built for tutorial purposes, for actual packet crafting use modules like dpkt
arp_request_hex_template = "%(dst_mac)s%(src_mac)s08060001080006040001" \
"%(sender_mac)s%(sender_ip)s%(target_mac)s%(target_ip)s" + "00" * 18
packet = arp_request_hex_template % {
"dst_mac": "aa"*6,
"src_mac": "bb"*6,
"sender_mac": "bb"*6,
"target_mac": "cc"*6,
# 192.168.0.1
"sender_ip": "c0a80001",
# 192.168.0.2
"target_ip": "c0a80002"
}
# Send the packet (ethernet frame with an arp request) on the interface
WinPcapUtils.send_packet("*Ethernet*", packet.decode("hex"))

How to capture the packets that you send in Scapy?

I am sending packets using:
send(IP(dst="192.168.1.114")/fuzz(UDP()/NTP(version=4)), loop=1)
But I am not able to capture these packets in any other nearby machine (including the one with IP 192.168.1.114) which is on the same network. I am using wlan as my interface.
I also tried to sniff and then replay using scapy but I am still not able to capture those packets.
i would first try to capture the traffic on the sender machine with tcpdump while executing your program:
tcpdump -i any udp dst 192.168.1.114
if you can see the traffic leaving the source host it may be that it does not arrive on the target host. UDP packets are the first packets to be dropped by any network device and as it is the nature of UDP it wont get retransmitted. if you are sure the packet leaves the source verify if it arrives at the target:
tcpdump -i any upd dst 192.168.1.114
Another point to check is your firewall settings. It could be either on the source or target system that your firewall is blocking those requests.
I finally resolved this. Here is the checklist I made which might help others when dealing with replaying/fuzzing using scapy.
Check if all IP addresses you are dealing with are alive in the
network (use ping)
Understand the difference between send() (layer 3)and sendp() (layer 2)
If mutating existing packet make sure to
remove the checksum (using 'del') and recalculate the checksum
either using show2() or using str to convert packets to string
and then converting them back to packets
You should use Wireshark, or the sniff function in Scapy and make it pretty print the contents on the screen:
sniff(lambda x:x.show())

Can't extract individual fields from scapy packet

I've been experimenting with scapy and Python 3 and I want to use the ARP protocol to find mac addresses of computers on the network. This is my code:
>>> packet = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=IP_OF_HOST))
Then to extract the data from this packet I used the following line:
>>> packet[0][Ether].src
However for some reason this produces the error:
AttributeError: 'list' object has no attribute 'src'
Every tutorial I read used the method I used for extracting field data, why wouldn't it work for me?
It has to do with the difference between the functions srp(), and srp1() (Those being for network layer 2, for layer 3 you would use sr() and sr1() respectively).
srp() sends the packet, and saves all packets, whether they are answered or not. To get say the source MAC address I'd do this:
answered_packets, unanswered_packets = packet
for packet_i_sent, packet_i_received in answered_packets:
print(packet_i_received.src)
srp1() sends a packet, and waits for one answer, and saves only the reply packet. This means the format is different, since you don't have to deal with the unanswered packets, and my previous method would work:
print(packet.src) #The [Ether] part doesn't matter,
#that's just for looking fancier when you print(packet)
So basically I used the command srp() and tried to decode the reply like I was using srp1()

Categories