Last active
July 4, 2023 19:55
-
-
Save joncutrer/862488b349a8faea631f6b521fae6c79 to your computer and use it in GitHub Desktop.
This script listens for DHCP packets using scapy to learn when LAN hosts request IP addresses from DHCP Servers. Learn more about this script at https://jcutrer.com/python/scapy-dhcp-listener
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
"""scapy-dhcp-listener.py | |
Listen for DHCP packets using scapy to learn when LAN | |
hosts request IP addresses from DHCP Servers. | |
Copyright (C) 2018 Jonathan Cutrer | |
License Dual MIT, 0BSD | |
""" | |
from __future__ import print_function | |
from scapy.all import * | |
import time | |
__version__ = "0.0.3" | |
# Fixup function to extract dhcp_options by key | |
def get_option(dhcp_options, key): | |
must_decode = ['hostname', 'domain', 'vendor_class_id'] | |
try: | |
for i in dhcp_options: | |
if i[0] == key: | |
# If DHCP Server Returned multiple name servers | |
# return all as comma seperated string. | |
if key == 'name_server' and len(i) > 2: | |
return ",".join(i[1:]) | |
# domain and hostname are binary strings, | |
# decode to unicode string before returning | |
elif key in must_decode: | |
return i[1].decode() | |
else: | |
return i[1] | |
except: | |
pass | |
def handle_dhcp_packet(packet): | |
# Match DHCP discover | |
if DHCP in packet and packet[DHCP].options[0][1] == 1: | |
print('---') | |
print('New DHCP Discover') | |
#print(packet.summary()) | |
#print(ls(packet)) | |
hostname = get_option(packet[DHCP].options, 'hostname') | |
print(f"Host {hostname} ({packet[Ether].src}) asked for an IP") | |
# Match DHCP offer | |
elif DHCP in packet and packet[DHCP].options[0][1] == 2: | |
print('---') | |
print('New DHCP Offer') | |
#print(packet.summary()) | |
#print(ls(packet)) | |
subnet_mask = get_option(packet[DHCP].options, 'subnet_mask') | |
lease_time = get_option(packet[DHCP].options, 'lease_time') | |
router = get_option(packet[DHCP].options, 'router') | |
name_server = get_option(packet[DHCP].options, 'name_server') | |
domain = get_option(packet[DHCP].options, 'domain') | |
print(f"DHCP Server {packet[IP].src} ({packet[Ether].src}) " | |
f"offered {packet[BOOTP].yiaddr}") | |
print(f"DHCP Options: subnet_mask: {subnet_mask}, lease_time: " | |
f"{lease_time}, router: {router}, name_server: {name_server}, " | |
f"domain: {domain}") | |
# Match DHCP request | |
elif DHCP in packet and packet[DHCP].options[0][1] == 3: | |
print('---') | |
print('New DHCP Request') | |
#print(packet.summary()) | |
#print(ls(packet)) | |
requested_addr = get_option(packet[DHCP].options, 'requested_addr') | |
hostname = get_option(packet[DHCP].options, 'hostname') | |
print(f"Host {hostname} ({packet[Ether].src}) requested {requested_addr}") | |
# Match DHCP ack | |
elif DHCP in packet and packet[DHCP].options[0][1] == 5: | |
print('---') | |
print('New DHCP Ack') | |
#print(packet.summary()) | |
#print(ls(packet)) | |
subnet_mask = get_option(packet[DHCP].options, 'subnet_mask') | |
lease_time = get_option(packet[DHCP].options, 'lease_time') | |
router = get_option(packet[DHCP].options, 'router') | |
name_server = get_option(packet[DHCP].options, 'name_server') | |
print(f"DHCP Server {packet[IP].src} ({packet[Ether].src}) " | |
f"acked {packet[BOOTP].yiaddr}") | |
print(f"DHCP Options: subnet_mask: {subnet_mask}, lease_time: " | |
f"{lease_time}, router: {router}, name_server: {name_server}") | |
# Match DHCP inform | |
elif DHCP in packet and packet[DHCP].options[0][1] == 8: | |
print('---') | |
print('New DHCP Inform') | |
#print(packet.summary()) | |
#print(ls(packet)) | |
hostname = get_option(packet[DHCP].options, 'hostname') | |
vendor_class_id = get_option(packet[DHCP].options, 'vendor_class_id') | |
print(f"DHCP Inform from {packet[IP].src} ({packet[Ether].src}) " | |
f"hostname: {hostname}, vendor_class_id: {vendor_class_id}") | |
else: | |
print('---') | |
print('Some Other DHCP Packet') | |
print(packet.summary()) | |
print(ls(packet)) | |
return | |
if __name__ == "__main__": | |
sniff(filter="udp and (port 67 or 68)", prn=handle_dhcp_packet) | |
Gives this error
File "scapy-dhcp-listener.py", line 45
print(f"Host {hostname} ({packet[Ether].src}) asked for an IP")
^
SyntaxError: invalid syntax
This is because the script above uses f strings which were introduced in Python 3.6 - if your python is <3.6 you will get an error.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Gives this error
File "scapy-dhcp-listener.py", line 45
print(f"Host {hostname} ({packet[Ether].src}) asked for an IP")
^
SyntaxError: invalid syntax