SCAPY port scanner - python

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.

Related

scapy sniffed udp packets can not be received after sending to another host

I am trying to send udp packets sniffed by scapy to another host. The host address is reachable through my default gateway.
In the host I listen to the destination port 10000 by tcpdump, but the packets are not received.
My code is this:
from scapy.all import *
class Des:
def __init__(self, port):
self.port = port
def send_packets(port):
des = Des(port)
def get_pack(packet):
pkt = packet.copy()
pkt['IP'].dst= "192.168.20.111" # address of the destination host
pkt['IP'].src = "192.168.12.111" # address of my system
pkt['UDP'].dport = des.port
pkt['Ethernet'].dst = dst_mac
pkt['Ethernet'].src = src_mac
send(pkt)
return get_pack
sniff(filter = 'udp and port 50000', prn = send_packets(10000))
If I send the packets to another host 192.168.12.112 in my network the problem still exists. In this scenario if I replace the send line with the following line, these packets will be received in the destination!
send(IP(dst='192.168.12.112')/UDP(dport=10000))
While replacing it with the following line, results in no receiving packets in host 192.168.20.111.
send(IP(dst='192.168.20.111')/UDP(dport=10000))
I searched for the problem but found no result. The firewall in the both side is disabled and scapy has the routing path as the following output.
>>> conf.route
Network Netmask Gateway Iface Output IP
0.0.0.0 0.0.0.0 192.168.12.1 vr0 192.168.12.111
192.168.12.0 255.255.255.0 0.0.0.0 vr0 192.168.12.111
Furthermore, tcpdump on my output interface shows that when using
send(IP(dst='ANY_DST)/UDP(dport=ANY_PORT))
the packets are going out, but when I send the sniffed packets, they don't!
Where did I go wrong?
May the problem be with the changed packets? They are RTP packets containing payload.
I am very new to python and scapy. Any help can be a light to move in the right direction. Thanks for your time.
My OS is FreeBSD9.2 and i am using python 2.7 and scapy (2.2.0).
if I replace the send line with the following line, these packets will
be received in the destination!
send(IP(dst='192.168.12.112')/UDP(dport=10000))
After debugging the code and checking the source of scapy, I found the reason of the above problem. When I call send(pkt), From class L3dnetSocket following function is called.
def send(self, x):
iff,a,gw = x.route()
# Rest of the code is ignored here
Here object x is of class Packet and its route function always returns None for gateway! So the packet will be send in layer 2.
def route(self):
return (None,None,None)
While when I call send(IP(dst='192.168.12.112')/UDP(dport=10000)), type of object x is class IP that it's route function returns the correct gateway.
def route(self):
dst = self.dst
if isinstance(dst,Gen):
dst = iter(dst).next()
return conf.route.route(dst)
Finally, I changed my code as the following and it works correctly :)
def send_packet(port):
des = Des(port)
def get_pack(packet):
udp = UDP()
udp.sport = packet[UDP].sport
udp.dport = des.port
udp.len = packet[UDP].len
ip = IP()
ip.version = packet[IP].version
ip.tos = packet[IP].tos
ip.len = packet[IP].len
ip.id = packet[IP].id
ip.flags = packet[IP].flags
ip.frag = packet[IP].frag
ip.ttl = packet[IP].ttl
ip.proto = packet[IP].proto
ip.dst = '192.168.12.112'
payload = packet[Raw].load
pkt = ip/udp/payload
pkt = pkt.__class__(bytes(pkt))
send(pkt)
return get_pack
Furthermore, the reason that the packets were not received in my reachable networks, was from my network :)

Problem with reading destination IP from pcap file

I am trying to read list of destination IP's from a pcap file, the problem is when I run the while loop a get this error
Traceback (most recent call last):
File "/root/PycharmProjects/pcap/pcap.py", line 10, in <module>
print(pcap[4]['IP'].show())
File "/root/venv/pcap/lib/python3.7/site-packages/scapy/packet.py", line 1171, in __getitem__
raise IndexError("Layer [%s] not found" % lname)
IndexError: Layer ['IP'] not found
When I checked Wireshark, I found that error appears because of requests made by vmware, since I wrote this code on a Kali virtual machine. Here is my code
from scapy.all import *
from nmap import *
from collections import OrderedDict
scanner = nmap.PortScanner()
pcap = rdpcap('/root/Downloads/nakerah.pcap')
ip_list = []
x = 0
while x < 4:
host_ip = pcap[x]['IP'].dst
ip_list.append(host_ip)
final_list = list(OrderedDict.fromkeys(ip_list))
x += 1
print(final_list)
The error tells you exactly what you need to know.
IndexError: Layer ['IP'] not found
One of the packets in your packet captures that does not contain an IP layer. You need to check if the IP layer exists before accessing it. For example, an ARP packet will not have an IP layer and will break your code.
Using this pcap from wireshark's sample captures, we can get the dest IPs by checking if the IP layer exists.
# print_ips.py
from scapy.all import rdpcap
ip_list = []
pkts = rdpcap('allen_test.pcap')
# Limit analysis to 20 packets for brevity
twenty_pkts = pkts[:20]
for packet in twenty_packets:
# This check is what you are missing
if 'IP' in packet:
dest_ip = packet['IP'].dst
ip_list.append(dest_ip)
print("Out of", len(twenty_packets), "packets,", len(ip_list), "were IP packets.")
print("Dest IPs", ip_list)
Running this in the shell, we get
$ python print_ips.py
WARNING: DNS decompression loop detected
Out of 20 packets, 7 were IP packets.
Dest IPs ['172.19.255.255', '172.19.255.255', '172.19.255.255', '172.19.255.255', '224.0.0.9', '172.19.0.240', '172.19.0.240']

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

Can't assign requested address : Python Multicasting

I just started with networking and am writing a very simple code for multicasting. I am still not sure about the different interfaces. Some examples used "0.0.0.0" while others have used "127.0.0.1".
Code for Server
import socket
import sys
import time
ANY = socket.gethostbyname('localhost')
S_PORT = 1501
M_ADDR = "224.168.2.9"
M_PORT = 1600
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEPORT,1)
sock.bind((ANY,S_PORT))
sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,255)
while 1:
message = raw_input("Enter message: ")
sock.sendto(message,(M_ADDR,M_PORT))
if message == "exit":
break
sock.close()
Code for Client
import socket
import time
import sys
ANY = socket.gethostbyname('localhost')
M_ADDR = "224.168.2.9"
M_PORT = 1600
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEPORT,1)
sock.bind((ANY,M_PORT))
sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,255)
status = sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(M_ADDR) + socket.inet_aton(ANY))
while 1:
data,addr = sock.recvfrom(1024)
print "Received message from " + str(addr) + " : " + data
if data == "exit":
break
sock.close()
The Client code runs properly and is waiting to receive message on the socket. But the Code Server crashes as soon as I enter any message.
Traceback (most recent call last):
File "multicast_server.py", line 17, in <module>
sock.sendto(message,(M_ADDR,M_PORT))
socket.error: [Errno 49] Can't assign requested address
What is causing this issue ?
The above code works if I use ANY = "0.0.0.0". Why is that ? What changes ?
In IPv4, 0.0.0.0 is a special address, aka INADDR_ANY, that means "bind every possible address on every interface".
So, the multicast network at 224.168.2.9, if it's reachable at all, will certainly be reachable from a socket bound to 0.0.0.0.
Meanwhile, 127.0.0.1 is a special address, aka INADDR_LOOPBACK, that means "bind localhost only on the loopback device". There's no way to reach anything but the local host itself on that socket. In particular, you can't reach your multicast network. Whether you get an ENETUNREACH, ENETDOWN, or EADDRNOTAVAIL is platform-specific, but whether it works is not—it can't possibly work.
If you want to test multicasting without testing across multiple computers, you will need to set up a loopback network with more than one address, so you can bind the client, the server, and the multicast group all to different addresses within that network.
When you use "0.0.0.0" or "" for networking in python, it opens up to any IP inbound. For your case, I would use "0.0.0.0" or "127.0.0.1" (if you are not comfortable opening up to the world.)
get your device ip address
ubuntu: ifcongfig
choose the ip address from any ethernet ,loop, wlan
replace M_ADDR with that ip address

Python Port Scanner

Am newbie to python and stuck at a point. I want to create port scanner with using only python 3 inbuilt libraries (means avoiding scapy etc) I have following code :
import socket
for i in range(1,26):
s = socket.socket()
s.settimeout(0.5)
ip = "74.207.244.221" #scanme.nmap.org
response = s.connect_ex((ip, i))
if response:
print ("%d\tclose" %i)
else:
print ("%d\topen" %i)
s.close()
Now I want to add 2 functionalities to this : that is
Distinguish between close and filtered ports . In both cases am receiving same errno in return so how can I check if I have received back a rst packet or nothing ? As far as I have tried s.recv() isn't working for this.
I want to control the number of tries (attempts), i.e I want to send only one or two syn packets. I don't want this program to send more than 2 syn packets for probes. How can this thing be achieved ?
Distinguish between close and filtered ports . In both cases am
receiving same errno in return so how can I check if I have received
back a rst packet or nothing
You've probably only checked with servers that send back a RST. Here's what I tried:
First case, normal config:
>>> os.strerror(s.connect_ex((ip, 81)))
'Connection refused'
Second, with manual iptables:
iptables -A OUTPUT -p tcp --dport 81 -j DROP
>>> os.strerror(s.connect_ex((ip, 81)))
'Resource temporarily unavailable'
I want to control the number of tries (attempts), i.e I want to send
only one or two syn packets.
I don't think there's a setsockopt TCP option exposed, but on linux there's:
net.ipv4.tcp_syn_retries
However, since you limited the timeout for the socket, all operations that don't finish within 0.5 seconds will time out. So it's likely only 1 or 2 SYNs will leave the station.
#!/usr/bin/python
import socket
s = socket.socket(socket.AF_INET, socekt.SOCK_STREAM)
host = 74.207.244.221
def portscan(port):
try:
s.connect((host,port))
return True
else:
return False
for x in range(1,255):
if portscan(x):
print('Port',x,'Is Open')

Categories

Resources