브라이언Firefox는 웹사이트 로그인에 대한 비밀번호 데이터를 파일에 저장하는 것으로 알려져 ~/.mozilla/firefox/key3.db
있습니다 ~/.mozilla/firefox/signons.sqlite
. 이러한 파일은 일부 sqlite 편집기를 사용하여 읽을 수 있습니다.
웹사이트에 사용자 이름과 비밀번호를 문의해 보았습니다(예:https://sourceforge.net/account/login.php) Firefox 데이터베이스에서. 내 Firefox GUI가 제대로 작동하지 않기 때문에 Firefox를 통해 이 작업을 수행할 수 없습니다. 저는 이 작업을 수행하기 위해 데이터베이스를 사용하는 것이 상당히 익숙하지 않으며 학습에 관심이 있습니다.
key3.db
및 의 다른 효과는 무엇입니까signons.sqlite
?sqlite3
인터넷에서 검색했는데 데이터베이스를 열 때 사용해야 하는 것이 맞나요?$ sqlite3 key3.db SQLite version 3.7.9 2011-11-01 00:52:41 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> .tables Error: file is encrypted or is not a database
이 실패의 원인은 Firefox에서 저장된 비밀번호에 액세스하기 위한 기본 키를 설정했기 때문인 것 같습니다. 특정 웹 사이트의 비밀번호를 어떻게 쿼리해야 합니까?
내 운영 체제는 Ubuntu이고 파일 형식은 다음과 같습니다
key3.db
.$ file key3.db key3.db: Berkeley DB 1.85 (Hash, version 2, native byte-order)
특정 웹사이트 이름에서 비밀번호를 문의하려면 무엇을 읽고 배워야 합니까?
읽을 수 있다http://www.sqlite.org/cli.html돕다?
Gareth TheRed에게:
나는 당신의 명령을 시도했습니다. 그러나 아무것도 반환하지 않습니다. 출력은 끔찍합니다.
$ sqlite3 signons.sqlite
SQLite version 3.7.9 2011-11-01 00:52:41
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
moz_deleted_logins moz_disabledHosts moz_logins
sqlite> select * from moz_logins;
...
55|https://sourceforge.net||https://sourceforge.net|form_loginname|form_pw|MDIEEPgAAAAAAAAAAAAAAAAAAAEwF\AYIKoZIhvcNAwcECCPrVdOzWamBBAjPs0DI8FrUnQ==|MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCnZved1LRQMBBBV\DtXpOvAp0TQHibFeX3NL|{16e782de-4c65-426f-81dc-ee0361816262}|1|1327675445094|1403706275829|1327675445094|\4
...
Firefox는 마스터 키 유무에 관계없이 비밀번호를 암호화합니까? 그렇다면 명령줄에서 암호를 해독할 수 있습니까? (Firefox CLI는 여전히 작동할 수 있습니다.)
또는 Chrome에서 Firefox에 저장된 비밀번호를 읽고 가져올 수 있나요?
답변1
어떤 사람들은 필요한 모든 코드를 하나로 묶어 놓은 것 같습니다.여기:
#!/usr/bin/env python
"Recovers your Firefox or Thunderbird passwords"
import base64
from collections import namedtuple
from ConfigParser import RawConfigParser, NoOptionError
from ctypes import (Structure, CDLL, byref, cast, string_at, c_void_p,
c_uint, c_ubyte, c_char_p)
from getpass import getpass
import logging
from optparse import OptionParser
import os
try:
from sqlite3 import dbapi2 as sqlite
except ImportError:
from pysqlite2 import dbapi2 as sqlite
from subprocess import Popen, CalledProcessError, PIPE
import sys
LOGLEVEL_DEFAULT = 'warn'
log = logging.getLogger()
PWDECRYPT = 'pwdecrypt'
SITEFIELDS = ['id', 'hostname', 'httpRealm', 'formSubmitURL', 'usernameField', 'passwordField', 'encryptedUsername', 'encryptedPassword', 'guid', 'encType', 'plain_username', 'plain_password' ]
Site = namedtuple('FirefoxSite', SITEFIELDS)
'''The format of the SQLite database is:
(id INTEGER PRIMARY KEY,hostname TEXT NOT NULL,httpRealm TEXT,formSubmitURL TEXT,usernameField TEXT NOT NULL,passwordField TEXT NOT NULL,encryptedUsername TEXT NOT NULL,encryptedPassword TEXT NOT NULL,guid TEXT,encType INTEGER);
'''
#### These are libnss definitions ####
class SECItem(Structure):
_fields_ = [('type',c_uint),('data',c_void_p),('len',c_uint)]
class secuPWData(Structure):
_fields_ = [('source',c_ubyte),('data',c_char_p)]
(PW_NONE, PW_FROMFILE, PW_PLAINTEXT, PW_EXTERNAL) = (0, 1, 2, 3)
# SECStatus
(SECWouldBlock, SECFailure, SECSuccess) = (-2, -1, 0)
#### End of libnss definitions ####
def get_default_firefox_profile_directory(dir='~/.mozilla/firefox'):
'''Returns the directory name of the default profile
If you changed the default dir to something like ~/.thunderbird,
you would get the Thunderbird default profile directory.'''
profiles_dir = os.path.expanduser(dir)
profile_path = None
cp = RawConfigParser()
cp.read(os.path.join(profiles_dir, "profiles.ini"))
for section in cp.sections():
if not cp.has_option(section, "Path"):
continue
if (not profile_path or
(cp.has_option(section, "Default") and cp.get(section, "Default").strip() == "1")):
profile_path = os.path.join(profiles_dir, cp.get(section, "Path").strip())
if not profile_path:
raise RuntimeError("Cannot find default Firefox profile")
return profile_path
def get_encrypted_sites(firefox_profile_dir=None):
'Opens signons.sqlite and yields encryped password data'
if firefox_profile_dir is None:
firefox_profile_dir = get_default_firefox_profile_directory()
password_sqlite = os.path.join(firefox_profile_dir, "signons.sqlite")
query = '''SELECT id, hostname, httpRealm, formSubmitURL,
usernameField, passwordField, encryptedUsername,
encryptedPassword, guid, encType, 'noplainuser', 'noplainpasswd' FROM moz_logins;'''
# We don't want to type out all the column from the DB as we have
## stored them in the SITEFIELDS already. However, we have two
## components extra, the plain usename and password. So we remove
## that from the list, because the table doesn't have that column.
## And we add two literal SQL strings to make our "Site" data
## structure happy
#queryfields = SITEFIELDS[:-2] + ["'noplainuser'", "'noplainpassword'"]
#query = '''SELECT %s
# FROM moz_logins;''' % ', '.join(queryfields)
connection = sqlite.connect(password_sqlite)
try:
cursor = connection.cursor()
cursor.execute(query)
for site in map(Site._make, cursor.fetchall()):
yield site
finally:
connection.close()
def decrypt(encrypted_string, firefox_profile_directory, password = None):
'''Opens an external tool to decrypt strings
This is mostly for historical reasons or if the API changes. It is
very slow because it needs to call out a lot. It uses the
"pwdecrypt" tool which you might have packaged. Otherwise, you
need to build it yourself.'''
log = logging.getLogger('firefoxpasswd.decrypt')
execute = [PWDECRYPT, '-d', firefox_profile_directory]
if password:
execute.extend(['-p', password])
process = Popen(execute,
stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, error = process.communicate(encrypted_string)
log.debug('Sent: %s', encrypted_string)
log.debug('Got: %s', output)
NEEDLE = 'Decrypted: "' # This string is prepended to the decrypted password if found
output = output.strip()
if output == encrypted_string:
log.error('Password was not correct. Please try again without a '
'password or with the correct one')
index = output.index(NEEDLE) + len(NEEDLE)
password = output[index:-1] # And we strip the final quotation mark
return password
class NativeDecryptor(object):
'Calls the NSS API to decrypt strings'
def __init__(self, directory, password = ''):
'''You need to give the profile directory and optionally a
password. If you don't give a password but one is needed, you
will be prompted by getpass to provide one.'''
self.directory = directory
self.log = logging.getLogger('NativeDecryptor')
self.log.debug('Trying to work on %s', directory)
self.libnss = CDLL('libnss3.so')
if self.libnss.NSS_Init(directory) != 0:
self.log.error('Could not initialize NSS')
# Initialize to the empty string, not None, because the password
# function expects rather an empty string
self.password = password = password or ''
slot = self.libnss.PK11_GetInternalKeySlot()
pw_good = self.libnss.PK11_CheckUserPassword(slot, c_char_p(password))
while pw_good != SECSuccess:
msg = 'Password is not good (%d)!' % pw_good
print >>sys.stderr, msg
password = getpass('Please enter password: ')
pw_good = self.libnss.PK11_CheckUserPassword(slot, c_char_p(password))
#raise RuntimeError(msg)
# That's it, we're done with passwords, but we leave the old
# code below in, for nostalgic reasons.
if password is None:
pwdata = secuPWData()
pwdata.source = PW_NONE
pwdata.data = 0
else:
# It's not clear whether this actually works
pwdata = secuPWData()
pwdata.source = PW_PLAINTEXT
pwdata.data = c_char_p (password)
# It doesn't actually work :-(
# Now follow some attempts that were not succesful!
def setpwfunc():
# One attempt was to use PK11PassworFunc. Didn't work.
def password_cb(slot, retry, arg):
#s = self.libnss.PL_strdup(password)
s = self.libnss.PL_strdup("foo")
return s
PK11PasswordFunc = CFUNCTYPE(c_void_p, PRBool, c_void_p)
c_password_cb = PK11PasswordFunc(password_cb)
#self.libnss.PK11_SetPasswordFunc(c_password_cb)
# To be ignored
def changepw():
# Another attempt was to use ChangePW. Again, no effect.
#ret = self.libnss.PK11_ChangePW(slot, pwdata.data, 0);
ret = self.libnss.PK11_ChangePW(slot, password, 0)
if ret == SECFailure:
raise RuntimeError('Setting password failed! %s' % ret)
#self.pwdata = pwdata
def __del__(self):
self.libnss.NSS_Shutdown()
def decrypt(self, string, *args):
'Decrypts a given string'
libnss = self.libnss
uname = SECItem()
dectext = SECItem()
#pwdata = self.pwdata
cstring = SECItem()
cstring.data = cast( c_char_p( base64.b64decode(string)), c_void_p)
cstring.len = len(base64.b64decode(string))
#if libnss.PK11SDR_Decrypt (byref (cstring), byref (dectext), byref (pwdata)) == -1:
self.log.debug('Trying to decrypt %s (error: %s)', string, libnss.PORT_GetError())
if libnss.PK11SDR_Decrypt (byref (cstring), byref (dectext)) == -1:
error = libnss.PORT_GetError()
libnss.PR_ErrorToString.restype = c_char_p
error_str = libnss.PR_ErrorToString(error)
raise Exception ("%d: %s" % (error, error_str))
decrypted_data = string_at(dectext.data, dectext.len)
return decrypted_data
def encrypted_sites(self):
'Yields the encryped passwords from the profile'
sites = get_encrypted_sites(self.directory)
return sites
def decrypted_sites(self):
'Decrypts the encrypted_sites and yields the results'
sites = self.encrypted_sites()
for site in sites:
plain_user = self.decrypt(site.encryptedUsername)
plain_password = self.decrypt(site.encryptedPassword)
site = site._replace(plain_username=plain_user,
plain_password=plain_password)
yield site
def get_firefox_sites_with_decrypted_passwords(firefox_profile_directory = None, password = None):
'Old school decryption of passwords using the external tool'
if not firefox_profile_directory:
firefox_profile_directory = get_default_firefox_profile_directory()
#decrypt = NativeDecryptor(firefox_profile_directory).decrypt
for site in get_encrypted_sites(firefox_profile_directory):
plain_user = decrypt(site.encryptedUsername, firefox_profile_directory, password)
plain_password = decrypt(site.encryptedPassword, firefox_profile_directory, password)
site = site._replace(plain_username=plain_user, plain_password=plain_password)
log.debug("Dealing with Site: %r", site)
log.info("user: %s, passwd: %s", plain_user, plain_password)
yield site
def main_decryptor(firefox_profile_directory, password, thunderbird=False):
'Main function to get Firefox and Thunderbird passwords'
if not firefox_profile_directory:
if thunderbird:
dir = '~/.thunderbird/'
else:
dir = '~/.mozilla/firefox'
firefox_profile_directory = get_default_firefox_profile_directory(dir)
decryptor = NativeDecryptor(firefox_profile_directory, password)
for site in decryptor.decrypted_sites():
print site
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-d", "--directory", default=None,
help="the Firefox profile directory to use")
parser.add_option("-p", "--password", default=None,
help="the master password for the Firefox profile")
parser.add_option("-l", "--loglevel", default=LOGLEVEL_DEFAULT,
help="the level of logging detail [debug, info, warn, critical, error]")
parser.add_option("-t", "--thunderbird", default=False, action='store_true',
help="by default we try to find the Firefox default profile."
" But you can as well ask for Thunderbird's default profile."
" For a more reliable way, give the directory with -d.")
parser.add_option("-n", "--native", default=True, action='store_true',
help="use the native decryptor, i.e. make Python use "
"libnss directly instead of invoking the helper program"
"DEFUNCT! this option will not be checked.")
parser.add_option("-e", "--external", default=False, action='store_true',
help="use an external program `pwdecrypt' to actually "
"decrypt the passwords. This calls out a lot and is dead "
"slow. "
"You need to use this method if you have a password "
"protected database though.")
options, args = parser.parse_args()
loglevel = {'debug': logging.DEBUG, 'info': logging.INFO,
'warn': logging.WARN, 'critical':logging.CRITICAL,
'error': logging.ERROR}.get(options.loglevel, LOGLEVEL_DEFAULT)
logging.basicConfig(level=loglevel)
log = logging.getLogger()
password = options.password
if not options.external:
sys.exit (main_decryptor(options.directory, password, thunderbird=options.thunderbird))
else:
for site in get_firefox_sites_with_decrypted_passwords(options.directory, password):
print site
보다관련 토론Mozilla 포럼에서.
답변2
불행히도 다른 답변에 연결된 두 Python 스크립트는 내 시스템의 분할 오류로 인해 실패했습니다. 내가 찾은 것은NSS 비밀번호, OCAML과 C로 작성된 것 같아서 어디에서나 작동할지는 모르겠지만 적어도 Ubuntu와 같은 Debian 기반 배포판에서는 쉽게 설치할 수 있습니다.
$ sudo apt install nss-passwords
[...]
$ nss-passwords stackoverflow
[it asks for the main password]
| https://stackoverflow.com | [email protected] | this_is_not_my_real_password |
방금 stackverflow의 비밀번호를 찾는 데 사용하여 적어 둘 수 있었는데 훌륭하게 작동했습니다! 얼마나 메타적인가!
답변3
Berleley DB 1.85 형식인 것처럼 file
보이지만 key3.db
사실은 아닙니다. Mozilla의 독점 형식을 사용합니다. 암호화 시 사용자 이름과 비밀번호에 사용됩니다 signons.sqlite
.
다음 명령을 사용하면 날짜를 볼 수 있습니다 signons.sqlite
(사용자 이름과 비밀번호는 해독할 수 없음) sqlite3
.
sqlite3 signons.sqlite
SQLite version 3.8.5 2014-06-04 14:06:34
Enter ".help" for usage hints.
sqlite> .tables
moz_deleted_logins moz_disabledHosts moz_logins
sqlite> select * from moz_logins;
1|https://bugs.archlinux.org||https://bugs.archlinux.org|user_name|password|MDoEEP...
[more here]
특정 웹사이트를 검색하려면 기본 SQL 쿼리를 사용하세요.
sqlite> select * FROM moz_logins WHERE hostname LIKE "%arch%";
32|https://bbs.archlinux.org||https://bbs.archlinux.org|req_username|req_password|MD...
검색 문구는 큰따옴표로 묶여 있습니다. 는 %
와일드카드이므로 위의 예에서는 임의의 텍스트를 찾은 다음 , 임의의 텍스트를 찾습니다 arch
. 다뤘습니다 http://bbs.archlinux.org
.
답변4
이 파일에는 key3.db
에 저장된 비밀번호를 암호화하는 데 사용되는 키가 포함되어 있습니다 signons.sqlite
.
이는 사용자 정의 형식이므로 이를 처리하려면 표준 데이터베이스 명령을 사용하는 대신 특수 프로그램이 필요합니다.
파일을 활용할 수 있는 Windows 도구가 있는 것 같습니다 key3.db
. 다음 질문에 대한 답변을 참조하세요.Firefox 프로필에 있는 key3.db 데이터베이스의 암호화 키는 무엇입니까?
@StéphaneChazelas의 답변은 Linux에서 실행되어야 하는 Python 스크립트를 제공합니다.
최신 버전은 다음과 같습니다.https://hg.cryptobitch.de/firefox-passwords/file/