Credential Gathering From Third-Party Software

Clock Icon 14 min read
Related Products

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.

The red box highlights the "Save password" option in WinSCP, which is specifically listed as "not recommended."
Figure 1. Password saving is not recommended by WinSCP.

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:

Steps to decrypt a password in WinSCP include performing the XOR with 0xA3 and finding the complement, as shown in the table.
Figure 2. Decrypting a password stored in WinSCP.

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.

The screenshot shows how to decrypt the Password registry value from the index in WinSCP: start index + username length + hostname length to length. The result is the password.
Figure 3. Location of username, hostname and password, concatenated.

In the wild

We have seen the following script executed in multiple customer environments:

A suspicious PowerShell script that Unit 42 has observed in multiple environments. The decoded script attempts to decrypt WinSCP passwords.
Figure 4. Suspicious PowerShell with encoded command.
  • -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:

Decoded version of PowerShell script used for credential gathering.
Figure 5. PowerShell script for extracting and decrypting WinSCP’s passwords.


Red boxes show how the Credential Gathering Protection module in Cortex XDR identifies a suspicious registry operation.
Figure 6. Cortex XDR prevented attempts to read passwords stored in WinSCP.

Software: Git

Tested version:

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:


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.

A red box highlights the contents of the Password tag in the credentialsProfile XML tags in RDCMan.settings
Figure 7. Looking at the XML tags in RDCMan.settings

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:

  1. Open the RDCMan.settings file and check for the password XML tag.
  2. Decode the string in the tag with base64.
  3. Call CryptUnprotectData with the decoded password string.
  4. Decode the result using UTF-8 (or other relevant formats).
  5. Remove unnecessary null characters.

Looking at the example above, the password saved in the file was:


And the decrypted password is Aa123456.

The red box highlights the results of password decryption efforts that complete credential gathering targeting RDCMan.
Figure 8. Recovering a password from RDCMan.

Software: OpenVPN

Tested version:


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.

Above, the PowerShell script for recovering an OpenVPN password. Below, the way auth-data and entropy registry values are shown via reg.exe
Figure 9. Above, the PowerShell script for recovering an OpenVPN password. Below, the way auth-data and entropy registry values are shown via reg.exe (POC).

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.

The screenshot shows a redacted version of the screen that appears when checking stored passwords in Google Chrome settings.
Figure 10. Passwords saved by Google Chrome Version 102.0.5005.115 (Official Build) (64-bit).

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:

  1. The encrypted password.
  2. The initialization vector.
  3. 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:

Example from Google Chrome of a password saved in the local state JSON file of Google Chrome. Visible phrases include os_crypt, encrypted_key and password_manager.
Figure 11. Password that is being saved in local state JSON file of Google Chrome.

It is being saved with a prefix of five letters at the beginning: DPAPI.

The decoded password is shown. Highlighted in red at the beginning is a prefix of five letters: DPAPI
Figure 12. The decoded password that was saved in Google Chrome.

If the attacker is able to run in the context of the user, all that is necessary to complete gathering user credentials is:

  1. Copy both login data and local state files.
  2. Get the AES GCM key from the local state JSON file.
  3. Decode (base64), decrypt (CryptUnprotectData) and remove the padding from the key.
  4. Decrypt each password in the login data database, using the decrypted AES GCM key.
A proof of concept for recovering passwords that were saved in Chrome - the screenshot shows the outcome of, with sensitive information redacted.
Figure 13. POC for recovering passwords that were saved in Chrome.

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:


(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
Red boxes highlight the Credential Gathering module and the key file paths observed.
Figure 14. Cortex XDR detected attempts to read passwords saved in the Microsoft Edge browser.

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.

Screenshot of stored passwords in Mozilla Firefox with key info redacted.
Figure 15. Passwords that are being saved by Firefox Version 101.0.1 (64-bit).

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.

The screenshot shows encryptedUsername and encryptedPassword, among other logins data
Figure 16. Password saved in the logins.json file of Firefox.

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:

  1. The attacker copies the logins.json file.
  2. Loads the NSS library (nss3.dll)
  3. Decodes (base64) the encryptedUsername and encryptedPassword from the copy of logins.json.
  4. Stores each of the inputs in a SecItem object, which is later used throughout NSS to pass blocks of binary data back and forth.
  5. Creates SecItem objects for output.
  6. 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.

Red boxes highlight where User S is able to gather credentials from User D.
Figure 17. User S got passwords belonging to user D that were saved in Firefox profile 2. (POC)

In the wild

We have seen the following script executed:

A suspicious obfuscated PowerShell script that attempts to gather credentials from Mozilla Firefox.
Figure 18. Suspicious obfuscated PowerShell script.

After decoding:

The deobuscated PowerShell script reveals a series of links as shown
Figure 19. De-obfuscated PowerShell script.

The script:

  1. Creates the folder %localappdata%\ujXgAD
  2. 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
  3. 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:


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:

B. The DLL executes a couple of discovery commands:

    1. systeminfo – To list machine information.
    2. ipconfig /all – To list all network interfaces on the machine.
    3. 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:

  1. One of them has a new random name but is still signed by Microsoft.
  2. 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:

Certutil SHA256 - Note that this file is benign:

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:

DLLs downloaded as part of this credential gathering attack include lw1JF63zARLUV8UwpwGnWpgg.dll and RwuuPYoVei7FkJB.dll
Figure 20. DLLs that we were able to download.
  • First downloaded DLL:
    Downloaded filename: RwuuPYoVei7FkJB.dll
    (SHA256: A1D513E4A5C83895E5769C994C4D319959EF5AE3F679CE6C0C5211B5BECA7695)
  • Second downloaded DLL:
    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.

Red boxes highlight how the Credential Gathering Protection module identifies key file paths.
Figure 21. Cortex XDR prevented attempts to read passwords saved in the Firefox browser.

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.

Red boxes how the Cortex XDR Credential Gathering Protection module identifies key file paths.
Figure 22. Cortex XDR detected attempts to read passwords saved in the Microsoft Edge browser.
Red boxes highlight how the Credential Gathering Module identifies key file paths.
Figure 23. Cortex XDR detected attempts to read passwords saved in the Google Chrome browser.


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.

WebBrowserPassView.exe shows usernames, passwords and the file path that stores each of them. While sensitive info is redacted, the web browser from which each password was taken is visible.
Figure 24. WebBrowserPassView.exe shows usernames, passwords and the file path that stores each of them.

We can see the login data file for Chromium-based browsers, and the logins.json file for the Firefox browser.

Red boxes highlight how the Credential Gathering Protection module identifies key file paths.
Figure 25. Cortex XDR prevented attempts to read browsers' saved passwords by WebBrowserPassView.exe.


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
  • HKCU\software\martin prikryl\winscp 2\sessions\<session_name>\Password
  • HKCU\software\openvpn-gui\configs\<session_name>\auth-data
  • HKCU\software\openvpn-gui\configs\<session_name>\entropy
Unauthorized access to the following files
  • <userprofile>\.git-credentials
  • <userprofile>\.config\git\credentials
  • %localappdata%\Microsoft\Remote Desktop Connection Manager\RDCMan.settings
  • %localappdata%\google\chrome\user data\<PROFILE>\login data
  • %localappdata%\microsoft\edge\user data\<PROFILE>\login data
  • %appdata%\opera software\opera stable\<PROFILE>\login data
  • %localappdata%\google\chrome\user data\local state
  • %localappdata%\microsoft\edge\user data\local state
  • %appdata%\opera software\opera stable\local state
  • %appdata%\mozilla\firefox\profiles\<PROFILE>\logins.json
  • %appdata%\mozilla\firefox\profiles\<PROFILE>\key<3/4>.json
Malicious hashes




Additional Resources

Enlarged Image