Search code examples
pythonpython-3.xsocketspacket-sniffersicmp

ICMP packet sniffing not receiving any data (Black Hat Python Book)


I came across this code snippet from the book, Black hat Python, Chapter 3. The Network: Raw Sockets and Sniffing :

import socket
import os

host = "x.x.x.x"        # Host to listen on

# Create a raw socket and bind it to the public interface
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP
else:
    socket_protocol = socket.IPPROTO_ICMP
    
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)     # We want the IP headers included in the capture
# if we're using Windows, we need to send an IOCTL
# to set up promiscuous mode
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
print(sniffer.recvfrom(65565))      # Read in a single packet

# If we're using Windows, turn off promiscuous mode
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

And when I do ping google.com the above code is supposed to capture the first ping packet, but instead waits for the packet indefinitely in the line print(sniffer.recvfrom(65565)).

I tried with host as my machine's local ip and with localhost, and tried changing the buffer size as seen in a similar question. But it did not good.

I works when I set host = "" and do ping 127.0.0.1, but not when I ping other urls.

Can somebody please tell me what is wrong?

I am using Python 3.8.2 and Ubuntu 18.04.


Solution

  • The issue is not actually with the code, it is a compatibility issue, pinging the more modern servers are done by IPv6, while the code picks only IPv4 ICMP packets. An easy solution will be to restrict the ping to IPv4 by :

    ping -4 google.com
    

    The sniffer for IPv6 only requires small changes form the IPv4 version, it is as follows :

    import socket
    import os
    
    host = ""       # Host to listen on
    
    # Create a raw socket and bind it to the public interface
    if os.name == "nt":
        socket_protocol = socket.IPPROTO_IPV6
    else:
        socket_protocol = socket.IPPROTO_ICMPV6
        
    sniffer = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket_protocol)
    sniffer.bind((host, 0))
    sniffer.setsockopt(socket.IPPROTO_IPV6, socket.IP_HDRINCL, 1)       # We want the IP headers included in the capture
    # if we're using Windows, we need to send an IOCTL
    # to set up promiscuous mode
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
    print(sniffer.recvfrom(65565))      # Read in a single packet
    
    # If we're using Windows, turn off promiscuous mode
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)