Photo Vault app still pwnable in 2021? An adventure in iOS RE

Update 2021/08/22: Thanks to a tip from a reader, it was brought to my attention that PPV iOS made some pretty big changes in a recent update (early August 2021 – version 11.9). In reading the release notes, as well as doing some of my own tests, I’ve discovered some stuff and wanted to touch on their impact.


  • Happy to say that across the board, almost all the changes improve security for PPV users. The only source of apprehension for me is the cloud based backup which I will discuss in more detail later in the post.
  • Bruteforcing the PIN is still possible although the time it takes to do so has increased, due to a larger keyspace.
  • With some effort put into reversing, it is still possible to decrypt the media, especially if you know the PIN or have access to the decrypted keychain.
  • For users who were using PPV before this version, my suspicion is that the app will continue to use the old way, in order to avoid re-encrypting all media. So don’t assume if you have data from 11.9 that it will be subject to the new approach.

Database location has changed, and is now encrypted.

  • The database can be found a directory up from its previous location, with the extension ‘.ecd’ (short for Encrypted Core Data). Despite this, it is fully compatible with SQLCipher viewing tools like DB Browser for SQLCipher.
  • This is a solid security upgrade to PPV as previously one could glean a lot of information about what the encrypted media might be by simply looking at the database (and observing things like album titles).

Media items are now protected using a unique key per item.

  • Formerly, once you had derived the ‘media key’, you were good to go for decrypting all data if you knew the structure of the cryptographic media container.
  • From a security perspective, this doesn’t change things a whole lot because of the fact that you still only need 1 key (the SQLCipher key) to get to the database. It does add extra steps though, and falls more in line with what some of the larger apps are doing (e.g. Snapchat).

Numeric passcodes can now be *up to* 8 digits long.

  • This is a long overdue increase of security for the app. 4 digit numeric keyspaces, even ones with a reasonably strong KDF backing them, are pretty much always going to be susceptible to bruteforce.
  • The ‘up to’ is significant as well. Instead of having a known length (4 digit) PIN (keyspace of 10^4), we now have any length from 4-8, e.g. (10^4+10^5+10^6+10^7+10^8)
  • Still, having any cap on the length at all seems unnecessary. Also having an option for custom alphanumeric would be nice to see.

There is now a ‘cloud backup’ option available (for a fee).

  • Full disclosure: I have not investigated this feature as of yet. These opinions are based on generalized concepts that would apply to any vault app with such a feature.
  • I will say that non-CloudKit (iCloud) based storage for an app like this, for me, is on its own reason enough to exercise extreme caution. This is not a reflection on PPV itself (I have no knowledge of the developer), but I have seen enough abhorrent things with other vault apps with this type of offering to default to alarm bells.
  • The patch notes clearly state that the separate ‘cloud password’ is never backed up to their server, but even without the password I would have a lot of questions about how strong of a KDF is being used on the password, what are the minimum password strength requirements, etc. Imagine a scenario where their backup server gets breached, and the only thing standing between the attacker and your most sensitive media is 10,000 rounds of PBKDF2-SHA1.
  • Beyond outsider threats, what guarantees do we have that people affiliated with PPV aren’t going to attempt to bruteforce our data? How big is this company, how many people work there, how many of those people have access to this server and what access controls and auditing is in place to monitor that access?
  • The fact that a paid subscription is required means that PPV will indirectly have access to a lot more PII of their users than they otherwise would, which could be used to associate media to a specific identity.
  • Does PPV have the resources to respond to legal orders, such as warrants or preservation orders? It is only a matter of time before CSAM gets uploaded to their server.

Update 2020/01/29: I have since done a bit more work with this app and have found a way to bruteforce the PIN without keychain access. I also created a Python based decryptor script (instead of the C# one attached to this post). Rather than make them publicly available, please contact me and I will be happy to share the scripts with you. You can do so on the DFIR Discord or Twitter @forensicmike1.

Original post: It’s been a while since I posted anything, and I suppose that’s a natural part of having a blog. I decided not to force myself to procure content and instead wait until I had something I really wanted to write about. And so here we are! In this article I’m going to talk about a process brand new to me until a few days ago. This has been an absolute blast to learn about, although I will admit it was frustrating at times.

This article focuses more on the outcome of my research, without dwelling too much on exactly how I got there. I am however planning a follow-up post with a whole pile of lessons learned as I think there are a lot of gotchas and overall frustrations that could very possibly be skipped.

Why target this app specifically?

com.enchantedcloud.photovault or “Private Photo Vault” (hereafter PPV) has been the subject of security research before. In November 2015, a detailed breakdown was published by Michael Allen at IOActive and he found that the app didn’t actually encrypt anything! It’s security amounted to blocking users from seeing any media inside until the passcode had been entered and this was extremely easy to defeat. I figured revisiting this same app in 2019 could be fun/interesting just to see how far it has or hasn’t come since then.

Key Takeaways

Whether you consider this app secure or not depends on what kind of access you’ve got to various extraction methods. For examiners with filesystem type extractions (GrayKey / Cellebrite CAS / jailbroken devices), the security of PPV is trivial to defeat and I will demonstrate how below. For examiners obtaining logical type extractions (iTunes backup, UFED 4PC, Magnet ACQUIRE, etc.) decryption will be more challenging and further reversing work will be required. I do believe it is possible though.

PPV uses RNCryptor, an encryption library with implementations available in ObjectiveC, C#, JS etc. RNCryptor is open source and we can absolutely use that to our advantage. One thing RNCryptor doesn’t manage is key storage, and the developer of PPV has apparently decided to rely on the security of the iOS Keychain to store, well, everything we need to perform decryption.

The master key is stored in the keychain under “ppv_DateHash”. The plaintext PIN, which is a maximum 4 digits, is also stored in the keychain as “ppv_uuidHash1”.

Each encrypted media file (found with its original in the app’s sandbox at /Library/PPV_Pics/) is essentially a container. The first two bytes can be safely ignored, the next 16 bytes are the IV (Initialization Vector), and the remaining bytes are the cipher text with the exception of the last 32 bytes which are related to HMAC and can safely be ignored.

Once generated, the master encryption key never changes even if you change your PIN. This might seem like a poor design choice, but it’s actually how your iPhone works too and it can be quite secure as long as the master key is well protected. Secure Enclave makes sure that this key never sees the light of day but this is not true for keychain data.

Basic Outline of the Process / Tools Used

  • Locate and jailbreak test iOS device (I used Electra root for my test device, an iPhone 6S running iOS 11.2.1).
  • Installed PPV (target app) by sideloading with Cydia Impactor (app store works too).
  • Setup access over USB with ITNL (iTunnel) and obtained root access to device via SSH.
SSH tunnel over USB thanks to itnl.
  • Installed and verified operation of frida-server on the device – I did this using Sileo but should be doable via Cydia as well.
  • Used frida-ios-dump by AloneMonkey to obtain decrypted binary of the target app (recommend Python 3.7)
  • Conducted static analysis of decrypted binary using Hopper . I had great success with searching for a value from the plist I believed to be associated to crypto. This app is not free but the trial is fully functional for 15 minutes – make sure you hurry! 🙂
Static analysis using Hopper – this class looks like it might be of use!
  • With my newly discovered knowledge I fired up Frida with this little gem: ObjC Method Observer, an awesome codeshare script by mrmacete (@bezjaje) to snoop on iOS method invocations of a specific class on a live device. (I targetted LSLCrypt and RNCryptor classes on PPV)
Note the test passcode of 1234 at the end of the giant SHA256 string.
  • Switched back and forth between Hopper and Frida console until I established a good idea of what was going on. The biggest breakthrough here was that the encryption key doesn’t change when you change the passcode, and that it is stored in keychain.plist
PIN change does not affect our encryption key, which conveniently gets stored in this device’s keychain.plist
  • Studied the RNCryptor-objc github repo to develop an understanding of how this AES wrapper works.
  • Develop PoC in C# using the amazing LINQpad to decrypt media in PPV_Photos given the keychain.plist

Decryption PoC

This script is C# and was written in/for Linqpad, but could be adapted to a Visual Studio project very easily. It uses only native libraries. You will need to plugin your AES Key as base64 in the “USER CONFIGURATION REQUIRED” section 😀 ! I call this a PoC because it does zero error checking and may or may not work for you without tweaking.

I might throw together a GUI app to do this more easily if people would use it. DM me on Twitter or Discord and let me know if that sounds interesting/useful.

void Main()
	// USER CONFIGURATION REQUIRED --------------------------------->
		// The input directory should point to the PPV sandbox where all the encrypted media resides
		var pathToEncryptedFiles = @"c:\ppvtest\335CE0B0-..-B521433DD5D2\Library\PPV_Pics";
		// Where to spit out the decrypted media
		var decryptFilesTo = @"c:\ppvtest\out\";
		// from keychain.plist -- genp with key "ppv_dateHash"
		var aesKeyb64 = "mUAf0A6QF+DOoo...7tbZuqw2ImuRAkql0mY0zM=";


	// Convert to byte[] from base64 string
	var aesKey = Convert.FromBase64String(aesKeyb64);
	// Iterate encrypted files in the PPV_Pics folder.
	foreach (var item in Directory.GetFiles(pathToEncryptedFiles))
		var inputData = File.ReadAllBytes(item);
		// The IV is located at offset 0x2 and is 16 bytes long.
		var iv = inputData.Skip(2).Take(16).ToArray();
		// Our header is 18 bytes (0x0 for version, 0x1 for options, and 0x2 for 16 bytes IV)
		var headerLength = 18;
		// The cipher text is the rest, minus 32 which is used for HMAC stuff.
		var cipherText = inputData.Skip(headerLength).Take(inputData.Length - headerLength - 32).ToArray();
		File.WriteAllBytes(decryptFilesTo + new FileInfo(item).Name, decryptAesCbcPkcs7(cipherText, aesKey, iv));

// Borrowed from Rob Napier's RNCryptor-cs
private byte[] decryptAesCbcPkcs7(byte[] encrypted, byte[] key, byte[] iv)
	var aes = Aes.Create();
	aes.Mode = CipherMode.CBC;
	aes.Padding = PaddingMode.PKCS7;
	var decryptor = aes.CreateDecryptor(key, iv);

	byte[] plainBytes;
	using (MemoryStream msDecrypt = new MemoryStream())
		using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
			csDecrypt.Write(encrypted, 0, encrypted.Length);
			plainBytes = msDecrypt.ToArray();

	return plainBytes;


I’d like to thank the following people for their assistance on this research project:

  • Braden Thomas (@drspringfield) at Grayshift for his always spot-on advice and extensive depth of knowledge on all things iOS.
  • Ivan Rodriguez (@ivRodriguezCA) for his excellent blog and great advice.
  • @karate on DFIR Discord (Magnus RC3 Sweden) (@may_pol17) for his excellent guidance and urging to get Frida working.
  • Or Begam (@shloophen) from Cellebrite for reviewing my decryption PoC and spotting that final bug, connecting me with Ivan Rodriguez and generally being awesome.

14 thoughts on “Photo Vault app still pwnable in 2021? An adventure in iOS RE

  1. Really appreciate and respect your research. I’m trying to follow how you actually snagged the encryption key. Are you getting this from memory with Friday?

    1. Hi Paul, you are correct! Using Frida we can intercept calls happening between the Photo Vault app and the keychain, and display the contents in the terminal. Once you have frida up and running, it’s really just a matter of finding the right function to hook (and occasionally fiddling with the output if it doesn’t display nicely).

  2. Thanks for the post! I was working on trying this myself when I stumbled upon your article after googling LSLCrypt, guess I was on the right track 🙂

    Where did you find the keylist.plist? I’ve looked around but can’t seem to find it.

    1. Hi Jon,
      You’ll need to find a way to extract the keychain. This can be done with various commercial tools or, fairly easily at runtime with Frida.

      Key derivation follows a specific pattern (hint: it involves the string concatenation of three specific things), so it should be possible to do without the keychain at all as I alluded to in the post.

      Best of luck!

      1. Thanks for the help, Mike! Learning Frida was a bit of a challenge but once I figured that out, the rest was trivial.

        Observing the LSLCrypt class and entering a wrong password will result in the correct one being dumped to the console… Not very private, huh? 🙂

        1. Very true! I often say taking those first steps with Frida are the most difficult ones. That’s not to say there aren’t challenges every time, but once you start to see what’s possible it becomes so fun to poke around.

  3. You have given me a glimmer of hope when PPV took it away. I am one of the unfortunate souls who purchased this application and now find myself stuck waiting on the company to provide support. It’s been a month btw and no response from them.

    I upgraded my device from an iPhone 8 to the 11 but downsized from the 256 GIG to 128 GIG model. Before shipping the device back to Apple for credit, I of course made a full encrypted backup on my Mac thinking that I could pull out the data I wanted at a later time. I purchased a 3rd party application to extract files / data from my backup which works great, the app supports the decryption process of Apple Backups and I have full access to the previous devices file system. My problem is the PPV files are still encrypted and the current version of the application does not allow you to put them back on a device in a manner that restores viewing. I know the PIN code which was set on the previous device and PPV app.

    I obviously don’t code or fully understand the methods you described in the write up but I figured it was worth a shot to ask if you had a suggestion. You can see for yourself in the App store comments section that I’m not alone in my frustration with the makers of PPV!

  4. Hi Mike,
    Thanks for your detailed code .
    I used to have that app and since it got discontinued and I upgraded to a new phone I have lost all the data as I am unable to get/acces PPV app.
    Is there any way to extract the data from Iphone backup instead?

      1. Hi Mike,

        Really interesting blog post!

        I have similiar problem as Matt. I still have my old iphone backup but switched to android so I cant restore the PPV app.

        Is there a way to extract the key from the keychain-backup.plist somehow?

  5. I Hey Mike,
    I really appreciate the write up on this. I’m in the same boat as Matt K. above. I have an iPhone backup that I’ve extracted the PPV app data from and need some assistance decrypting it. Any help would be greatly appreciated. I’ll reach out via Discord also. Thanks!

  6. I had private photo vault on my old iphonex and before I traded it in , I made sure to do an iCloud backup and an iTunes backup on my MacBook Air. I now have an iPhone pro max 12 and when I added everything from my old phone to this one, in private photo vault , everything didn’t transfer over; please help!

Leave a Reply

Your email address will not be published. Required fields are marked *


Analysis of the ABTraceTogether app (iOS)

I decided to have a look at the ABTraceTogether contract tracing app released by the Alberta Government today (May 1 2020) and blog about my findings. There’s potential for conspiracy theories and disinformation to run rampant for an app like this, so I wanted to have a look for myself and see how it actually […]