Pinging an IP range with Scapy - python

I'm attempting to write a Python script which uses the Scapy module to ping an internal IP range to determine which IP's are online. I've got this so far:
#!/usr/bin/python
from scapy.all import *
conf.verb = 0
for ip in range(0, 256):
packet = IP(dst="192.168.0." + str(ip), ttl=20)/ICMP()
reply = sr1(packet)
if "192.168." in reply.src:
print reply.src, "is online"
And the program will sit for a while doing nothing, and then if I kill it with CTRL+C I get an error message:
Traceback (most recent call last):
File "sweep.py", line 7, in <module>
if "192.168." in reply.src:
AttributeError: 'NoneType' object has no attribute 'src'
However if I try it with a single IP address, instead of a range, it works. Like this:
#!/usr/bin/python
from scapy.all import *
conf.verb = 0
packet = IP(dst="192.168.0.195", ttl=20)/ICMP()
reply = sr1(packet)
if "192.168." in reply.src:
print reply.src, "is online"
Anyone know how I can fix this problem? Or do you have any other ideas on how I can ping an IP range with Scapy, to determine which hosts are online?

You just need to ensure that reply is not NoneType as illustrated below... sr1() returns None if you get a timeout waiting for the response. You should also add a timeout to sr1(), the default timeout is quite absurd for your purposes.
#!/usr/bin/python
from scapy.all import *
TIMEOUT = 2
conf.verb = 0
for ip in range(0, 256):
packet = IP(dst="192.168.0." + str(ip), ttl=20)/ICMP()
reply = sr1(packet, timeout=TIMEOUT)
if not (reply is None):
print reply.dst, "is online"
else:
print "Timeout waiting for %s" % packet[IP].dst

You can't show reply.src field if the return of variable is null. In other words, you need to validate if the variable has return with some value (if the ping was successful). You can make an IF condition to get the .src field only when variable is not null.

FTR, Scapy supports implicit generators.
This works:
ans, unans = sr(IP(dst="192.169.0.1-255")/ICMP(), timeout=2)
Then iterate through the answers.
It is probably much better :)

Related

SCAPY port scanner

In my python for cyber security class, I have gotten tasked with creating a port scanner using Scapy. I am running python 3.9.10. here is the code.
from http.client import ResponseNotReady
from scapy.all import *
from scapy.layers.inet import IP, TCP
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
import sys
if len(sys.argv) !=4:
print('usage: %s target startport endport' % (sys.argv[0]))
sys.exit(0)
target = str(sys.argv[1])
startport = int(sys.argv[2])
endport = int(sys.argv[3])
print(f'scanning {target} for open TCP ports\n')
if startport==endport:
endport+=1
for x in range(startport,endport):
packet = IP(dst=target)/TCP(dport=x,flags='S')
response = sr1(packet,timeout=0.5,verbose=0)
if response.haslayer(TCP) and response.getlayer(TCP).flags==0x12:
print(f'Port {str(x)} is open!\n')
sr(IP(dst=target)/TCP(dport=response.sport,flags='R'),timeout=0.5,verbose=0)
print('Scan is complete!\n')
The error I am getting is essentially a none type. Here it is.
\1pyCodePrac>py 4-5Assign.py 8.8.8.8 80 80
scanning 8.8.8.8 for open TCP ports
Traceback (most recent call last):
File "E:\School\CNA 256\1pyCodePrac\4-5Assign.py", line 27, in <module>
if response.haslayer(TCP) and response.getlayer(TCP).flags==0x12:
AttributeError: 'NoneType' object has no attribute 'haslayer'
It seems to be the variable "response" and where it's defined. It's supposed to get a response, but instead, it's getting nothing. I did a print(response) with a time.sleep(3) after it, just to see, and it is coming up as None
Any help?
You cannot assume that your SYN packet will eventually be followed by a response even if you set its timeout parameter to a large value. It may indeed be the case that your SYN packet is filtered at some point between your machine and the destination host or that your SYN packet has just been ignored by the destination host or more simply that the destination host is just down.
In that case (if no response has been received) sr1 will return None. So you have to adjust your test to handle the situation where no response was received:
if response is not None and response.haslayer(TCP) and response.getlayer(TCP).flags==0x12:
Also note that your packets can be accessed like dictionaries whose keys are layers and values are payloads. So this test can be rewritten as:
if response is not None and TCP in response and response[TCP].flags == 0x12:
which is more readable IMHO.

scapy not getting HTTP requests on non-standard ports

I'm trying to get all HTTP GET/POST incoming requests.
I've found this code which seems promising, but I've noticed that it only works on standard HTTP ports. If I use another port (say 8080) scapy can't find the HTTP layer (packet.haslayer(http.HTTPRequest) == False).
This is the code:
from scapy.all import IP, sniff
from scapy.layers import http
def process_tcp_packet(packet):
'''
Processes a TCP packet, and if it contains an HTTP request, it prints it.
'''
if not packet.haslayer(http.HTTPRequest):
# This packet doesn't contain an HTTP request so we skip it
return
http_layer = packet.getlayer(http.HTTPRequest)
ip_layer = packet.getlayer(IP)
print '\n{0[src]} just requested a {1[Method]} {1[Host]}{1[Path]}'.format(ip_layer.fields, http_layer.fields)
# Start sniffing the network.
sniff(filter='tcp', prn=process_tcp_packet)
Any idea about what I'm doing wrong?
**** UPDATE ****
I got rid of scapy_http and just looked at the raw data.
I'm posting here the code I'm using - it works fine for me as I'm debugging a strange problem I'm having on Apache Solr - but your mileage may vary.
def process_tcp_packet(packet):
msg = list()
try:
if packet.dport == 8983 and packet.haslayer(Raw):
lines = packet.getlayer(Raw).load.split(os.linesep)
# GET or POST requests ?
if lines[0].lower().startswith('get /') or lines[0].lower().startswith('post /'):
# request forwarded by a proxy ?
_ = [line.split()[1] for line in lines if line.startswith('X-Forwarded-For:')]
s_ip = (_[0] if _ else packet.getlayer(IP).src)
# collect info
d_port = packet.getlayer(IP).dport
now = datetime.datetime.now()
msg.append('%s: %s > :%i -> %s' % (now, s_ip, d_port, lines[0]))
except Exception, e:
msg.append('%s: ERROR! %s' % (datetime.datetime.now(), str(e)))
pass
with open('http.log', 'a') as out:
for m in msg:
out.write(m + os.linesep)

Detecting Dash button ARP requests

I am having some trouble finding working code to find ARP requests sent out by an Amazon dash button. I tried Ted Benson's code, and also this code here, but neither seem to be working.
Ted's code:
from scapy.all import *
def arp_display(pkt):
if pkt[ARP].op == 1: #who-has (request)
if pkt[ARP].psrc == '0.0.0.0': # ARP Probe
print("ARP Probe from: " + pkt[ARP].hwsrc)
print(sniff(prn=arp_display, filter="arp", store=0, count=10))
The issue I am having is with the line scapy.all import *. I get a long list of explanation, but the last line of the error is
import dnet ImportError: No module named dnet.
The second code I tried is
import socket
import struct
import binascii
# Written by Bob Steinbeiser (https://medium.com/#xtalker)
rawSocket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
socket.htons(0x0003))
MAC = '74c24671971c'
while True:
packet = rawSocket.recvfrom(2048)
ethernet_header = packet[0][0:14]
ethernet_detailed = struct.unpack('!6s6s2s', ethernet_header)
arp_header = packet[0][14:42]
arp_detailed = struct.unpack('2s2s1s1s2s6s4s6s4s', arp_header)
# skip non-ARP packets
ethertype = ethernet_detailed[2]
if ethertype != '\x08\x06':
continue
source_mac = binascii.hexlify(arp_detailed[5])
dest_ip = socket.inet_ntoa(arp_detailed[8])
if source_mac == MAC:
print "Dash button pressed!, IP = " + dest_ip
This is the error I am getting: 'AttributeError: 'module' object has no attribute 'AF_PACKET''.
I have tried both code in python 2.7 and 3.4 and it neither work. Please let me know if there is anything I can do, or any code I can re-appropriate.
For the first example, you are probably missing the libdnet dependency, as discussed in this SO answer.
Also, note that a different approach is required to detect newer model Dash buttons (listening for DHCP requests rather than ARP requests). I describe the solution in this answer.

How to get the length of http response python scapy

So I am writing a python scapy script that will first just complete a 3 way handshake, send an http get, and then close the connection.
This is the code so far:
!/usr/bin/env python
from scapy.all import *
getStr = 'GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n'
#Send SYN
syn = IP(src="31.31.31.10",dst='31.31.31.17') / TCP(dport=80,sport=RandShort(),flags='S')
syn_ack = sr1(syn)`
#Send GET w/ ACK of server's SA
get1 = (IP(src="31.31.31.10",dst="31.31.31.17")/TCP(dport=80, sport=syn_ack[TCP].dport,seq=syn_ack[TCP].ack, ack=syn_ack[TCP].seq + 1, flags='PA')/getStr)
send (get1)
pkts = sniff(count=1,filter="host 31.31.31.17 and port 80",prn=lambda x:x.summary())
print pkts.sprintf("%TCP.len%") #Will this work? I am getting an issue here
AttributeError: 'list' object has no attribute 'sprintf'
Basically I just want to extract the len of data from the PA, which is the http response, so I can correctly generate the following sequence number to ACK the http response.
I have also tried: ans,uns=sr(get1) But I could not find a good way to get the length from this either.
I have also tried: print len(pkts.getlayer(TCP)) AttributeError: 'list' object has no attribute 'getlayer'
Any help would be appreciated
As you suspected since you have used a plural, pkts is a PacketList and not a Packet.
You can use:
for p in pkts:
if TCP in p:
print len(p[TCP].payload)
You can add and not isinstance(p[TCP].payload, NoPayload) if you want to skip packets with no data.
You can also modify the BPF filter so that it reads: "host 31.31.31.17 and tcp port 80" (add tcp).
Scapy overloads a lot of internal attributes in it's base class, one of the being __len__. Building on #Pierre's answer I would think that
for p in pkts:
print len(p)
if you need only for the length of the TCP packet and onward only, try:
for p in pkts:
print len(p[TCP])
have a look in the Scapy source at scapy/packet.py to figure you what else is overloaded by class Packet.
Do not use packet[TCP].payload alone, it includes the padding and will give you an incorrect length if the packet has padding.
Instead, you can use:
tcp_payload_len = len(pkt[TCP].payload)
if pkt.haslayer(Padding):
tcp_payload_len -= len(pkt[Padding])
see: https://github.com/secdev/scapy/issues/707

Python socket not receiving anything

I'm trying to receive a variable length stream from a camera with python, but get weird behaviour. This is Python 2.6.4 (r264:75706) on linux(Ubuntu 9.10)
The message is supposed to come with a static header followed by the size, and rest of the stream. here is the code
from socket import *
import array
import select
HOST = '169.254.0.10'
PORT = 10001
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
tcpCliSock.setblocking(0)
def dump(x):
dfile = open('dump','w')
dfile.write(x)
dfile.close
data='I'
tcpCliSock.send(data)
tcpCliSock.shutdown(1)
ready_to_read, ready_to_write, in_error = select.select(
[tcpCliSock],
[],
[],
30)
if ready_to_read == []:
print "sokadens"
data=''
while len(data)<10:
chunk = tcpCliSock.recv(1024)
print 'recv\'d %d bites'%len(data)
data=data+chunk
index=data.find('##IMJ')
if index == -1:
dump(data)
raise RuntimeError, "imahe get error"
datarr = array.array('B',data)
size=datarr[6]+datarr[7]<<8+datarr[8]<<16+datarr[9]<<24
ready_to_read, ready_to_write, in_error = select.select(
[tcpCliSock],
[],
[],
30)
if ready_to_read == []:
print "sokadens"
while len(data)<size:
chunk = tcpCliSock.recv(1024)
data=data+chunk
outfile=open('resim.jpg','w')
outfile.write(data[10:])
outfile.close
tcpCliSock.close()
with this code I either get stuck in a "recv\'d 0 bites" loop(which happens rarely)
or this:
`recv'd 0 bites`
Traceback (most recent call last):
File "client.py", line 44, in <module>
raise RuntimeError, "imahe get error"
RuntimeError: imahe get error
which is totally weird(receive 0 bytes but get out of the loop). The dumped data is erroneous, which is expected in that situation
Edit 1: the device is supposed to send a JPEG image, preceded by a 10-byte header. When(if) I get past the first loop, I need to check this header for correctness and size info. The program terminates with wrong data error, and the dump file is a bunch of binary garbage, so I have no Idea what I received at the end. I am pretty sure the device at the other side is trying to send the correct data.
You don't really know how many bytes you received, since your code is:
data=''
while len(data)<10:
chunk = tcpCliSock.recv(1024)
print 'recv\'d %d bites'%len(data)
data=data+chunk
i.e., you're receiving bytes in chunk, but what you're printing is len(data) before you update data. So of course it will print 0 the first time, always -- then it will update data and exit if the chunk was at least 10 bytes.
This info is not sufficient to debug your problem, but printing len(chunk), and len(data) upon exiting the loop, can't hurt the attempt to understand what's going on. Also, what's in dump when you exit with the imahe get error message?
Problem is resolved, interestingly shutdown(1) was causing the problem, the other side does not like http style shutdowns. There are also obvious typos and missing checks but they are not the problem.

Categories

Resources