Skip to content

Commit 6aaeaee

Browse files
authored
Add files via upload
Signed-off-by: halil ibrahim deniz <62566319+HalilDeniz@users.noreply.github.com>
1 parent ebd8915 commit 6aaeaee

10 files changed

+418
-0
lines changed

core/__init__.py

Whitespace-only changes.

core/dnsfiglet.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pyfiglet
2+
from colorama import init, Fore
3+
4+
# colorama'yı başlat
5+
init(autoreset=True)
6+
7+
def dnsfiglet():
8+
metin = "DNS Packet Sniffer started..."
9+
figlet_yazi = pyfiglet.figlet_format(metin, font="slant")
10+
11+
12+
return print(Fore.GREEN + figlet_yazi)

dnswatch/__init__.py

Whitespace-only changes.

dnswatch/dnswatch.py

+305
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/usr/bin/env python3
2+
3+
import requests
4+
import argparse
5+
from colorama import Fore, Style
6+
from datetime import datetime
7+
from scapy.layers.dns import DNSQR, DNSRR
8+
from scapy.layers.inet import IP
9+
from scapy.all import sniff, Ether, wrpcap
10+
11+
12+
from utils.dnsdata import DNSDataStorage
13+
from utils.version import __version__
14+
from core.dnsfiglet import dnsfiglet
15+
16+
17+
dns_requests = {}
18+
dns_types = {}
19+
20+
def resolve_dns_doh(dns_request):
21+
# DNS isteğini DoH ile çözümle
22+
url = f"https://cloudflare-dns.com/dns-query?name={dns_request}&type=A"
23+
headers = {"Accept": "application/dns-json"}
24+
response = requests.get(url, headers=headers)
25+
26+
if response.status_code == 200:
27+
try:
28+
result = response.json()
29+
if "Answer" in result:
30+
answers = result["Answer"]
31+
return [answer["data"] for answer in answers]
32+
except Exception as e:
33+
print(f"{Fore.RED}[!] Error resolving DNS over HTTPS: {e}{Style.RESET_ALL}")
34+
35+
return None
36+
def dns_sniffer(pkt, verbose, output_file, target_ip=None, analyze_dns_types=False, use_doh=False, filter_domains=[], dns_storage=None):
37+
if target_ip and pkt[IP].src != target_ip and pkt[IP].dst != target_ip:
38+
return
39+
40+
dns_storage = DNSDataStorage()
41+
42+
if pkt.haslayer(DNSQR) and pkt.haslayer(Ether):
43+
ip_header = pkt.getlayer('IP')
44+
udp_header = pkt.getlayer('UDP')
45+
dns_request = pkt[DNSQR].qname.decode()
46+
dns_type = pkt[DNSQR].qtype
47+
dns_src_ip = pkt[IP].src
48+
dns_dest_ip = pkt[IP].dst
49+
ether_header = pkt[Ether]
50+
src_mac = ether_header.src
51+
dst_mac = ether_header.dst
52+
ttl = ip_header.ttl if ip_header.ttl else "N/A"
53+
ip_checksum = ip_header.chksum
54+
udp_checksum = udp_header.chksum
55+
56+
timestamp = datetime.fromtimestamp(pkt.time).strftime('%Y-%m-%d %H:%M:%S')
57+
58+
if filter_domains and not any(domain in dns_request for domain in filter_domains):
59+
return # Skip DNS requests that don't match filter domains
60+
print(f"{Fore.CYAN}\tDNS Request:{Style.RESET_ALL}")
61+
print(f"Timestamp : {Fore.YELLOW}{timestamp}{Style.RESET_ALL}")
62+
print(f"Source IP : {Fore.GREEN}{dns_src_ip}{Style.RESET_ALL}")
63+
print(f"Destination IP : {Fore.GREEN}{dns_dest_ip}{Style.RESET_ALL}")
64+
print(f"Source MAC : {Style.RESET_ALL}{src_mac}")
65+
print(f"Destination MAC: {Style.RESET_ALL}{dst_mac}")
66+
print(f"Packet Size : {Fore.GREEN}{len(pkt)} bytes{Style.RESET_ALL}")
67+
print(f"TTL : {Style.RESET_ALL}{ttl}")
68+
print(f"Type : {Fore.GREEN}{dns_type}{Style.RESET_ALL}")
69+
print(f"IP Checksum : {Fore.GREEN}{ip_checksum}{Style.RESET_ALL}")
70+
print(f"UDP Checksum : {Fore.GREEN}{udp_checksum}{Style.RESET_ALL}")
71+
print(f"DNS Request : {Fore.GREEN}{dns_request}{Style.RESET_ALL}")
72+
print("*" * 60)
73+
74+
if use_doh:
75+
resolved_ips = resolve_dns_doh(dns_request)
76+
if resolved_ips:
77+
print(f"{Fore.CYAN}\tDNS Request (DoH){Style.RESET_ALL}")
78+
print(f"Timestamp : {Fore.YELLOW}{timestamp}{Style.RESET_ALL}")
79+
print(f"Source IP : {Fore.CYAN}{dns_src_ip}{Style.RESET_ALL}")
80+
print(f"Destination IP : {Fore.CYAN}{dns_dest_ip}{Style.RESET_ALL}")
81+
print(f"Source MAC : {Style.RESET_ALL}{src_mac}")
82+
print(f"Destination MAC: {Style.RESET_ALL}{dst_mac}")
83+
print(f"Packet Size : {Fore.GREEN}{len(pkt)} bytes{Style.RESET_ALL}")
84+
print(f"TTL : {Style.RESET_ALL} {ttl}")
85+
print(f"Type : {Fore.GREEN}{dns_type}{Style.RESET_ALL}")
86+
print(f"IP Checksum : {Fore.GREEN}{ip_checksum}{Style.RESET_ALL}")
87+
print(f"UDP Checksum : {Fore.GREEN}{udp_checksum}{Style.RESET_ALL}")
88+
print(f"DNS Request : {Fore.GREEN}{dns_request}{Style.RESET_ALL}")
89+
print(f"Resolved IPs : {Fore.GREEN}{', '.join(resolved_ips)}{Style.RESET_ALL}")
90+
print("*" * 60)
91+
92+
93+
else:
94+
print(f"{Fore.CYAN}\tDNS Request (DoH){Style.RESET_ALL}")
95+
print(f"Timestamp : {Fore.YELLOW}{timestamp}{Style.RESET_ALL}")
96+
print(f"Source IP : {Fore.CYAN}{dns_src_ip}{Style.RESET_ALL}")
97+
print(f"Destination IP : {Fore.CYAN}{dns_dest_ip}{Style.RESET_ALL}")
98+
print(f"Source MAC : {Style.RESET_ALL}{src_mac}")
99+
print(f"Destination MAC: {Style.RESET_ALL}{dst_mac}")
100+
print(f"Packet Size : {len(pkt)} bytes{Style.RESET_ALL}")
101+
print(f"TTL : {Style.RESET_ALL}{ttl}")
102+
print(f"Type : {dns_type}")
103+
print(f"IP Checksum : {Fore.GREEN}{ip_checksum}{Style.RESET_ALL}")
104+
print(f"UDP Checksum : {Fore.GREEN}{udp_checksum}{Style.RESET_ALL}")
105+
print(f"DNS Request : {dns_request}")
106+
print(f"Resolved IPs : (Cannot resolve with DoH)")
107+
print("*" * 60)
108+
109+
else:
110+
print(f"{Fore.CYAN}\tDNS Request{Style.RESET_ALL}")
111+
print(f"Timestamp : {Fore.YELLOW}{timestamp}{Style.RESET_ALL}")
112+
print(f"Source IP : {Fore.CYAN}{dns_src_ip}{Style.RESET_ALL}")
113+
print(f"Destination IP : {Fore.CYAN}{dns_dest_ip}{Style.RESET_ALL}")
114+
print(f"Source MAC : {Style.RESET_ALL}{src_mac}")
115+
print(f"Destination MAC: {Style.RESET_ALL}{dst_mac}")
116+
print(f"Packet Size : {Fore.GREEN}{len(pkt)} bytes{Style.RESET_ALL}")
117+
print(f"TTL : {Style.RESET_ALL}{ttl}")
118+
print(f"Type : {dns_type}")
119+
print(f"IP Checksum : {Fore.GREEN}{ip_checksum}{Style.RESET_ALL}")
120+
print(f"UDP Checksum : {Fore.GREEN}{udp_checksum}{Style.RESET_ALL}")
121+
print(f"DNS Request : {dns_request}")
122+
print("*" * 60)
123+
124+
if dns_request in dns_requests:
125+
dns_requests[dns_request][0] += 1
126+
else:
127+
dns_requests[dns_request] = [1, []]
128+
129+
if analyze_dns_types:
130+
if dns_type in dns_types:
131+
dns_types[dns_type] += 1
132+
else:
133+
dns_types[dns_type] = 1
134+
135+
if verbose:
136+
print(pkt.show())
137+
138+
if output_file:
139+
with open(output_file, "a") as file:
140+
file.write("\tDNS Request details:\n")
141+
file.write(f"Timestamp : {timestamp}\n")
142+
file.write(f"Source IP : {dns_src_ip}\n")
143+
file.write(f"Destination IP : {dns_dest_ip}\n")
144+
file.write(f"Destination IP : {dns_dest_ip}\n")
145+
file.write(f"Source mac : {src_mac}\n")
146+
file.write(f"Packet Size : {len(pkt)} bytes\n")
147+
file.write(f"Tll : {ttl}\n")
148+
file.write(f"Type : {dns_type}\n")
149+
file.write(f"IP Checksum : {ip_checksum}\n")
150+
file.write(f"UDP Checksum : {udp_checksum}\n")
151+
file.write(f"DNS Request : {dns_request}\n")
152+
file.write(f"{'-' * 60}\n")
153+
dns_storage.insert_dns_request(timestamp, dns_src_ip, dns_dest_ip, src_mac, dst_mac, len(pkt), ttl, ip_checksum, udp_checksum, dns_request, dns_type)
154+
155+
if pkt.haslayer(DNSRR):
156+
dns_response = pkt[DNSRR].rrname.decode()
157+
dns_type = pkt[DNSRR].type
158+
dns_src_ip = pkt[IP].src
159+
dns_dest_ip = pkt[IP].dst
160+
timestamp = datetime.fromtimestamp(pkt.time).strftime('%Y-%m-%d %H:%M:%S')
161+
162+
if filter_domains and not any(domain in dns_response for domain in filter_domains):
163+
return # Skip DNS responses that don't match filter domains
164+
165+
print(f"{Fore.CYAN}\tDNS Response details{Style.RESET_ALL}")
166+
print(f"Timestamp : {Fore.YELLOW}{timestamp}{Style.RESET_ALL}")
167+
print(f"Source IP : {Fore.CYAN}{dns_src_ip}{Style.RESET_ALL}")
168+
print(f"Destination IP : {Fore.CYAN}{dns_dest_ip}{Style.RESET_ALL}")
169+
print(f"Source MAC : {Style.RESET_ALL}{src_mac}")
170+
print(f"Destination MAC: {Style.RESET_ALL}{dst_mac}")
171+
print(f"Packet Size : {Fore.GREEN}{len(pkt)} bytes{Style.RESET_ALL}")
172+
print(f"TTL : {Style.RESET_ALL}{ttl}")
173+
print(f"Type : {dns_type}")
174+
print(f"IP Checksum : {Fore.GREEN}{ip_checksum}{Style.RESET_ALL}")
175+
print(f"UDP Checksum : {Fore.GREEN}{udp_checksum}{Style.RESET_ALL}")
176+
print(f"DNS Response : {dns_response}")
177+
print("*" * 60)
178+
179+
if dns_response in dns_requests:
180+
dns_requests[dns_response][1].append(dns_src_ip)
181+
182+
if analyze_dns_types:
183+
if dns_type in dns_types:
184+
dns_types[dns_type] += 1
185+
else:
186+
dns_types[dns_type] = 1
187+
188+
if verbose:
189+
print(pkt.show())
190+
191+
if output_file:
192+
with open(output_file, "a") as file:
193+
file.write(f"\tDNS Response details:\n")
194+
file.write(f"Timestamp : {timestamp}\n")
195+
file.write(f"Source IP : {dns_src_ip}\n")
196+
file.write(f"Destination IP : {dns_dest_ip}\n")
197+
file.write(f"Source mac : {src_mac}\n")
198+
file.write(f"Destination Mac: {dst_mac}\n")
199+
file.write(f"Packet Size : {len(pkt)} bytes\n")
200+
file.write(f"Tll : {ttl}\n")
201+
file.write(f"Type : {dns_type}\n")
202+
file.write(f"IP Checksum : {ip_checksum}\n")
203+
file.write(f"UDP Checksum : {udp_checksum}\n")
204+
file.write(f"DNS Response : {dns_response}\n")
205+
file.write(f"{'-'*60}\n")
206+
207+
dns_storage.insert_dns_request(timestamp, dns_src_ip, dns_dest_ip, src_mac, dst_mac, len(pkt), ttl, ip_checksum, udp_checksum, dns_response, dns_type)
208+
209+
dns_storage.close()
210+
211+
212+
def dns_data_analysis():
213+
if dns_requests:
214+
print(f"\n\t{Fore.CYAN}DNS Data Analysis{Style.RESET_ALL}")
215+
total_requests = sum(count for count, _ in dns_requests.values())
216+
unique_domains = len(dns_requests)
217+
most_requested = max(dns_requests, key=lambda x: dns_requests[x][0])
218+
most_requested_count = dns_requests[most_requested][0]
219+
220+
resolved_by_counts = {}
221+
for resolved_ips in dns_requests.values():
222+
for ip in resolved_ips[1]:
223+
if ip in resolved_by_counts:
224+
resolved_by_counts[ip] += 1
225+
else:
226+
resolved_by_counts[ip] = 1
227+
228+
print(f"{Fore.GREEN}Total DNS Requests :{Style.RESET_ALL} {total_requests}")
229+
print(f"{Fore.GREEN}Unique Domains :{Style.RESET_ALL} {unique_domains}")
230+
print(f"{Fore.GREEN}Most Requested Domain:{Style.RESET_ALL} {most_requested} (Count: {most_requested_count})")
231+
232+
print(f"\n{Fore.CYAN}\tMost Resolved by{Style.RESET_ALL}")
233+
for ip, count in resolved_by_counts.items():
234+
print(f"{ip} : {count}")
235+
236+
if dns_types:
237+
print(f"\nDNS Type Analysis:")
238+
for dns_type, count in dns_types.items():
239+
print(f"Type: {dns_type} - Count: {count}")
240+
else:
241+
print("No DNS requests to analyze.")
242+
243+
244+
def main():
245+
parser = argparse.ArgumentParser(description="DNSWatch packet sniffer")
246+
parser.add_argument("-i", "--interface", help="Specify the network interface, for example 'eth0'", required=True)
247+
parser.add_argument("-v", "--verbose", help="Use this flag to get more verbose output", action="store_true")
248+
parser.add_argument("-t", "--target-ip", help="Specify specific target IP address to monitor")
249+
parser.add_argument("-adt", "--analyze-dns-types", help="Use this flag to analyze DNS types", action="store_true")
250+
parser.add_argument("--doh", help="DNS over HTTPS (DoH) use this flag to use", action="store_true")
251+
parser.add_argument("-fd", "--target-domains", nargs="+", help="Filter DNS requests by specified domains", default=[])
252+
parser.add_argument("-o", "--output", help="Specify the filename to save the results to a file")
253+
parser.add_argument("-d", "--database", help="Enable database storage", action="store_true")
254+
parser.add_argument("-p", "--pcap", help="Save captured packets to a .pcap file", default=None)
255+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}") # Sürüm bilgisini göstermek için bayrak ekleyin
256+
257+
args = parser.parse_args()
258+
259+
iface = args.interface
260+
filter_rule = "udp port 53"
261+
262+
try:
263+
f"\t\t{dnsfiglet()}"
264+
dns_storage = DNSDataStorage() if args.database else None # Database Storage'ı isteğe bağlı olarak etkinleştirin
265+
266+
if args.pcap:
267+
pcap_filename = args.pcap
268+
print(f"Saving captured packets to '{pcap_filename}'")
269+
packets = []
270+
271+
272+
def packet_callback(pkt):
273+
packets.append(pkt)
274+
dns_sniffer(pkt, args.verbose, args.output, args.target_ip, args.analyze_dns_types,
275+
args.doh, args.target_domains, dns_storage)
276+
277+
278+
try:
279+
sniff(iface=iface, filter=filter_rule, prn=packet_callback)
280+
except KeyboardInterrupt:
281+
pass
282+
wrpcap(pcap_filename, packets)
283+
else:
284+
sniff(iface=iface, filter=filter_rule,
285+
prn=lambda pkt: dns_sniffer(pkt, args.verbose, args.output, args.target_ip, args.analyze_dns_types,
286+
args.doh, args.target_domains, dns_storage))
287+
except PermissionError:
288+
print(
289+
f"{Fore.RED}Error: You do not have sufficient privileges. Try running the program with 'sudo'.{Style.RESET_ALL}")
290+
exit()
291+
except OSError as e:
292+
if "No such device" in str(e):
293+
print(
294+
f"{Fore.RED}Error: Interface '{iface}' does not exist. \nPlease provide a valid interface name.{Style.RESET_ALL}")
295+
exit()
296+
else:
297+
raise
298+
except KeyboardInterrupt:
299+
pass
300+
301+
302+
dns_data_analysis()
303+
304+
if __name__ == "__main__":
305+
main()

img/dnswatch.png

447 KB
Loading

img/dnswatch1.png

443 KB
Loading

setup.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from setuptools import setup, find_packages
2+
3+
setup(
4+
name='dnswatch',
5+
version='1.0.0',
6+
packages=find_packages(),
7+
install_requires=[
8+
'requests',
9+
'scapy',
10+
'colorama',
11+
'datetime',
12+
],
13+
entry_points={
14+
'console_scripts': [
15+
'dnswatch = dnswatch.dnswatch:main',
16+
],
17+
},
18+
author='Halil Ibrahim Deniz',
19+
long_description=open('Readme.md').read(),
20+
author_email='halildeniz313@gmail.com',
21+
description='DNS sniffer tool',
22+
license='MIT',
23+
keywords='dns sniffer cybersecurity',
24+
url='https://github.com/HalilDeniz/DNSWatch',
25+
26+
classifiers=[
27+
'Programming Language :: Python :: 3',
28+
'License :: OSI Approved :: MIT License',
29+
'Operating System :: OS Independent',
30+
],
31+
python_requires='>=3.6',
32+
)

utils/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)