Python Packet Sniffer and Sockets for Noobs - python

So I have some questions about sockets and sniffer programming...
I've just started programming and have a project where I would like to use information that is sent across my network.
I tried watching several videos on youtube that talk about this process a little, and tried to find better material to research it further, but I haven't been able to find a source that makes sense to me.
The code I included came from a video on youtube and seemed to make sense as they explained it, but I guess he might have been using Linux or something else because Windows did not support AF_PACKET. After some research I found that people use AF_INET, but I got the error:
OSError: [WinError 10043] The requested protocol has not been configured into the system, or no implementation for it exists
Is there a place or a way someone might be able to explain sockets a little bit for me? I don't plan to use windows for the final version of this project, and I also plan to modify it for bluetooth in the future, so I would like to learn the reasoning behind things if I can find a way to do that.
`
import socket
import struct
import textwrap
def main():
conn = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.ntohs(3))
while True:
raw_data, addr = conn.recvfrom(65535)
dest_mac, src_mac, eth_proto, data = ethernet_frame(raw_data)
print('\nEthernet Frame:')
print('Destination: {}, Source: {}, Protocol: {}'.format(dest_mac, src_mac, eth_proto, data[:14]))
#unpack ethernet frame
def ethernet_frame(data):
dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])
return get_mac_addr(dest_mac), get_mac_addr(src_mac), socket.htons(proto), data[14:]
#Get Mac Address
def get_mac_addr(bytes_addr):
bytes_str = map('{:02x}'.format, bytes_addr)
return ':'.join(bytes_str).upper()
main()
`

The OSes on which you use a socket for packet sniffing are:
Linux
Irix
Windows is not on that list (neither are anything with "BSD" in the name, OS X, Solaris, HP-UX, AIX, etc.). Linux and Irix both happen to use sockets to do sniffing, but that's just their choice (and they didn't choose the same type of socket, they just happened to choose sockets).
If you want to write a sniffer, you're probably best advised to use a wrapper around libpcap/WinPcap, and let them deal with the painful details of the way packet sniffing is done on a particular operating system. Wrappers for Python include pylibpcap and pcapy; I don't know whether either of them work on Windows with WinPcap.
(Note that you are not guaranteed to get Ethernet headers on sniffed packets; you should call pcap_datalink(), or whatever the wrapper's equivalent is, and check its return value - if it's not DLT_EN10MB, or the wrapper's equivalent, you won't be getting Ethernet headers.)
AF_INET raw sockets, on any platform, aren't going to give you Ethernet headers. I don't know what you'll get with a protocol argument of 3 - 3 is the Internet protocol number for GGP, as per RFC 823 Appendix A, and that protocol is ancient and not used as far as I know; you'll probably end up with a socket on which you can send GGP packets and from which you can receive GGP packets, for what that's worth (which is not much). (Also, the arguments to the socket() call in C are in host byte order, and Python probably works the same, so you probably don't want that socket.ntohs() in there, not that it'll make a difference.)

Related

communciation with Fluke endurance pyrometer (profinet/RS485) via python

I have a Fluke Endurance pyrometer (and a pretty flimsy user guide) which has output via an ethernet cable which I'm then connecting to my computer via a USB3.0 to Gigabit ethernet adaptor (windows control panel tells me it's working). According to the guide, communication should be possible via RS485 and profinet. But it's unclear to me if both are possible through the same cable.
My initial plan was to try to use snap7 to try profinet communication, but when I try:
import snap7
plc = snap7.client.Client()
plc.connect("192.168.42.132",0,1)
I get
snap7.snap7exceptions.Snap7Exception: b' TCP : Unreachable peer'
I can ping the device at that IP address.
So my questions are:
any ideas why I can ping, but snap7 seems to have problems connecting?
if I can't get profinet communication to work, any chance I can get RS485 communication somehow through the ethernet cable? Everything I looked up on that talks about having hardware that does the conversion, so I'm assuming it's more than just looking for the right signal format on the right wires. I can't find anything in the manual that turns on/off one form of communication, and it looks like from the menu they are both active, if that's possible.
Better yet, if anyone has communicated with this device in python, any suggestions would be greatly appreciated!
Thanks,
Matthew
Update:
In case anyone else has a similar problem, I solved this by using socket and ASCII commands like this:
import socket
pyrometerIP = "192.168.42.132"
pyrometerPORT = 6363
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(3) # 3 second timeout on commands
s.connect((pyrometerIP, pyrometerPORT))
MESSAGE=bytes('U=C\r', 'ascii')
s.send(MESSAGE)
There are couple of reasons why, some of the reason are listed below:
The rack and slot available to over TIA portal.
You cannot reach the PLC on that IP
You do not have the two DLL file for snap7 module which can be found on https://sourceforge.net/projects/snap7/files/1.4.2/

Sending raw IP Traffic with Python: Detect MTU

How can I implement a manual MTU discovery with Python?
From: https://networkengineering.stackexchange.com/a/28988/23983
Send a ping to a target, in my example, I'll use Google's DNS server (8.8.8.8). Set your DF bit in your ping to on, to prevent your ping from being fragmented. Set your packet size to some large number, or the standard MTU of 1500. Note that some ping implementations set the size of just the payload, which means you have to account for the 8 byte ICMP header, and 20 byte IP header.
I'd like to add that you could to this with raw sockets as well.
scapy is a nice abstraction layer that does much of the magic for you, but in all fairness if you're going to go low you could go all the way for the learning experience. (Note that raw sockets require elevated permissions in most modern OS'es, and as you go deeper implementations may vary from Windows and Linux.)
import socket
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
This will give you a raw packet based socket that literally delivers all the frames for you. It varies a bit between Windows and Linux but I'll stick to Linux in this answer. Also note that all outgoing packets might not get picked up by this socket, if you need that functionality (sniffing stuff) consider searching for promiscuous mode related hits.
All you need to do now is treat each packet as the segments they come or go in, for instance - unpacking a ethernet and IP frame would look something like this:
frame, meta = s.recvfrom(65565)
print(frame, meta)
ethernet = frame[0:14]
ethernet_segments = struct.unpack("!6s6s2s", ethernet)
mac_source, mac_dest = (binascii.hexlify(mac) for mac in ethernet_segments[:2])
ip = frame[14:34]
ip_segments = struct.unpack("!12s4s4s", ip)
ip_source, ip_dest = (socket.inet_ntoa(section) for section in ip_segments[1:3])
print('MAC Source:', b':'.join(mac_source[i:i+2] for i in range(0, len(mac_source), 2)))
print('MAC Dest:', b':'.join(mac_dest[i:i+2] for i in range(0, len(mac_dest), 2)))
print('IP Source:', ip_source)
print('IP Dest:', ip_dest)
Payloading would be "easy", considering you're building the packets yourself.
All be it not the most conventional of ways or the initially fastest way, but you'd be able to implement whatever you wanted.
Sending is just as easy, use struct and have a look at the many ICMP examples out there, including those with checksum calculations:
https://www.g-loaded.eu/2009/10/30/python-ping/
https://gist.github.com/pklaus/856268
Regarding MTU, this is logic you'd have to implement yourself because there's no pre-built library that does this that I'm aware of.
But this is my contribution to sending raw IP traffic with Python.
Sending anything raw in python, you are looking at using at using scapy.
For info on how to send ping messages:
http://www.secdev.org/projects/scapy/doc/usage.html#icmp-ping
To set the DF Flag:
IP(flags='DF')
For how to adjust the particular size so that you can simulate fragmentation:
Adding payload in packet (scapy)
Putting it all together:
data = "x" * 1473
ans,unans = sr(IP(dst="<target_ip>", flags='DF' )/ICMP() / Raw(load=data))
If you aren't actually that interested in creating these things raw, this question is a dup of: How to find mtu value of network through code(in python)?

How to assign IP address to interface in python?

I have python script that set the IP4 address for my wireless and wired interfaces. So far, I use subprocess command like :
subprocess.call(["ip addr add local 192.168.1.2/24 broadcast 192.168.1.255 dev wlan0"])
How can I set the IP4 address of an interface using python libraries?
and if there is any way to get an already existing IP configurations using python libraries ?
With pyroute2.IPRoute:
from pyroute2 import IPRoute
ip = IPRoute()
index = ip.link_lookup(ifname='em1')[0]
ip.addr('add', index, address='192.168.0.1', mask=24)
ip.close()
With pyroute2.IPDB:
from pyroute2 import IPDB
ip = IPDB()
with ip.interfaces.em1 as em1:
em1.add_ip('192.168.0.1/24')
ip.release()
Set an address via the older ioctl interface:
import socket, struct, fcntl
SIOCSIFADDR = 0x8916
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def setIpAddr(iface, ip):
bin_ip = socket.inet_aton(ip)
ifreq = struct.pack('16sH2s4s8s', iface, socket.AF_INET, '\x00' * 2, bin_ip, '\x00' * 8)
fcntl.ioctl(sock, SIOCSIFADDR, ifreq)
setIpAddr('em1', '192.168.0.1')
(setting the netmask is done with SIOCSIFNETMASK = 0x891C)
Ip addresses can be retrieved in the same way: Finding local IP addresses using Python's stdlib
I believe there is a python implementation of Netlink should you want to use that over ioctl
You have multiple options to do it from your python program.
One could use the ip tool like you showed. While this is not the best option at all this usualy does the job while being a little bit slow and arkward to program.
Another way would be to do the things ip does on your own by using the kernel netlink interface directly. I know that libnl has some experimental (?) python bindings. This may work but you will have to deal with a lot of low level stuff. I wouldn't recommend this way for simple "set and get" ips but it's the most "correct" and reliable way to do so.
The best way in my opinion (if you only want to set and get ips) would be to use the NetworkManagers dbus interface. While this is very limited and may have its own problems (it might behave not the way you would like it to) this is the most straight forward way if the NetworkManager is running anyway.
So, choose the libnl approach if you want to get your hands dirty, it's clearly superior but also way more work. You may also get away with the NetworkManager dbus interface, depending on your needs and general system setup. Otherwise you can just leave it that way.

How to send ICMP packet through sockets?

I'm trying to send a message through ICMP packets but I don't know how to do it.
This is the code I currently have, but obviously doesn't work:
s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
s.setsockopt(IPPROTO_IP, IP_HDRINCL, 1)
s.settimeout(3.0)
s.sendto("Hello!" + "\r\n", (server, 7))
msg = s.recvfrom(buff_size)
s.close()
I have to receive an answer from server if string "Hello!" is sent, but I don't get it.
I suppose, that "Hello!" string will be encapsulated into Data field:
In order to construct an ICMP packet, you have to create the whole packet yourself using a raw socket. The struct module is useful for this.
Secondly, in order to even use raw sockets in the first place, you need to have permission to do so—you should be running as root (I know this is a sufficient condition, but I'm not 100% certain that it's a necessary condition). The ping(1) executable is able to do this because it's a setuid executable that runs as root when you run it. Since scripts cannot be made setuid on Linux, you'll have to make a wrapper setuid program in C that just executes your Python script.
I don't think that SOCK_RAW is going an ICMP datagram for you just because you set the protocol field to IPPROTO_ICMP! You have to construct the packet yourself.
Take a look at the source of ping.
There are (at least) two popular packages that provide ping in GNU/Linux operating systems. One is netkit and the other iputils. (netkit-combo is a tarball which has all the netkit utilities in one: telnet, FTP, ...) The *BSD guys probably have their own.

How can I perform a ping or traceroute using native python?

I would like to be able to perform a ping and traceroute from within Python without having to execute the corresponding shell commands so I'd prefer a native python solution.
If you don't mind using an external module and not using UDP or TCP, scapy is an easy solution:
from scapy.all import *
target = ["192.168.1.254"]
result, unans = traceroute(target,l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="www.google.com")))
Or you can use the tcp version
from scapy.all import *
target = ["192.168.1.254"]
result, unans = traceroute(target,maxttl=32)
Please note you will have to run scapy as root in order to be able to perform these tasks or you will get:
socket.error: [Errno 1] Operation not permitted
Running interpreters as root is often frowned upon on security grounds (and of course you DO need to have root permission to access the "raw" socked as needed by the ICMP specs of ping and traceroute!), but if you have no problems with that it's not hard -- e.g., this post(dead?) or this post give a workable ping, and Jeremy Hylton's old page has still-usable underlying code for ICMP (both ping and traceroute) though it's written for very old Python versions and needs a litte facelift to shine with modern ones -- but, the concepts ARE all there, in both the URLs I gave you!
The Webb Library is very handy in performing all kinds of web related extracts...and ping and traceroute can be done easily through it. Just include the URL you want to traceroute to:
import webb
webb.traceroute("your-web-page-url")
If you wish to store the traceroute log to a text file automatically, use the following command:
webb.traceroute("your-web-page-url",'file-name.txt')
Similarly a IP address of a URl (server) can be obtained with the following lines of code:
print(webb.get_ip("your-web-page-url"))
Hope it helps!
The mtrpacket package can be used to send network probes, which can perform either a ping or a traceroute. Since it uses the back-end to the mtr commandline tool, it doesn't require that your script run as root.
It also uses asyncio's event loop, so you can have multiple ongoing traceroutes or pings simultaneously, and deal with their results as they complete.
Here is a Python script to traceroute to 'example.com':
import asyncio
import mtrpacket
async def trace():
async with mtrpacket.MtrPacket() as mtr:
for ttl in range(1, 256):
result = await mtr.probe('example.com', ttl=ttl)
print(result)
if result.success:
break
asyncio.get_event_loop().run_until_complete(trace())
The loop with 'ttl' is used because the 'time-to-live' of an outgoing packet determines the number of network hops the packet will travel before expiring and sending an error back to the original source.
you might want to check out the scapy package. it's the swiss army knife of network tools for python.
ICMP Ping is standard as part of the ICMP protocol.
Traceroute uses features of ICMP and IP to determine a path via Time To Live values. Using TTL values, you can do traceroutes in a variety of protocols as long as IP/ICMP work because it is the ICMP TTL EXceeded messages that tell you about the hop in the path.
If you attempt to access a port where no listener is available, by ICMP protocol rules, the host is supposed to send an ICMP Port Unreachable message.
I wrote a simple tcptraceroute in python which does not need root privileges http://www.thomas-guettler.de/scripts/tcptraceroute.py.txt
But it can't display the IP addresses of the intermediate hops. But sometimes it is useful, since you can guess where the blocking firewall is: Either at the beginning or at the end of the route.

Categories

Resources