[PYTHON] Essayez de déchiffrer les données de connexion stockées dans Firefox

introduction

Essayez de déchiffrer le mot de passe stocké dans Chrome Essayez de sortir les données de connexion enregistrées dans IE / Edge Cette fois, j'ai essayé de savoir comment Firefox stockait les données de connexion du site Web.

Comment enregistrer les données de connexion Firefox

Après quelques recherches, j'ai trouvé que Firefox utilise une bibliothèque appelée NSS (Network Security Services) pour crypter les noms d'utilisateur et les mots de passe, les encoder en Base64 et les enregistrer dans le dossier de profil logins.json.

Le profil est Dossier utilisateur \ AppData \ Roaming \ Mozilla \ Firefox \ Profiles Il est géré pour chaque dossier dans. En outre, la bibliothèque NSS est stockée dans le dossier où Firefox est installé \ nss3.dll.

Essayez de lire les données de connexion

Implémentons un programme qui lit les données de connexion. J'avais l'habitude d'utiliser C # avant, mais cette fois cela n'a pas fonctionné donc j'utiliserai Python.

Tout d'abord, chargez la bibliothèque NSS requise pour le décryptage. La structure PK11SlotInfo est Opaque et n'a donc aucun membre défini.


#Chargement de DLL
dllpath = os.path.join(os.environ["programfiles"], "Mozilla Firefox\\nss3.dll")
nss3 = ct.CDLL(dllpath)

#Fonction de chargement
def getfunc(restype, name, *argtypes):
    res = getattr(nss3, name)
    res.restype = restype
    res.argtypes = argtypes
    return res

class SECItem(ct.Structure):
    _fields_ = [
        ('type', ct.c_uint),
        ('data', ct.c_char_p),
        ('len', ct.c_uint),
    ]

class PK11SlotInfo(ct.Structure):
    #Structure opaque
    pass

SlotInfoPtr = ct.POINTER(PK11SlotInfo)
SECItemPtr = ct.POINTER(SECItem)

NSS_Init = getfunc(ct.c_int, "NSS_Init", ct.c_char_p)
NSS_Shutdown = getfunc(ct.c_int, "NSS_Shutdown")
PK11_GetInternalKeySlot = getfunc(SlotInfoPtr, "PK11_GetInternalKeySlot")
PK11_FreeSlot = getfunc(None, "PK11_FreeSlot", SlotInfoPtr)
PK11_CheckUserPassword = getfunc(ct.c_int, "PK11_CheckUserPassword", SlotInfoPtr, ct.c_char_p)
PK11SDR_Decrypt = getfunc(ct.c_int, "PK11SDR_Decrypt", SECItemPtr, SECItemPtr, ct.c_void_p)
SECITEM_ZfreeItem = getfunc(None, "SECITEM_ZfreeItem", SECItemPtr, ct.c_int)

Ensuite, énumérez les profils stockés sur votre ordinateur et extrayez uniquement les profils qui contiennent les données de connexion (où logins.json est).


#Énumérer les profils
def getprofiles():
    profdir = os.path.join(os.environ["appdata"], "Mozilla\\Firefox\\Profiles")
    files = os.listdir(profdir)
    profiles = [os.path.join(profdir, f) for f in files if os.path.isfile(os.path.join(profdir, f, "logins.json"))]

    return profiles

Sélectionnez un profil et initialisez NSS avec ce profil.


profiles = getprofiles()
print("Veuillez saisir le numéro de profil.")
for i in range(len(profiles)):
    print("%d: %s" % (i, profiles[i]))
number = int(input("nombre: "))

encprof = profiles[number].encode("utf8")
#Initialisation NSS
e = NSS_Init(b"sql:" + encprof)
if e != 0:
    raise Exception("Échec de l'initialisation de NSS.")

Firefox vous permet de définir un mot de passe principal sur votre profil pour protéger vos informations personnelles. Si un mot de passe principal a été défini, vous devrez vous authentifier avec la fonction PK11_CheckUserPassword.


keyslot = PK11_GetInternalKeySlot()
if not keyslot:
    raise Exception("Impossible d'obtenir Keyslot.")
askpass = input("Veuillez saisir le mot de passe principal: ")
if askpass:
    e = PK11_CheckUserPassword(keyslot, askpass.encode("utf8"))
    if e != 0:
        raise Exception("Le mot de passe principal est incorrect.")
else:
    print("Pas de mot de passe")
PK11_FreeSlot(keyslot)

Ensuite, chargez logins.json. Bien que considérablement omis, la structure de logins.json est la suivante.

{
    "logins": [
        {
            "hostname": "https://id.unity.com",
            "encryptedUsername": "Nom d'utilisateur chiffré 1",
            "encryptedPassword": "Mot de passe crypté 1",
            "encType": 1
        },
        {
            "hostname": "https://accounts.google.com",
            "encryptedUsername": "Nom d'utilisateur chiffré 2",
            "encryptedPassword": "Mot de passe crypté 2",
            "encType": 1
        },
    ]
}

Chargez logins.json et

def getcreds(profile):
    db = os.path.join(profile, "logins.json")
    with open(db) as fh:
        data = json.load(fh)
        try:
            logins = data["logins"]
        except Exception:
            raise Exception("{0}Ne peut pas être lu.".format(db))

        for i in logins:
            yield (i["hostname"], i["encryptedUsername"],
                   i["encryptedPassword"], i["encType"])

Enfin, décryptez et affichez-le, puis fermez NSS.

for url, user, passw, enctype in getcreds(profiles[number]):
    if enctype:
        user = decode(user)
        passw = decode(passw)
        print("Url: " + url)
        print("Username: " + user)
        print("Password: " + passw)

NSS_Shutdown()

Le contenu de la fonction de décodage essentielle de décodage est le suivant.

def decode(data64):
    data = b64decode(data64)
    inp = SECItem(0, data, len(data))
    out = SECItem(0, None, 0)

    e = PK11SDR_Decrypt(inp, out, None)
    try:
        if e == -1:
            print("Le déchiffrement a échoué.")
            exit()

        res = ct.string_at(out.data, out.len).decode("utf8")
    finally:
        #Libérer SECItem
        SECITEM_ZfreeItem(out, 0)

    return res

Placez les données décodées en Base64 dans la structure SECItem et transmettez-les à la fonction PK11SDR_Decrypt, et les données déchiffrées seront renvoyées.

Programme pour lire les données de connexion

Le programme qui génère les données de connexion Firefox est le suivant.

import ctypes as ct
import os
import json
from base64 import b64decode

#Chargement de DLL
dllpath = os.path.join(os.environ["programfiles"], "Mozilla Firefox\\nss3.dll")
nss3 = ct.CDLL(dllpath)

#Charge de fonction
def getfunc(restype, name, *argtypes):
    res = getattr(nss3, name)
    res.restype = restype
    res.argtypes = argtypes
    return res

class SECItem(ct.Structure):
    _fields_ = [
        ('type', ct.c_uint),
        ('data', ct.c_char_p),
        ('len', ct.c_uint),
    ]

class PK11SlotInfo(ct.Structure):
    #Structure opaque
    pass

SlotInfoPtr = ct.POINTER(PK11SlotInfo)
SECItemPtr = ct.POINTER(SECItem)

NSS_Init = getfunc(ct.c_int, "NSS_Init", ct.c_char_p)
NSS_Shutdown = getfunc(ct.c_int, "NSS_Shutdown")
PK11_GetInternalKeySlot = getfunc(SlotInfoPtr, "PK11_GetInternalKeySlot")
PK11_FreeSlot = getfunc(None, "PK11_FreeSlot", SlotInfoPtr)
PK11_CheckUserPassword = getfunc(ct.c_int, "PK11_CheckUserPassword", SlotInfoPtr, ct.c_char_p)
PK11SDR_Decrypt = getfunc(ct.c_int, "PK11SDR_Decrypt", SECItemPtr, SECItemPtr, ct.c_void_p)
SECITEM_ZfreeItem = getfunc(None, "SECITEM_ZfreeItem", SECItemPtr, ct.c_int)

#Processus de décryptage
def decode(data64):
    data = b64decode(data64)
    inp = SECItem(0, data, len(data))
    out = SECItem(0, None, 0)

    e = PK11SDR_Decrypt(inp, out, None)
    try:
        if e == -1:
            print("Le déchiffrement a échoué.")
            exit()

        res = ct.string_at(out.data, out.len).decode("utf8")
    finally:
        #Libérer SECItem
        SECITEM_ZfreeItem(out, 0)

    return res

#Énumérer les profils
def getprofiles():
    profdir = os.path.join(os.environ["appdata"], "Mozilla\\Firefox\\Profiles")
    files = os.listdir(profdir)
    profiles = [os.path.join(profdir, f) for f in files if os.path.isfile(os.path.join(profdir, f, "logins.json"))]

    return profiles

#Chargement Json
def getcreds(profile):
    db = os.path.join(profile, "logins.json")
    with open(db) as fh:
        data = json.load(fh)
        try:
            logins = data["logins"]
        except Exception:
            raise Exception("{0}Ne peut pas être lu.".format(db))

        for i in logins:
            yield (i["hostname"], i["encryptedUsername"],
                   i["encryptedPassword"], i["encType"])

def main():
    profiles = getprofiles()
    print("Veuillez saisir le numéro de profil.")
    for i in range(len(profiles)):
        print("%d: %s" % (i, profiles[i]))
    number = int(input("nombre: "))

    #Initialisation NSS
    encprof = profiles[number].encode("utf8")
    e = NSS_Init(b"sql:" + encprof)
    if e != 0:
        raise Exception("Échec de l'initialisation de NSS.")

    #Authentification par mot de passe
    keyslot = PK11_GetInternalKeySlot()
    if not keyslot:
        raise Exception("Impossible d'obtenir Keyslot.")
    askpass = input("Veuillez saisir le mot de passe principal: ")
    if askpass:
        e = PK11_CheckUserPassword(keyslot, askpass.encode("utf8"))
        if e != 0:
            raise Exception("Le mot de passe principal est incorrect.")
    else:
        print("Pas de mot de passe")
    PK11_FreeSlot(keyslot)

    #Décrypter et sortir
    for url, user, passw, enctype in getcreds(profiles[number]):
        if enctype:
            user = decode(user)
            passw = decode(passw)
            print("Url: " + url)
            print("Username: " + user)
            print("Password: " + passw)

    NSS_Shutdown()

main()

Maintenant, exécutons le programme.

Lorsque vous l'exécutez, il vous sera demandé le profil à charger, alors entrez ce numéro.

Veuillez saisir le numéro de profil.
0: C:\Users\admin\AppData\Roaming\Mozilla\Firefox\Profiles\aaaaaaaa.TestProfile
1: C:\Users\admin\AppData\Roaming\Mozilla\Firefox\Profiles\bbbbbbbb.default-release
nombre: 

Entrez ensuite le mot de passe principal. S'il n'est pas défini, appuyez simplement sur la touche Entrée.

Veuillez saisir le mot de passe principal:

Ensuite, les données de connexion seront sorties.

Url: https://id.unity.com
Username: admin
Password: SecurePass9999

Url: https://accounts.google.com
Username: [email protected]
Password: passwd314159

en conclusion

Donc, cette fois, j'ai essayé d'analyser les données de connexion de Firefox. On a l'impression que c'était plus facile que Chrome ou Edge / IE, mais je pense que la définition d'un mot de passe principal vous donnera une sécurité plus forte que tout autre navigateur.

Actuellement, il ne semble y avoir aucun moyen de déchiffrer directement le mot de passe principal, donc en conclusion, il semble plus sûr de définir et d'utiliser le mot de passe principal dans Firefox.

À part: À propos du problème qui n'a pas pu être implémenté en C

Le code suivant est le code que j'ai essayé d'implémenter en C #.


using System;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using Newtonsoft.Json;

namespace FirefoxDecrypt
{
    struct SECItem
    {
        public uint type;
        public byte[] data;
        public uint len;
    }

    public class Logins
    {
        public Credential[] logins { get; set; }
    }

    public class Credential
    {
        public string hostname { get; set; }
        public string encryptedUsername { get; set; }
        public string encryptedPassword { get; set; }
        public int encType { get; set; }
    }

    class Program
    {
        const string nss = "C:\\Program Files\\Mozilla Firefox\\nss3.dll";
        const string profile = "C:\\Users\\user01\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\xxxxxxxx.default-release";

        [DllImport(nss)]
        static extern int NSS_Init(string s);
        [DllImport(nss)]
        static extern int NSS_Shutdown();
        [DllImport(nss)]
        static extern int PK11SDR_Decrypt(ref SECItem s1, ref SECItem s2, IntPtr ptr);
        [DllImport(nss)]
        static extern void SECITEM_ZfreeItem(ref SECItem s, int i);

        static void Main(string[] args)
        {
            int e = NSS_Init("sql:" + profile);
            Console.WriteLine("NSS_Init: " + e);

            string db = Path.Combine(profile, "logins.json");
            string v = File.ReadAllText(db);
            var data = JsonConvert.DeserializeObject<Logins>(v);
            foreach(var login in data.logins)
            {
                if(login.encType == 1)
                {
                    var user = Decode(login.encryptedUsername);
                    var pass = Decode(login.encryptedPassword);
                    Console.WriteLine("URL: " + login.hostname);
                    Console.WriteLine("Username: " + user);
                    Console.WriteLine("Password: " + pass);
                }
            }

            NSS_Shutdown();
            Console.ReadKey(true);

        }
        static string Decode(string b64)
        {
            var bin = Convert.FromBase64String(b64);

            //Peut-être qu'il y a un problème ici
            SECItem inp = new SECItem { data = bin, len = (uint)bin.Length, type = 0 };
            SECItem outs = new SECItem { data = null, len = 0, type = 0 };

            var e = PK11SDR_Decrypt(ref inp, ref outs, IntPtr.Zero);
            Console.WriteLine("PK11SDR_Decrypt: " + e);

            var res = Encoding.UTF8.GetString(outs.data);
            SECITEM_ZfreeItem(ref outs, 0);
            return res;
        }
    }
}

Il devrait traiter presque le même que la version Python, mais lorsque j'appelle PK11SDR_Decrypt de la méthode Decode, le code d'erreur -1 est inévitablement renvoyé. Il n'y avait pas de problème avec les données décodées en Base64, donc je pense qu'il y a un problème avec la structure SECItem, mais à la fin j'ai abandonné sans connaître la cause. Si quelqu'un connaît la cause, faites-le moi savoir dans les commentaires. ~~ N'est-ce pas dans la pierre ... ~~

Les références

Recommended Posts

Essayez de déchiffrer les données de connexion stockées dans Firefox
Essayez de mettre des données dans MongoDB
Essayez Cython dans les plus brefs délais
Essayez d'afficher les données ferroviaires des informations numériques des terres nationales en 3D
Connectez-vous au site Web en Python
Essayez de gratter les données COVID-19 Tokyo avec Python
Différentes façons de calculer la similitude entre les données avec python
Essayez d'extraire les mots-clés populaires dans COTOHA
Essayez de déchiffrer les caractères déformés dans le nom du fichier joint avec Python
La méthode minimale à retenir lors de l'agrégation de données avec Pandas
Essayez d'extraire les caractéristiques des données de capteur avec CNN
Programmation pour combattre dans le monde ~ 5-1
Programmer pour combattre dans le monde 5-3
Programmation pour combattre dans le monde - Chapitre 4
Dans la commande python, python pointe vers python3.8
Essayez d'introduire le thème sur Pelican
Essayez de calculer Trace en Python
Essayez de modéliser le rendement cumulatif du roulement dans le trading à terme
Vérifiez le résumé des données dans CASTable
Le moyen le plus rapide d'essayer EfficientNet
Essayez de résoudre l'itinéraire le plus court avec les données sociales Python + NetworkX +
Programmation pour combattre dans le monde ~ 5-2
La façon la plus simple d'essayer PyQtGraph
Essayez le livre "Introduction au développement d'applications de traitement du langage naturel en 15 étapes" - Chapitre 4 Étape 15 Mémo "Collecte de données"
Essayez d'obtenir l'état de la surface de la route en utilisant de grandes données de gestion de la surface de la route
Essayez de vous connecter à qiita avec Python
Essayez d'utiliser l'API Wunderlist en Python
Essayez d'utiliser l'API Kraken avec Python
Essayez d'utiliser la bande HL dans l'ordre
Essayez de faire face à la somme partielle
Essayez de travailler avec des données binaires en Python
Dans Jupyter, ajoutez IPerl au noyau.
J'ai essayé de sauvegarder les données récupérées au format CSV!
Essayez de convertir en données ordonnées avec les pandas
Python amateur tente de résumer la liste ①
Essayez le nouveau chaînage du planificateur dans PyTorch 1.4
Divers commentaires à écrire dans le programme
Essayez d'accéder à l'API Spotify dans Django.
Livres sur la science des données à lire en 2020
[Django memo] Je souhaite définir à l'avance les informations de l'utilisateur connecté dans le formulaire.
Essayez d'afficher la séquence de Fibonacci dans différentes langues comme pratique d'algorithme
Utilisez PIL en Python pour extraire uniquement les données souhaitées d'Exif
Je suis accro à la différence dans la façon dont Flask et Django reçoivent les données JSON
La première étape de l'analyse du journal (comment formater et mettre les données du journal dans Pandas)
Essayez de résoudre le problème du fizzbuzz avec Keras
Comment utiliser la bibliothèque C en Python
[Introduction au modèle SEIR] Essayez d'ajuster les données COVID-19 ♬
Connectez-vous à un serveur distant avec SSH
Essayez d'implémenter Oni Mai Tsuji Miserable avec python
Essayez d'ajouter la distorsion de l'objectif fisheye à l'image
Calculons en fait le problème statistique avec Python
3.14 π jour, alors essayez de sortir en Python
Essayez d'agréger les données de musique doujin avec des pandas
Essayez de décomposer la procession du daimyo en Tucker
Essayez de résoudre le problème de l'héritage de classe Python