This post is also available in: 日本語 (Japanese)
Executive Summary
There is a constant debate between usability and security in the software world. Many third-party programs can make their users’ lives easier and save them time by storing their credentials. However, as it turns out, this convenience often comes at the price of poor security, causing the risk of password theft. Credentials gathered in this manner can then be used during an actual cyberattack.
In this article, we will explain the dangers of credential theft. We will examine some common third-party software scenarios related to credential gathering, looking into how passwords are stored, how they can be retrieved and how to monitor these actions based on real-world attack scenarios.
Cortex XDR Customers are protected from such attacks using the Credential Gathering Protection Module released in Cortex 3.4 on Windows, Linux and MacOS agents.
Related Unit 42 topics | Credential harvesting |
The Dangers of Credential Theft: How Attackers Can Expand Their Access
It is clear that credential theft is bad. However, it is important to emphasize the scale of the impact that credential theft can have.
Many people tend to use the same password in different programs and rarely change their passwords. When the time comes to modify their passwords, many people follow a predictable pattern.
Thus, when attackers can get a password from one source, they can try to use it against other resources, including some that are more protected. So, for every program A that is well secured, the user could use the same password or pattern on program B that is less secure – which could result in making program A less secure.
Furthermore, if it turns out that a person is using their operating system password in other less secure locations, a whole new world of possibilities is open to the attacker.
Let’s say, for example, that person X uses the same password for his Windows account domain and a Linux FTP file server. In this scenario, person X uses the common program WinSCP to manage their files in the file server. Although WinSCP advises that saving passwords isn’t recommended, person X accesses this file server every week, so they prefer to save time and save their password.
As we will demonstrate later in this blog, the user’s password can easily be retrieved from where WinSCP stores it. An attacker who can get a foothold on X’s personal computer can get their domain account password – only because it is being saved insecurely. This is on top of the fact that the password is valid for connecting to the file server. This file server may contain files with sensitive information to which the attacker now has access. From there, the attacker can use tools like BloodHound to estimate how far they can spread within an organization.
Credential Gathering in Practice
Software: WinSCP
WinSCP is a popular SFTP client and FTP client for Microsoft Windows that is used to copy files between local Windows computers and remote servers using FTP, FTPS, SCP and SFTP.
Tested version:
5.19.6 (Build 12002 2022-02-22)
Where are credentials stored?
WinSCP stores the encrypted user’s password under the registry key HKCU\software\martin prikryl\winscp 2\sessions\<session_name> in a value called Password.
How can the credentials be recovered?
WinSCP performs symmetric mathematical operations on the bits of the user’s passwords. It takes each byte of the password, computes the complement to 0xFF (11111111), and after that, XORs it with the byte 0xA3 (10100011).
The encryption process comprises finding the complement and performing the XOR one time. The password is then stored in the Password registry value. Since these mathematical operations are symmetric, all we need to do is perform the same two operations once again, in reverse order, to get the original value.
For example, let's take a commonly used password: Aa123456. This is how WinSCP will store this password: 1D3D6D6E6F68696A.
In Figure 2, we see the steps to decrypt the password:
The password is saved along with the HostName and UserName. To get it from the Password registry value, we must find the index at the beginning of the password. This calculation is pretty easy – depending on the WinSCP version, the first or third byte of the registry value is the length of the username, hostname and password, concatenated. The start index is the following byte to the length, and its value is multiplied by two. Both the length and the start index are encrypted in the same way.
The UserName and HostName are also saved on different registry values, so we know their length and value. All we do is decrypt the Password registry value from the index: start index + username length + hostname length to length, and we will get our password.
In the wild
We have seen the following script executed in multiple customer environments:
- -enc stands for EncodedCommand, meaning that a base-64-encoded string is used as the command.
In the decoded script, we can see an attempt to decrypt WinSCP passwords:
Software: Git
Tested version:
2.35.1.windows.2
Where are credentials stored?
Git allows for the use of both passwords and Personal Access Tokens (PATs).
When users want to save time by saving their Git credentials, they can do it using the following command:
git config credential.helper 'store'
Using this command, Git will save the user’s credentials indefinitely on disk, in plain text.
Possible files containing passwords:
- <userprofile>\.git-credentials
- <userprofile>\.config\git\credentials
Git allows using PATs as credentials instead of the traditional use of passwords. These tokens are more modular, as any number of access tokens can be created, each with different permissions and expiration dates.
Although it is possible to control users’ actions in a more modular and granular way, each associated with a specific PAT, anyone who has the user’s PAT can view all repositories to which the user has access.
These tokens also appear in cleartext in the same files mentioned above.
How can the credentials be recovered?
Anyone who reads these files will see the username, password or token, and relevant Git repository in plain text.
Software: RDCMan
Tested version:
2.83
RDCMan manages multiple remote desktop connections. It is useful for managing server labs where you need regular access to machines, such as automated check-in systems and data centers.
Where are credentials stored?
When a user decides to save a password for a session using RDCMan, the default configuration file will be %localappdata%\Microsoft\Remote Desktop Connection Manager\RDCMan.settings
This file is an XML file that contains general metadata about each RDP connection.
Among the data, there is an XML tag called CredentialsProfiles, which has attracted our attention.
We can see that under this tag, there is another one called CredentialsProfiles, and inside there are credentialsProfile XML tags, with a Password tag.
How can the credentials be recovered?
To retrieve the password, we will have to execute commands in the context of the person using the RDCMan program. This is because the password is being saved using the DATA Protection API (DPAPI), which enables symmetric encryption and decryption of any kind of data using the functions CryptProtectData and CryptUnprotectData, respectively.
So, to get the password, we need to call the function CryptUnprotectData.
Usually, the only user who can decrypt the data is a user with the same login credentials as the user who encrypted the data.
Although gathering credentials from RDCMan requires an additional step from the attacker than was needed in some of our other examples – running the software in the context of the relevant user – there is great value to the result for the attacker. If the effort is successful, it’s possible for the threat actor to get all the users and passwords for all of the machines that this specific user connects to.
Once the attacker is able to execute commands in the context of the user, all that remains in order to gather credentials is to:
- Open the RDCMan.settings file and check for the password XML tag.
- Decode the string in the tag with base64.
- Call CryptUnprotectData with the decoded password string.
- Decode the result using UTF-8 (or other relevant formats).
- Remove unnecessary null characters.
Looking at the example above, the password saved in the file was:
AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA8/nnW5aFNUi0AKiTG4y9UQAAAAACAAAAAAAQZgAAAAEAACAAAADIjLLw0X4z9RDdWgPpqabLU7hTcJ1HVlFklpzX3eA14QAAAAAOgAAAAAIAACAAAAB01OvDCNCjaEhrq8J8hRm/SKycef7nR52ZkqcPLJqMsCAAAACg2htaeRsutDziS3FISeEAg3DsBpGxBGpPeWlUSVnXOkAAAAB5Tei9g5KWcVIhOKQ2cXxr5ONUOHMEEH5h3Lmp12mPlWaaZ6y8dGIVz8WnNKr4e73dhqNU8NyzI7RZBamS6DG6
And the decrypted password is Aa123456.
Software: OpenVPN
Tested version:
2.5.029
OpenVPN is a virtual private network system that implements techniques to create secure point-to-point or site-to-site connections in routed or bridged configurations and remote access facilities.
Where are credentials stored?
OpenVPN stores the user’s password under the registry key HKCU\software\openvpn-gui\configs\<session_name> in a value called auth-data.
How can the credentials be recovered?
OpenVPN also uses the DPAPI mechanism, with the additional optional entropy parameter (which can be set to NULL).
When an optional entropy DATA_BLOB structure was used in the encryption phase, that same DATA_BLOB structure must be used for the decryption phase.
In the case of OpenVPN, the entropy is saved in a registry value called entropy. The entropy registry value is also stored in the path HKCU\software\openvpn-gui\configs\<session_name>
So, calling CryptUnprotectData with the password from auth-data and entropy (from entropy) will give us the session password.
The entropy registry value contains an extra byte of 00, so we just need to omit it.
Software: Chromium-based Browsers
Tested version:
- Google Chrome – Tested version: 103.0.5060.53 (Official Build) (64-bit)
- Microsoft Edge – Tested version: 103.0.1264.37 (Official Build) (64-bit)
- Opera – Tested version: 88.0.4412.53
The Chromium projects include Chromium, the open-source project behind the Google Chrome browser.
In a typical usage routine, many people tend to save passwords while surfing the internet.
Where are credentials stored?
When using a Chromium-based browser, like Microsoft Edge, Opera or Google Chrome, passwords are located encrypted in an SQLite database file, usually called login data.
Each profile has a password database – its login data file.
The key used to encrypt the passwords is located in the parent folder, in a JSON file called local state.
For example:
login data locations:
- Google Chrome: %localappdata%\google\chrome\user data\<PROFILE>\login data
- Microsoft Edge: %localappdata%\microsoft\edge\user data\<PROFILE>\login data
- Opera: %appdata%\opera software\opera stable\<PROFILE>\login data
local state locations:
- Google Chrome: %localappdata%\google\chrome\user data\local state
- Microsoft Edge: %localappdata%\microsoft\edge\user data\local state
- Opera: %appdata%\opera software\opera stable\local state
How can the credentials be recovered?
Each password in the login data database is encrypted using the Advanced Encryption Standard (AES), with GCM mode. AES GCM is a symmetrical encryption method, so the same key is valid for both encryption and decryption. The AES algorithm uses a different key for every 128-bit block, which is based on the calculation of the previous block. For the first block, there is an option to use the Initialization Vector (IV).
To decrypt a password that a Chromium-based browser saves, we need to have:
- The encrypted password.
- The initialization vector.
- The AES key.
Let’s see how we can retrieve each of those:
A. The encrypted password.
Can be exported from the login data database – the encrypted password is taken from the password_value column, from the letter in the 15th position to the end – 16 letters. [15:-16]
B. The initialization vector.
Located in the same password_value field column, from the letter in the third position to the letter in the 15th position. [3:15]
C. The AES key.
Written in the local state JSON file, under keys os_crypt and encrypted_key, decoded with base64.
Chromium-based browsers save the AES key using the DPAPI mechanism, so to get it, we will have to decode it from base64 and use CryptUnprotectData in the user’s context.
Example from Google Chrome:
It is being saved with a prefix of five letters at the beginning: DPAPI.
If the attacker is able to run in the context of the user, all that is necessary to complete gathering user credentials is:
- Copy both login data and local state files.
- Get the AES GCM key from the local state JSON file.
- Decode (base64), decrypt (CryptUnprotectData) and remove the padding from the key.
- Decrypt each password in the login data database, using the decrypted AES GCM key.
You can read more about how to extract Chrome passwords in Python.
In the wild
We have seen the following DLL running from excel.exe using regsvr32.exe with the following command line:
C:\windows\system32\regsvr32.exe
C:\users\<username>\appdata\local\uolegxnwf\kgnkudbadmpogg.dll
(SHA256 of kgnkudbadmpogg.dll: 6599FEE8C7ADF30A00889A7070600F472F8CEAD8EA4DD1A85E724ED15F2AED0F)
After a chain of events, the final payload was trying to access Microsoft Edge credentials files:
- The login data file (SQLite database file)
C:\users\<username>\appdata\local\microsoft\edge\user data\default\login data
- The local state file (contains the encryption key)
C:\users\<username>\appdata\local\microsoft\edge\user data\local state
Software: Firefox Browser
Tested version:
Firefox Version 101.0.1 (64-bit)
The password-saving behavior pattern is also relevant when using other browsers, such as Mozilla Firefox.
Where are credentials stored?
Similar to Chromium-based browsers, in the Mozilla Firefox browser, each profile also has its own password file.
This file is called logins.json and is located in %appdata%\mozilla\firefox\profiles\<PROFILE>\logins.json
Both username and password are saved encrypted.
How can the credentials be recovered?
Each username and password in the logins.json file is encrypted using the PKCS #11 cryptography standard. Firefox has developed the NSS library to adopt this standard into its browser (nss3.dll).
NSS stores private keys in a file called key3.db or key4.db, depending on the NSS version.
To retrieve the user’s passwords, the attacker will have to access one of these files and the logins.json file.
So, if the attacker can gain access to run on the same machine, the process of stealing the passwords will be:
- The attacker copies the logins.json file.
- Loads the NSS library (nss3.dll)
- Decodes (base64) the encryptedUsername and encryptedPassword from the copy of logins.json.
- Stores each of the inputs in a SecItem object, which is later used throughout NSS to pass blocks of binary data back and forth.
- Creates SecItem objects for output.
- Decrypts each encryptedUsername and encryptedPassword input object, and stores the data in the new SecItem output objects, using the PK11 decryption function from nss3.dll.
Unlike the case of Chromium-based browsers, the attacker doesn’t have to run in a user’s context to get the person’s passwords, but can take advantage of any user who has permission to access the file system profile of the target user.
In the wild
We have seen the following script executed:
After decoding:
The script:
- Creates the folder %localappdata%\ujXgAD
- Tries to create Invoke-WebRequest for each of the links in $Links, downloads a DLL and saves it in the folder mentioned in step A with the name rRXqwGvGNR.wTj
- Breaks after the first successful execution.
Next, we saw that a DLL was created on the endpoint and regsvr32.exe was used with the following command line:
C:\WINDOWS\system32\regsvr32.exe
C:\Users\<USERNAME>\AppData\Local\Temp\..\ujXgAD\rRXqwGvGNR.wTj
Note that the path has an evasion in it: By using \..\ to go back to the Local folder, the attacker avoids accessing it directly.
After using regsvr32.exe:
A. The DLL copies itself to a random folder with a random name, with a DLL extension:
C:\Users\<USERNAME>\AppData\Local\<random_folder_name>\<random_dll_name>.dll
B. The DLL executes a couple of discovery commands:
-
- systeminfo – To list machine information.
- ipconfig /all – To list all network interfaces on the machine.
- nltest.exe /dclist: – To list all domain controllers in the domain.
C. The DLL creates and executes two files based on certutil.exe with random names:
- One of them has a new random name but is still signed by Microsoft.
- The other one is a mangled version of certutil – keeping the original name, but with different functionality, and no signature.
D. Step C above is done twice.
Unsigned file SHA256:
A88C344F3F80F8A3EA2E9BA0687FEBCEE2A730FD9AC037D54C4FD21C0AB91039
Certutil SHA256 - Note that this file is benign:
D252235AA420B91C38BFEEC4F1C3F3434BC853D04635453648B26B2947352889
The unsigned certutil.exe then tries to access password files, both for Chromium-based and Firefox-based browsers.
When checking the links from Figure 19, only two links worked:
- First downloaded DLL:
hxxps://www[.]yell[.]ge/nav_logo/AEnTP/
Downloaded filename: RwuuPYoVei7FkJB.dll
(SHA256: A1D513E4A5C83895E5769C994C4D319959EF5AE3F679CE6C0C5211B5BECA7695)
- Second downloaded DLL:
hxxps://yakosurf[.]com/wp-includes/S/
Downloaded filename: lw1JF63zARLUV8UwpwGnWpgg.dll
(SHA256: 1B8638333751EFCB6B5332C801C11DF0DE3D7077C6ACEA1D663C0302519D7172)
In both cases, it is actually the same DLL, except for a small difference that changes the SHA256 hash.
Looking into this sample, we identified the first DLL as part of the Emotet malware family.
Cortex XDR stops this operation synchronously, so the next attack stages are not performed. This malware tries to read passwords in this order: first Firefox, then Microsoft Edge and later, Google Chrome.
For the demonstration, we will illustrate Cortex XDR with report mode. We will see that the Credential Gathering Protection Module also detects attempts to read Chromium-based browsers' saved passwords.
Emotet?
Since we saw two different cases involving Emotet, we looked a bit deeper into this malware family and its methods for third-party credential gathering. We saw that sometimes malware does not even need to implement all the logical conditions on its own. It can just wrap existing tools, like the WebBrowserPassView Nirsoft tool, to reveal the passwords stored by the web browsers.
We can see the login data file for Chromium-based browsers, and the logins.json file for the Firefox browser.
Conclusion
It turns out that the way certain third-party software stores credentials is less secure than we thought. Most of these programs store the user’s credentials on the local disk, via file or registry values. This fact can be the one weak spot in the chain that attackers wish to find, giving them the access to perform an attack against an organization.
Palo Alto Networks customers using Cortex XDR receive protections using the new Credential Gathering Protection Module for the scenarios described above as well as other credential gathering techniques not mentioned in this write-up. Additional layers of protection – including Local Analysis, Behavioral Threat Protection, BIOC and Analytics BIOCs rules – are also available.
Palo Alto Networks customers that use WildFire receive protections from tools implementing these credential gathering attempts.
Nirsoft tools are marked as grayware in WildFire and are blocked by the XDR Agent.
Indicators of Compromise
Unauthorized access to the following registry values |
|
Unauthorized access to the following files |
|
Malicious hashes |
6599FEE8C7ADF30A00889A7070600F472F8CEAD8EA4DD1A85E724ED15F2AED0F
A88C344F3F80F8A3EA2E9BA0687FEBCEE2A730FD9AC037D54C4FD21C0AB91039 A1D513E4A5C83895E5769C994C4D319959EF5AE3F679CE6C0C5211B5BECA7695 1B8638333751EFCB6B5332C801C11DF0DE3D7077C6ACEA1D663C0302519D7172 |
Additional Resources
- Detecting Credential Stealing with Cortex XDR
- git-credential-store Documentation
- Git Tools - Credential Storage
- GitHub Docs: Creating a Personal Access Token
- CryptUnprotectData function (dpapi.h)
- How Emotet is altering techniques in response to Microsoft’s tightening of Workplace macro safety
- How to crack Firefox passwords with Python
- servo/nss