Braiam FirefoxはログインWebサイトのパスワードデータを~/.mozilla/firefox/key3.db
および~/.mozilla/firefox/signons.sqlite
ファイルに保存すると述べています。これらのファイルは、sqliteエディターで読み取ることができます。
FirefoxのデータベースからWebサイトのユーザー名とパスワード(例 https://sourceforge.net/account/login.php )を照会しようとしています。 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では、保存されているパスワードにアクセスするようにmasterキーワードを設定したためだと思います。特定のWebサイトのパスワードを照会するにはどうすればよいですか?
私のOSはUbuntuです。ファイルタイプはkey3.db
です。
$ file key3.db
key3.db: Berkeley DB 1.85 (Hash, version 2, native byte-order)
特定のWebサイト名からパスワードを照会するには、何を読んで学習する必要がありますか?
読書 http://www.sqlite.org/cli.html は役に立ちますか?
ガレス・ザ・レッド:
私はあなたの命令を試みました。 ただし、何も返さない。 出力はひどいです:
$ 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に保存されているパスワードを読み取ってインポートできる可能性はありますか?
一部の男は必要なすべてのコードを一緒に接着しているようです ここ :
#!/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フォーラムの 関連する議論 を参照してください。
残念ながら、他の回答にリンクされているPythonスクリプトは両方とも、システムのセグメンテーション違反で同様に失敗します。私が見つけたのは nss-passwords で、これは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のパスワードを見つけるために使用したので、それについて書くことができ、それは素晴らしい働きをします!なんてメタだ!
ファイルkey3.db
には、signons.sqlite
に格納されているパスワードの暗号化に使用されるキーが含まれています。
カスタム形式であるため、標準のデータベースコマンドを使用する代わりに、特別なプログラムを使用して作業する必要があります。
Windowsがkey3.db
ファイルを利用するためのツールがあるようです。SOでこの質問の回答を参照してください: firefoxプロファイルのkey3.dbデータベースの暗号化キーは何ですか?
@StéphaneChazelasの答えは、Linuxで機能するpythonスクリプトを提供します。
最新バージョンはこちら: https://hg.cryptobitch.de/firefox-passwords/file/
file
はkey3.db
がBerleley DB 1.85形式であることを示していますが、そうではありません。これは、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]
特定のWebサイトを検索するには、基本的な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
をカバーしています。
Ubuntu/Debianでは、nss-passwords
パッケージをインストールできます。これを使用すると、パスワードを1つずつ(サイトごとに)取得できます。コマンドcert8.db
を使用して、key3.db
、logins.json
、signons.sqlite
nss-passwords -d <path-with-files> <site-url-to-query>
ファイルを含むディレクトリを指定できます(デフォルトでは、Firefoxプロファイルが検索されます)。
サイトの名前を照会するには、Firefoxの新しいバージョン(> 32)でクリアテキストファイルlogins.json
を検索するか、sqlite3 signons.sqlite
を使用してからselect * from moz_logins;
を使用します。
たとえば、新しいFirefoxプロファイルを使用してすべてのサイトをクエリする場合は、ファイルcert8.db
、key3.db
、logins.json
をディレクトリにコピーして実行する必要があります。
cat logins.json | jq ".logins[].hostname" | uniq | xargs nss-passwords -d .
注:jq
はJSONを処理するためのツールです。