ARP request without reply raw sockets python - python

I'm trying to get a arp reply after send ARP request but the reply is not comming.
I had a look to wireshark for the results and i think he does the broadcast to the network, but no reply show up...
In results of wireshark the MAC addr of sender and receiver is do not correspond to the real MAC addr, im bealive i'm not packing this right but i dont understand why.
need help...
#!/usr/bin/env python3
import struct
import socket
raw = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0806))
raw.bind(("wlp3s0", socket.htons(0x0806)))
mac_local = b"ff:ff:ff:ff:ff:ff" # mac de quem envia request
ip_local = "192.168.1.7" # ip de quem envia request
mac_dest = b"00:00:00:00:00:00" # mac de quem recebe request
ip_dest = "192.168.1.2" # ip de quem recebe request
# Ethernet Header
protocol = 0x0806 # 0x0806 protocol to ARP
ethernet_header = struct.pack("!6s6sH", mac_dest, mac_local, protocol)
# ARP header
type_hardware = 1
type_protocol = 0x0800 # IPV4
size_addr_hardware = 6 # Refere ao tamanho do endereço do MAC que é
48 bits == 6 bytes
size_addr_protocol = 4 # Refere ao tamanho do endereço do ipv4 que é
32 bits == 4 bytes
operation = 1 # 1 = request / 2 = Reply
source_ip = socket.inet_aton(ip_local)
dest_ip = socket.inet_aton(ip_dest)
arp_addr = struct.pack("!HHBBH6s4s6s4s", type_hardware, type_protocol,
size_addr_hardware, size_addr_protocol, operation,
mac_local, source_ip, mac_dest, dest_ip)
pkt = ethernet_header + arp_addr
cont = 0
while cont < 6:
raw.send(pkt)
cont +=1
enter image description here
enter image description here

mac_dest and mac_local are definitely not right. You've just created a byte string with the ASCII value. Each of those is 17 bytes long. And you're just taking the first 6 of those 17 bytes for each of the addresses.
They should be something like this instead:
mac_dest = b'\x00\x00\x00\x00\x00\x00'
mac_local = b'\xff\xff\xff\xff\xff\xff'
Check that the length of the byte string before the struct.pack call is exactly six bytes.
Also, not sure what you're trying to do, but I doubt it makes sense to use the all-zero hardware address as a destination address. Pretty sure no one will receive that as it would be a unicast to an address that no one has. The opposite might be helpful (send to the broadcast address from all-zero) -- I think that's standard for ARP probes.

I'm somehow confused about the accepted answer.
Is MAC 00:00:00: 00:00:00 not a valid MAC from XEROX?
https://hwaddress.com/company/xerox-corporation/
When I see ARP-request captured with e.g wireshark
then mac adresses in requests are always like
MAC_DST: FF:FF:FF:FF:FF:FF //mac broadcast
MAC_SRC: MAC-address of requester
the mac adresses in reply would then be
MAC_DST: MAC-adress of requester
MAC_SRC: MAC-address of replier

Related

Python sending three-way handshake raw socket

I'm trying to code a python script through which I want to send packets from one host (desktop ubuntu) to a server (Ubuntu server) using multiple source addresses. I decided that would be best to use raw sockets and define my own TCP and IP header. I succeed to send the SYN packet and also get a response with the SYN-ACK packet but then my host doesn't respond with the ACK packet but with the RST packet. This is the first problem with I met in my solution. After the beginning of a three-way handshake I would like to send an HTTP GET keep-alive request. Does anyone know how to do it? Or does anyone know a better solution or library to use?
def send_raw_socket():
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
version_ihl_type_of_service = b'\x45\x00'
total_length = b'\x00\x3c'
Indentification = b'\xab\xcd'
flags_fragment_offset = b'\x00\x00'
TTL_protocol = b'\x40\x06'
Dest_add = b'\x0a\x0a'
Dest_add2 = b'\x1e\x03'
Src_add = b'\x0a\x0a'
src_add2 =b'\x0a\x0a'
Header_Checksum = my_ip_header_checksum(version_ihl_type_of_service, total_length, Indentification, flags_fragment_offset, TTL_protocol, Src_add,src_add2, Dest_add, Dest_add2)
IP_protocol = b'\x00\x06'
TCP_header_lenght = b'\x00\x14'
src_port = (int(src_port.hex(), 16)+49607).to_bytes(2, byteorder='big')
dest_port = b'\x1f\x95' # Source Port | Destination Port
seq_n1 = b'\x00\x00'
seq_n2 = b'\x00\x00' # Sequence Number
ack_n1 = b'\x00\x00'
ack_n2 = b'\x00\x00' # Acknowledgement Number
do_res = b'\x50\x02'
flags_win_s = b'\x71\x10' # Data Offset, Reserved, Flags | Window Size
checksum= tcp_header_checksum(IP_protocol, Src_add, src_add2, Dest_add, Dest_add2, TCP_header_lenght, src_port, dest_port, seq_n1, seq_n2, ack_n1, ack_n2, do_res, flags_win_s)
u_pinter= b'\x00\x00' # Checksum | Urgent Pointer
packet = version_ihl_type_of_service+total_length+Indentification+flags_fragment_offset+TTL_protocol+Header_Checksum+Src_add+src_add2+Dest_add+Dest_add2+src_port+dest_port+seq_n1+seq_n2+ack_n1+ack_n2+do_res+flags_win_s+checksum+u_pinter
s.sendto(packet, ('10.10.30.3',8085))
Wireshark three-way handshake

Identifying connected Device Router on Local Area Network using Python

So I have 4 routers and 1 PC with 4 devices that connect to the closest router. So What I want to do is write a script in python that is running on the PC (Windows) and that PC is connected to 1 main router and that router connects to the other 3 routers.
1 PC -> Main Router (WiFi Enabled) -> 3 Other Routers (WiFi Enabled)
The bracelet (a WiFi enabled device) will connect to the nearest WiFi Router available of the 4 routers. I want to detect to which router is that bracelet connected to via a Python script.
I can make the Bracelet's IP Address Static as well but I was hoping to identify them using the MAC Address.
I can edit the routers configuration via web-page if needed.
Do ask for any clarifications if needed.
Hoping to hear soon.
Edit_1: Not to be confused with me asking for the whole code, I am in search of the right functions that can be used for this process and what are the possible configurations that I should set the router to.
Edit_2: Below is a sample code which allows for pinging IP's. So how can I get their MAC address (function) and also ping to other access points? I can alter properties of the routers.
import socket
from datetime import datetime
net = input("Enter the IP address: ") #e.g. is 192.168.18.1
net1 = net.split('.')
a = '.'
net2 = net1[0] + a + net1[1] + a + net1[2] + a
st1 = int(input("Enter the Starting Number: ")) #e.g. 1
en1 = int(input("Enter the Last Number: ")) #e.g. 10
en1 = en1 + 1
t1 = datetime.now()
def scan(addr):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket.setdefaulttimeout(1)
result = s.connect_ex((addr,135))
if result == 0:
return 1
else :
return 0
def run1():
for ip in range(st1,en1):
addr = net2 + str(ip)
if (scan(addr)):
print (addr , "is live")
run1()
t2 = datetime.now()
total = t2 - t1
print ("Scanning completed in: " , total)
Edit_3: This searches out the MAC Address as well.
from scapy.all import ARP, Ether, srp
target_ip = "192.168.18.1/24"
# IP Address for the destination
# create ARP packet
arp = ARP(pdst=target_ip)
# create the Ether broadcast packet
# ff:ff:ff:ff:ff:ff MAC address indicates broadcasting
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
# stack them
packet = ether/arp
result = srp(packet, timeout=3, verbose=0)[0]
# a list of clients, we will fill this in the upcoming loop
clients = []
for sent, received in result:
# for each response, append ip and mac address to `clients` list
clients.append({'ip': received.psrc, 'mac': received.hwsrc})
# print clients
print("Available devices in the network:")
print("IP" + " "*18+"MAC")
for client in clients:
print("{:16} {}".format(client['ip'], client['mac']))
if (client['mac'] == "f4:f5:e8:37:67:92"): #Testing mac address filter
print("Success")

Network Scanner build using Scapy only scans itself

Below is the code of NetworkScanner that I tried to build as my first Python project.
#!/usr/bin/env python
import scapy.all as scapy
import optparse
def scan(ip):
packet1 = scapy.ARP(pdst=ip)
etherpacket = scapy.Ether(dst='ff:ff:ff:ff:ff:ff')
broadcast_packet = etherpacket / packet1
ans = scapy.srp(broadcast_packet, timeout=60, verbose=False)[0]
ret_list = list()
for item in ans:
dic = {}
dic['ip'] = item[1].pdst
dic['mac'] = item[1].hwdst
ret_list.append(dic)
print(ret_list)
return ret_list
def printfun(returnlist):
print("IP\t\t\tMAC Address\n----------------------------------------------")
for elem in returnlist:
print(elem["ip"] + "\t\t" + elem["mac"])
def getip():
parser = optparse.OptionParser()
parser.add_option('-i', "--ip", dest = 'received_ip', help="Please enter the ip you want to scan")
(option, arguments) = parser.parse_args()
return option.received_ip
ip = getip()
if ip:
result = scan(ip)
printfun(result)
else:
print("no ip given")
Now I did follow some tutorials and learned to build parallelly and it seems right to me but I am not good.
but when I execute the program, it only scans the IP of virtual Host itself on which it is executed.
/PycharmProjects/Networkscanner$ sudo python networkscanner.py -i 192.168.1.1/24
[{'ip': '192.168.1.205', 'mac': '08:00:27:1f:30:76'}, {'ip': '192.168.1.205', 'mac': '08:00:27:1f:30:76'}]
IP MAC Address
----------------------------------------------
192.168.1.205 08:00:27:1f:30:76
192.168.1.205 08:00:27:1f:30:76
when I use the inbuild network scanner of python it gives these results.
Currently scanning: Finished! | Screen View: Unique Hosts
5 Captured ARP Req/Rep packets, from 4 hosts. Total size: 300
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
192.168.1.1 a0:47:d7:36:2a:c0 2 120 Best IT World (India) Pvt Lt
192.168.1.203 e4:42:a6:30:93:64 1 60 Intel Corporate
192.168.1.205 30:b5:c2:10:05:3b 1 60 TP-LINK TECHNOLOGIES CO.,LTD
192.168.1.207 30:b5:c2:10:05:3b 1 60 TP-LINK TECHNOLOGIES CO.,LTD
Edit:
I tried the Monitor mode, but that does not help.
I also tried to run it on main windows with also an external WiFi adaptor, still same issue
can someone please assist what is wrong in my code?
Determining the problem
If we name variables more appropriately, it becomes apparent what the problem is:
for sent_recv in ans:
dic = {}
return_packet = sent_recv[1]
dic['ip'] = return_packet.pdst
dic['mac'] = return_packet.hwdst
ret_list.append(dic)
Each item is a tuple of a sent packet and a received packet. Naming it as such helps to identify it.
The 2nd element of sent_recv is the returned packet. Let's name it as such.
The destination IP and MAC address of the returning packet are going to be that of our machine. This makes sense in the context of your output: You get your own IP/MAC for every response.
So if we change it to the src IP/MAC of the returning packet, we'll get the information we're after:
for sent_recv in ans:
dic = {}
return_packet = sent_recv[1]
# Difference is dst => src here
dic['ip'] = return_packet.psrc
dic['mac'] = return_packet.hwsrc
ret_list.append(dic)
Note: An ARP packet should not take 60s to return - it's more on the scale of 10-100ms depending on network size. A timeout of 2s is more appropriate here.
Testing the fix
Running this with modified code, we get the desired result:
$ python script.py -i "192.168.1.0/24"
[{'ip': '192.168.1.48', 'mac': '00:1b:78:20:ee:40'},
{'ip': '192.168.1.67', 'mac': '6c:33:a9:42:6a:18'},
...
IP MAC Address
----------------------------------------------
192.168.1.48 00:1b:78:20:ee:40
192.168.1.67 6c:33:a9:42:6a:18
...

By scapy, update packet TCP Sequence and Acknowledge number and IP layer length automatically when packet edited manually

I am reading the packets from a pcap file and inserting some unique id in the TCP payload, So the IP layer length and TCP Sequence and Acknowledge number need to update. Is it possible to do this by scapy?
for pkt in pkt_reader_pcap:
if IP not in pkt:
continue
if TCP not in pkt:
continue
newp = pkt.payload
newp[IP].dst = 1.2.3.4
newp[IP].src = 1.2.3.4
del newp[IP].len
del newp[IP].chksum
# if pkt has TCP layer, insert field before Host to the payload
field = "Id: 1234\r\n"
insert = field + 'Host: '
http_content = newp.getlayer(Raw).load
http_content = http_content.replace(b"Host: ", insert.encode())
newp[Raw].load = http_content
newp[TCP].chksum = None
## manually updated length of IP header which works fine, but it should happen
## automatically or by some method of scapy
newp[IP].len += len(field)
I set the TCP/IP checksum to None, but still, the Seq/Ack number is not updated. Is there any way to achieve the same?

Receive UDP packet from specific source

I am trying to measure the responses back from DNS servers. Making a sniffer for a typical DNS response that is less than 512 bytes is no big deal. My issue is receiving large 3000+ byte responses - in some cases 5000+ bytes. I haven't been able to get a socket working that can receive that data reliably. Is there a way with Python sockets to receive from a specific source address?
Here is what I have so far:
import socket
import struct
def craft_dns(Qdns):
iden = struct.pack('!H', randint(0, 65535))
QR_thru_RD = chr(int('00000001', 2)) # '\x01'
RA_thru_RCode = chr(int('00100000', 2)) # '\x00'
Qcount = '\x00\x01' # question count is 1
ANcount = '\x00\x00'
NScount = '\x00\x00'
ARcount = '\x00\x01' # additional resource count is 1
pad = '\x00' #
Rtype_ANY = '\x00\xff' # Request ANY record
PROtype = '\x00\x01' # Protocol IN || '\x00\xff' # Protocol ANY
DNSsec_do = chr(int('10000000', 2)) # flips DNSsec bit to enable
edns0 = '\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00' # DNSsec disabled
domain = Qdns.split('.')
quest = ''
for x in domain:
quest += struct.pack('!B', len(x)) + x
packet = (iden+QR_thru_RD+RA_thru_RCode+Qcount+ANcount+NScount+ARcount+
quest+pad+Rtype_ANY+PROtype+edns0) # remove pad if asking <root>
return packet
def craft_ip(target, resolv):
ip_ver_len = int('01000101', 2) # IPvers: 4, 0100 | IP_hdr len: 5, 0101 = 69
ipvers = 4
ip_tos = 0
ip_len = 0 # socket will put in the right length
iden = randint(0, 65535)
ip_frag = 0 # off
ttl = 255
ip_proto = socket.IPPROTO_UDP # dns, brah
chksm = 0 # socket will do the checksum
s_addr = socket.inet_aton(target)
d_addr = socket.inet_aton(resolv)
ip_hdr = struct.pack('!BBHHHBBH4s4s', ip_ver_len, ip_tos, ip_len, iden,
ip_frag, ttl, ip_proto, chksm, s_addr, d_addr)
return ip_hdr
def craft_udp(sport, dest_port, packet):
#sport = randint(0, 65535) # not recommended to do a random port generation
udp_len = 8 + len(packet) # calculate length of UDP frame in bytes.
chksm = 0 # socket fills in
udp_hdr = struct.pack('!HHHH', sport, dest_port, udp_len, chksm)
return udp_hdr
def get_len(resolv, domain):
target = "10.0.0.3"
d_port = 53
s_port = 5353
ip_hdr = craft_ip(target, resolv)
dns_payload = craft_dns(domain) # '\x00' for root
udp_hdr = craft_udp(s_port, d_port, dns_payload)
packet = ip_hdr + udp_hdr + dns_payload
buf = bytearray("-" * 60000)
recvSock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0800))
recvSock.settimeout(1)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sendSock.settimeout(1)
sendSock.connect((resolv, d_port))
sendSock.send(packet)
msglen = 0
while True:
try:
pkt = recvSock.recvfrom(65535)
msglen += len(pkt[0])
print repr(pkt[0])
except socket.timeout as e:
break
sendSock.close()
recvSock.close()
return msglen
result = get_len('75.75.75.75', 'isc.org')
print result
For some reason doing
pkt = sendSock.recvfrom(65535)
Recieves nothing at all. Since I'm using SOCK_RAW the above code is less than ideal, but it works - sort of. If the socket is extremely noisy (like on a WLAN), I could end up receiving well beyond the DNS packets, because I have no way to know when to stop receiving packets when receiving a multipacket DNS answer. For a quiet network, like a lab VM, it works.
Is there a better way to use a receiving socket in this case?
Obviously from the code, I'm not that strong with Python sockets.
I have to send with SOCK_RAW because I am constructing the packet in a raw format. If I use SOCK_DGRAM the custom packet will be malformed when sending to a DNS resolver.
The only way I could see is to use the raw sockets receiver (recvSock.recv or recvfrom) and unpack each packet, look if the source and dest address match within what is supplied in get_len(), then look to see if the fragment bit is flipped. Then record the byte length of each packet with len(). I'd rather not do that. It just seems there is a better way.
Ok I was stupid and didn't look at the protocol for the receiving socket. Socket gets kind of flaky when you try to receive packets on a IPPROTO_RAW protocol, so we do need two sockets. By changing to IPPROTO_UDP and then binding it, the socket was able to follow the complete DNS response over multiple requests. I got rid of the try/catch and the while loop, as it was no longer necessary and I'm able to pull the response length with this block:
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
recvSock.settimeout(.3)
recvSock.bind((target, s_port))
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
#sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendSock.settimeout(.3)
sendSock.bind((target, s_port))
sendSock.connect((resolv, d_port))
sendSock.send(packet)
pkt = recvSock.recvfrom(65535)
msglen = len(pkt[0])
Now the method will return the exact bytes received from a DNS query. I'll leave this up in case anyone else needs to do something similar :)

Categories