Initialization vectors: iOS 11 & 12 Notifications Triage Parser

Wednesday, August 21, 2019

iOS 11 & 12 Notifications Triage Parser

Update 9/21/19: Parser now so processes iOS 11 notifications. See usage example at github link below. 

Short Version

Introducing a Python 3 script that looks for the UserNotifications folder in iOS 12 full file system extractions and parses the iOS notifications to easily triage their content. The script detailed below is a technical application of the research done at by my friend Christopher Vance that he kindly shared with me before making it public. Check out his blog on the topic at:
Script download:
Script purpose:

  1. To parse the iOS notifications plists for every app that has the functionality enabled. 
  2. Make a report of the plist contents giving the user the ability of hiding non-human readable, repeatable, and well know data by pressing a button on the HTML report.
  3. Report on and export any incepted bplists (full bplists found within a plist) for further analysis.
Script reason:
  1. As stated in there can be a wealth of data in the iOS screen notifications to include snippets of user generated content like chat messages, images received, and distinct alerts that might not be accessible in other ways.
  1. Python 3.
  2. Full file system extraction of an iOS 12 device or the UserNotifications directory. If extracting the directory itself for processing be aware that the script depends on the UserNotification directory (where notifications on iOS are kept) to be at least one level down (or more) from the data directory provided to the script.

Long Version 

When Chris shared his latest research with me I was immediately impressed on how much relevant data is contained in iOS notifications. For further details his blog post above is required reading. In this post I will only go into how to use the script to triage these important plists that seem to be overlooked but shouldn't.

Script usage

After downloading the script and configurations files you should see 4 files in the image.

Scripts and configuration files.


    Used to deserialize NSKeyedArchiver bplists. Thanks to Alex Caithness came up with this module. It saves us a lot of headaches. Added his module to my repo for convenience. It can be downloaded directly from the source here:

    This is where the magic happens. It searches in a specified directory for the UserNotifications directory and when found parses the DelivereNotifications.plist for every app that has notification data.
  3. NotificationsParams.txt

    It contains strings that I consider to be common, unreadable, or repetitive. The items in the list (one per line) are used to determine if they are to be hidden, not eliminated, from the final report. Be aware that the final HTML report has a couple of buttons that allow you to hide or show those fields as needed. To add more string to hide just add a new line to the text file. One string per line.
  4. script.txt

    Contains the javascript necessary to enable the hide/show functionality in the HTML report. It gets added to each report at processing time.

The script only has one parameter, the data directory to be parsed. See the help below.

When the script runs it tells you what notification is parsing and if a bplist was found within the plist. If found it will tell you that it was exported.

See the highlighted section above that shows a bplist exported. When done it also advises how many plist were processed, how many exported bplist, and how long processing took.

After the script runs a report directory will be created in the same location where the script resides.

As seen the report directories are timestamps so the script can be run multiple times and each time it will generate a new report directory. Within the directory each app has its own unique directory named after the app's bundle id.

Each app will have a report and exported bplist if any exists. For the screen time notifications in this data set one sees the following:

Each HTML report has a header and the Hide/Show buttons on the top.

Let's zoom in a little on the buttons.

As Christopher explains some of the ASCII values might not be important, unreadable, too much, or are simply repetitive. Hide rows hides them as explained previously by referencing the content of the NotificationsParams.txt file.

It will go from tons of pages to something like the following:

This is the same report. It has hidden a lot of repetitive data. Important note. It is worthwhile to always look at the full report if the app is important to the case. The report is only for triage purposes and will always require validation after execution. This is even more true when talking about the contents of within a plist. In some cases is either data that is not relevant or unreadable. In many cases it can contain a full bplist in them. The report deserializes this data and lets you read it. It is hard to read due to a lack of proper formatting but at least it will let you know if further analysis is warranted. Here is how a bplist in an field would look on the report.

Yes, hard to read but still it can be read. If anything pertinent is found then go and take the exported bplist and use any viewer for further and proper analysis.

Here is an example of the exported bplist and how a third party viewer shows you the data with ease.

Future work

As stated in Christopher's blog post there are additional data sources in the iOS notifications directory. I plan on making parsers for these as well. Like everyone else on this floating rock in space, when can have to many things but the thing we will never have enough of is time. If only the days had more hours and our bodies less need for sleep.

As always I can be reached on twitter @AlexisBrignoni and email 4n6[at]abrignoni[dot]com.