mirror of
https://github.com/MattKeeley/Spoofy.git
synced 2026-02-03 13:33:24 +00:00
Merge pull request #22 from darcosion/main
Big thank you to @darcosion ! Adding BIMI record retrieval
This commit is contained in:
46
libs/bimi.py
Executable file
46
libs/bimi.py
Executable file
@@ -0,0 +1,46 @@
|
||||
import dns.resolver
|
||||
|
||||
|
||||
def get_bimi_record(domain, dns_server):
|
||||
"""Returns the BIMI record for a given domain."""
|
||||
try:
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [dns_server, '1.1.1.1', '8.8.8.8']
|
||||
query_result = resolver.resolve('default._bimi.' + domain, 'TXT')
|
||||
for record in query_result:
|
||||
if 'v=BIMI' in str(record):
|
||||
return record
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_bimi_details(bimi_record):
|
||||
"""Returns a tuple containing policy, pct, aspf, subdomain policy,
|
||||
forensic report uri, and aggregate report uri from a BIMI record"""
|
||||
version = get_bimi_version(bimi_record)
|
||||
location = get_bimi_location(bimi_record)
|
||||
authority = get_bimi_authority(bimi_record)
|
||||
return version, location, authority
|
||||
|
||||
|
||||
def get_bimi_version(bimi_record):
|
||||
"""Returns the version value from a BIMI record."""
|
||||
if "v=" in str(bimi_record):
|
||||
return str(bimi_record).split("v=")[1].split(";")[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_bimi_location(bimi_record):
|
||||
"""Returns the location value from a BIMI record."""
|
||||
if "l=" in str(bimi_record):
|
||||
return str(bimi_record).split("l=")[1].split(";")[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_bimi_authority(bimi_record):
|
||||
"""Returns the authority value from a BIMI record."""
|
||||
if "a=" in str(bimi_record):
|
||||
return str(bimi_record).split("a=")[1].split(";")[0]
|
||||
else:
|
||||
return None
|
||||
12
libs/dns.py
Normal file → Executable file
12
libs/dns.py
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
import dns.resolver
|
||||
import socket
|
||||
from . import spf, dmarc
|
||||
from . import spf, dmarc, bimi
|
||||
|
||||
|
||||
def get_soa_record(domain):
|
||||
@@ -24,25 +24,27 @@ def get_soa_record(domain):
|
||||
def get_dns_server(domain):
|
||||
"""Finds the DNS server that serves the domain and returns it, along with any SPF or DMARC records."""
|
||||
SOA = get_soa_record(domain)
|
||||
spf_record = dmarc_record = partial_spf_record = partial_dmarc_record = None
|
||||
spf_record = dmarc_record = partial_spf_record = partial_dmarc_record = bimi_record = None
|
||||
|
||||
if SOA:
|
||||
spf_record = spf.get_spf_record(domain, SOA)
|
||||
dmarc_record = dmarc.get_dmarc_record(domain, SOA)
|
||||
bimi_record = bimi.get_bimi_record(domain, SOA)
|
||||
if spf_record and dmarc_record:
|
||||
return SOA, spf_record, dmarc_record
|
||||
return SOA, spf_record, dmarc_record, bimi_record
|
||||
|
||||
for ip_address in ['1.1.1.1', '8.8.8.8', '9.9.9.9']:
|
||||
spf_record = spf.get_spf_record(domain, ip_address)
|
||||
dmarc_record = dmarc.get_dmarc_record(domain, ip_address)
|
||||
bimi_record = bimi.get_bimi_record(domain, SOA)
|
||||
if spf_record and dmarc_record:
|
||||
return ip_address, spf_record, dmarc_record
|
||||
return ip_address, spf_record, dmarc_record, bimi_record
|
||||
if spf_record:
|
||||
partial_spf_record = spf_record
|
||||
if dmarc_record:
|
||||
partial_dmarc_record = dmarc_record
|
||||
|
||||
return '1.1.1.1', partial_spf_record, partial_dmarc_record
|
||||
return '1.1.1.1', partial_spf_record, partial_dmarc_record, bimi_record
|
||||
|
||||
|
||||
def get_txt_record(domain, record_type):
|
||||
|
||||
0
libs/logic.py
Normal file → Executable file
0
libs/logic.py
Normal file → Executable file
21
libs/report.py
Normal file → Executable file
21
libs/report.py
Normal file → Executable file
@@ -48,7 +48,7 @@ def write_to_excel(data):
|
||||
df.to_excel(file_name, index=False)
|
||||
|
||||
|
||||
def printer(domain, subdomain, dns_server, spf_record, spf_all, spf_includes, dmarc_record, p, pct, aspf, sp, fo, rua, spoofable):
|
||||
def printer(domain, subdomain, dns_server, spf_record, spf_all, spf_includes, dmarc_record, p, pct, aspf, sp, fo, rua, bimi_record, vbimi, location, authority, spoofable):
|
||||
"""This function is a utility function that takes in various parameters related to the
|
||||
results of DMARC and SPF checks and outputs the results to the console in a human-readable format.
|
||||
|
||||
@@ -96,11 +96,24 @@ def printer(domain, subdomain, dns_server, spf_record, spf_all, spf_includes, dm
|
||||
f"Aggregate reports will be sent to: {rua}" if rua else "No DMARC aggregate report location found.")
|
||||
else:
|
||||
output_warning("No DMARC record found.")
|
||||
|
||||
|
||||
if(bimi_record):
|
||||
output_info(f"BIMI record : {bimi_record}")
|
||||
output_info(f"BIMI version : {vbimi}")
|
||||
output_info(f"BIMI location : {location}")
|
||||
output_info(f"BIMI authority : {authority}")
|
||||
|
||||
if spoofable in [0, 1, 2, 3, 4, 5, 6, 7, 8]:
|
||||
if spoofable == 8:
|
||||
output_bad("Spoofing not possible for " + domain)
|
||||
else:
|
||||
output_good("Spoofing possible for " + domain if spoofable == 0 else "Subdomain spoofing possible for " + domain if spoofable == 1 else "Organizational domain spoofing possible for " + domain if spoofable == 2 else "Spoofing might be possible for " + domain if spoofable == 3 else "Spoofing might be possible (Mailbox dependant) for " +
|
||||
domain if spoofable == 4 else "Organizational domain spoofing may be possible for " + domain if spoofable == 5 else "Subdomain spoofing might be possible (Mailbox dependant) for " + domain if spoofable == 6 else "Subdomain spoofing might be possible (Mailbox dependant) for " + domain if spoofable == 7 else "")
|
||||
output_good("Spoofing possible for " + domain
|
||||
if spoofable == 0 else "Subdomain spoofing possible for " + domain
|
||||
if spoofable == 1 else "Organizational domain spoofing possible for " + domain
|
||||
if spoofable == 2 else "Spoofing might be possible for " + domain
|
||||
if spoofable == 3 else "Spoofing might be possible (Mailbox dependant) for " + domain
|
||||
if spoofable == 4 else "Organizational domain spoofing may be possible for " + domain
|
||||
if spoofable == 5 else "Subdomain spoofing might be possible (Mailbox dependant) for " + domain
|
||||
if spoofable == 6 else "Subdomain spoofing might be possible (Mailbox dependant) for " + domain
|
||||
if spoofable == 7 else "")
|
||||
print() # padding
|
||||
|
||||
34
spoofy.py
Normal file → Executable file
34
spoofy.py
Normal file → Executable file
@@ -3,7 +3,7 @@ import argparse
|
||||
import tldextract
|
||||
import threading
|
||||
import os
|
||||
from libs import dmarc, dns, logic, spf, report
|
||||
from libs import bimi, dmarc, dns, logic, spf, report
|
||||
|
||||
print_lock = threading.Lock()
|
||||
|
||||
@@ -14,29 +14,45 @@ def process_domain(domain, output):
|
||||
and outputs the results to the console or an Excel file."""
|
||||
try:
|
||||
dns_server = spf_record = dmarc_record = None
|
||||
spf_all = spf_includes = p = pct = aspf = sp = fo = rua = None
|
||||
spf_all = spf_includes = p = pct = aspf = sp = fo = rua = vbimi = location = authority = None
|
||||
subdomain = bool(tldextract.extract(domain).subdomain)
|
||||
with print_lock:
|
||||
dns_server, spf_record, dmarc_record = dns.get_dns_server(domain)
|
||||
dns_server, spf_record, dmarc_record, bimi_record = dns.get_dns_server(domain)
|
||||
if spf_record:
|
||||
spf_all = spf.get_spf_all_string(spf_record)
|
||||
spf_includes = spf.get_spf_includes(domain)
|
||||
if dmarc_record:
|
||||
p, pct, aspf, sp, fo, rua = dmarc.get_dmarc_details(dmarc_record)
|
||||
if bimi_record:
|
||||
vbimi, location, authority = bimi.get_bimi_details(bimi_record)
|
||||
spoofable = logic.is_spoofable(
|
||||
domain, p, aspf, spf_record, spf_all, spf_includes, sp, pct)
|
||||
if output == "xls":
|
||||
with print_lock:
|
||||
data = [{'DOMAIN': domain, 'SUBDOMAIN': subdomain, 'SPF': spf_record, 'SPF MULTIPLE ALLS': spf_all,
|
||||
'SPF TOO MANY INCLUDES': spf_includes, 'DMARC': dmarc_record, 'DMARC POLICY': p,
|
||||
'DMARC PCT': pct, 'DMARC ASPF': aspf, 'DMARC SP': sp, 'DMARC FORENSIC REPORT': fo,
|
||||
'DMARC AGGREGATE REPORT': rua, 'SPOOFING POSSIBLE': spoofable}]
|
||||
data = [{'DOMAIN': domain,
|
||||
'SUBDOMAIN': subdomain,
|
||||
'SPF': spf_record,
|
||||
'SPF MULTIPLE ALLS': spf_all,
|
||||
'SPF TOO MANY INCLUDES': spf_includes,
|
||||
'DMARC': dmarc_record,
|
||||
'DMARC POLICY': p,
|
||||
'DMARC PCT': pct,
|
||||
'DMARC ASPF': aspf,
|
||||
'DMARC SP': sp,
|
||||
'DMARC FORENSIC REPORT': fo,
|
||||
'DMARC AGGREGATE REPORT': rua,
|
||||
'BIMI_RECORD': bimi_record,
|
||||
'BIMI_VERSION': vbimi,
|
||||
'BIMI_LOCATION': location,
|
||||
'BIMI_AUTHORITY': authority,
|
||||
'SPOOFING POSSIBLE': spoofable}]
|
||||
report.write_to_excel(data)
|
||||
else:
|
||||
with print_lock:
|
||||
report.printer(domain, subdomain, dns_server, spf_record, spf_all, spf_includes, dmarc_record, p, pct, aspf,
|
||||
sp, fo, rua, spoofable)
|
||||
except:
|
||||
sp, fo, rua, bimi_record, vbimi, location, authority, spoofable)
|
||||
except Exception as e:
|
||||
raise e
|
||||
with print_lock:
|
||||
report.output_error(
|
||||
f"Domain {domain} is offline or format cannot be interpreted.")
|
||||
|
||||
Reference in New Issue
Block a user