Summary
Software applications that implement cryptography need to create and store cryptographic keys and possibly certificates to properly operate and service their clients. These keys and certificates might be stored in memory while the application uses them or stored in a permanent store for later use. In either case, developers must take the appropriate security measures to limit the access to this store also known as keystore. An insecure keystore bug allows an attacker to read cryptographic material such as keys and certificates from the keystore to use them during a cryptographic attack.
Follow these steps to test for insecure keystore bugs:
- Step 1: Understand attack scenarios
- Step 2: Analyze causes and countermeasures
- Step 3: Execute test cases
Step 1: Understand attack scenarios
During this first step, it is necessary to review the different attack scenarios for insecure keystore bugs:
- Temporary data stores
- Persistent data stores
Scenario 1: Temporary data stores
An application can either store cryptographic keys persistently or temporarily use them by placing them in memory. In both cases, the location that the application uses to store the crypto keys must be protected from unauthorized users. During this scenario, the attacker sniffs the different sources of temporary data stores such as system memory (RAM or cache) as well as page/swap files and crash dump files. The attacker’s goal is to find the location of the keystore to recover a key in order to mount further cryptographic attacks using the stolen key.
Scenario 2: Persistent data stores
This scenario assumes that the keystore is stored persistently as a file or in other persistent data store location where permissions can be used, such as the Windows registry. During this scenario the attacker logs into the target computer either locally or remotely, navigates to the folder (or registry location) where the keystore is stored, and accesses the file (or registry key) to steal its contents to execute further cryptographic attacks using the stolen keystore material. The attack depends on weak permissions set in the keystore file or registry location that allows an unauthorized attacker to access the sensitive information.
Step 2: Analyze Causes and Countermeasures
The next step in discovering insecure keystore bugs is to analyze what causes and how to countermeasure them.
Temporary data stores attacks
There are two different types of insecure keystore attacks. The first type targets temporary data stores such as system memory (RAM or operating system’s cache) or page/swap and crash dump files which are temporarily stored in the file system by the operating system. Each attack is caused by different reasons, depending on the memory type. For example, key material insecurely left in RAM occurs because the application doesn’t properly clean out data structures used to store key:
void Authenticate(char *strUser) {
char key[64];
if (ValidateKey(key, sizeof(key))) {
if (AuthenticateUser(strUser, key)) {
// Use key to authenticate user
}
}
}
To countermeasure this attack scenario developers must properly clear the key from memory:
void Authenticate(char *strUser) { .
char key[64];
if (ValidateKey(key, sizeof(key))) {
if (AuthenticateUser(strUser, key)) {
// Use key to authenticate user
}
}
memset(key, 0, sizeof(key));
}
However, be careful when using memset since compiler optimizations might render the memset call useless. It is recommended that you disable optimizations when using memset to clean out keystore material. To secure keystore material and avoid secure compiler optimizations, CERT recommends implementing secure memset function named memset_s of the following form [i]:
void *memset_s(void \*v, int c, size_t n) {
volatile char *p = v;
while(n--)
*p++ = c;
return v;
}
Developers must then replace calls to memset with the secure version for any keystore material:
void Authenticate(char *strUser) { .
char key[64];
if (ValidateKey(key, sizeof(key))) {
if (AuthenticateUser(strUser, key)) {
// Use key to authenticate user
}
}
memset_s(key, 0, sizeof(key));
}
Even if cleaning out cryptographic material from memory, this sensitive data might still make it to swap and crash dump files. Developers should look at the different options like protecting keys and certificates from making it to swap files by using mlock or VirtualLock, as well as avoiding an application from producing application crash dumps using functions such as setrlimit. However, keystore material might still make it to the file system in system wide memory dumps. To prevent against this scenario administrators must look at system configuration options to limit the creation of system crash dumps. For details on how to countermeasure temporary memory sniffing bugs refer to Team Mentor’s article How to - Test for Memory Sniffing Bugs.
Persistent data stores attacks
The second scenario relates to attacks that are achieved by accessing keystore files or registry keys in the target computer and it is caused due to insecure permissions set on those files or registry keys. For example, the Java code below deals with a keystore that exists in the file system:
KeyStore keyStore = KeyStore.getInstance("JKS");
String fileName = System.getProperty("java.home") +
"/lib/security/myKeyStore.jks";
FileInputStream stream = new FileInputStream(new File(fileName));
keyStore.load( stream, "storeit".toCharArray());
If the folder named java.home/lib/security allows read permissions on all users, the application is vulnerable to insecure keystore attacks by a low privileged attacker able to log in to the target computer.
Step 3: Execute Test Cases
Now that you’ve reviewed the theoretical aspects of insecure keystore bugs it is necessary to execute the necessary test cases to check if your application is vulnerable.
Test for insecure keystore in RAM (temporary store)
Follow these steps to create a record of physical memory in a text file in Windows platforms:
- Log into a Windows host running application under test.
- Download and install ManTech’s Memory DD (https://sourceforge.net/projects/mdd/).
- Open command prompt as a user possessing administrative privileges.
- Run mdd as follows: mdd_1.3.exe -o c:\ram.txt -v
- Once mdd finish executing scan through the output file ram.txt for sensitive information.
Expected results: ram.txt must not show any cryptographic keys.
Test for insecure keystore in virtual memory (temporary store)
The best way to search your application’s virtual memory for secrets is to use a debugger to create a dump of the virtual memory space at any given time:
- Log into a Windows host running application under test.
- Download and run user-mode Windows debugging windbg.
- Attach windbg to application under test.
- Break into the debugger and specify option to create a full dump for the application’s virtual memory: .dump /f c:\dump.txt
Expected results: The output file dump.txt must not show any cryptographic keys.
Test for insecure keystore in swap files (temporary store)
In Windows, swap files are stored in the pagefile:
- Log into a Windows host running application under test.
- Find location of the pagefile. It is named pagefile.sys and it’s usually on the root drive of the Windows partition (i.e. c:\pagefile.sys).
- Open read-only copy of file.
- Search for sensitive key information in the pagefile.
Expected results: The pagefile must not show any cryptographic keys.
Test for insecure keystore in dump files (temporary store)
Follow these steps test for insecure keystore in dump files:
- Log into a Windows host running application under test.
- Find out the location of dump files (in Windows this is indicated in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl).
- Open dump file and search for sensitive information.
- Repeat for each one of the dump files.
Expected results: The dump files must not contain any cryptographic keys.
Test for insecure keystore in file system (persistent store)
Follow these steps to test for insecure keystore in the file system:
- Log into the target computer (i.e. computer storing the keystore) using a low privileged or guest account.
- Browse to the folder containing the keystore files.
- Open a keystore file and read its contents.
Expected results: The application is vulnerable if it allows reading the keystore files using a low privileged account.
Test for insecure keystore in the Windows registry (persistent store)
Follow these steps to test for insecure keystore in the file system:
- Log into a target computer (computer storing the keystore) using a low privileged or guest account.
- Open the Windows Run dialog box (Start Menu->Run).
- Type regedit and click OK.
- In Registry Editor, browse to the registry location holding the keystore and read its contents.
Expected results: the application is vulnerable if it allows reading the keystore registry keys using a low privileged account.
Conclusions
Applications that implement cryptography need to store cryptographic keys and certificates in a temporary or persistent memory location known as the keystore. If an application doesn’t properly secure this location it enables attackers to retrieve the keystore contents and use the information for further cryptographic attacks. To protect against this vulnerability, developers must properly secure keystore material both in temporary memory and in the file system. To test for this bug it is suggested to execute memory dumping test cases as well as insecure storage test cases while searching for cryptographic keys and certificates in memory dumps, files, and registry keys.
[i] MSCxx-A. Be aware of insecure compiler optimization when dealing with sensitive data. Added by Joe Damato, edited by Shaun Hedrick. CERT. https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=2982545&navigatingVersions=true