About six months ago, I discovered something on my smartphone that horrified me: I went to undelete a file in DiskDigger, and I stumbled upon a plethora of unexpected jpegs: screenshots of my activity. Screenshots that I didn't take. Screenshots of my conversations. Screenshots of my GPS position. And screenshots of my bitcoin wallet.

I was perplexed. I was astonished. And, to be honest, I was scared. How did this happen? Was it a vulnerability shipped with LineageOS? Could it be some malicious binary embedded into AOSP? Or is it some exploit in one of those damned closed-source apps that I was forced to install through social pressure (*cough* whatsapp)?

This week I was honored to be accepted into a 1-week mini batch at the Recurse Center (formerly "Hacker School") in Brooklyn, NY. And, finally, I decided to roll-up my sleeves and dig into auditing android security with the ultimate goal of finding out what was responsible for creating (and then deleting) all these screenshots. Well, with no thanks to Google, I did find the source. And the codebase is integrated into AOSP. But (spoiler), it's not something to sweat about. Though it is a fun journey 🙂

UPDATE: to view the slides from my presentation at the RC, click here.

Context

I encountered this on a Nexus 5X (bullhead) running LineageOS 15.1 = 8.1.0 Oreo = API 26. This phone is rooted, and does not have gapps installed.

To be clear, I'm not an Android Developer--though I did write a hello world app once 🙂

Discovery

As most tech-savvy/sec-conscious readers are aware: when a file is deleted, the bits aren't lost--they're just marked as unused. Deleted files' data sticks around until it happens to be overwritten by a new file's data. Or until a Secure Wipe is performed.

This could be regarded as a bad thing for many people who are concerned about privacy--especially, for example, when they try to sell an old phone (factory reset is not enough to prevent the new owner from viewing all your photos!)

However, this could also be regarded as a good thing if you, like me, deleted a precious image by accident! And so, after I accidentally deleted an image (oh shoot!), I fired up DiskDigger--an app that can scan your disk for the remnants of deleted files and restore them. But as DiskDigger was scanning my /data/ partition for deleted image files, I stumbled upon more than I bargained for..

Here's some of the images that I restored from DiskDigger.





There are some interesting things to note about the above images. For example,

Many sensitive apps have not been spared, such as the encrypted messaging app Wire Some of the images were seemingly useless--such as a screenshot of my home screen (it's not limited to apps) The homescreen screenshot was also taken when a menu item was half-transparent, which is interesting regarding the timing of the screen capture event Many, many of the files were just corrupt. This could be an artifact of the fact that they were marked as unused by the filesystem and have subsequently been partially overwritten

Auditing Attempt #1, via SELinux

I had exactly 1 week at RC to figure out what the source of these screenshots was. When I finally sat down on Monday to begin my investigation, I figured I would write some auditd rule to capture all file writes and file deletions, throw selinux into permissive mode, and spend a lot of time searching through logs to pinpoint the process id responsible for creating these screenshot images.

I have a strong background in RHEL System Administration. In 1998, the NSA worked with RHEL to design SELinux (Security Enhanced Linux). 15 years later, Google ported selinux into Android version 4.3, Jelly Bean.

If I were trying to do this on a RHEL server, the tools-of-choice would be auditctl and ausearch . So, I thought: "Android is linux. I just have to install the auditd toolset, and I'll be able to use auditctl on my android shell." Oh boy, how ignorant I was..

The fact is: Google goes through great lengths to prevent users and developers from manipulating the selinux rules. This intention is explicitly outlined in Android's Compatibility Definition Document (CDD). Version 9's Section 9.7 Security Features reads:

Device implementations MUST ensure compliance with security features in both the kernel and platform as described below. The Android Sandbox includes features that use the Security-Enhanced Linux (SELinux) mandatory access control (MAC) system, seccomp sandboxing, and other security features in the Linux kernel. Device implementations:

...

[C-0-3] MUST NOT make SELinux or any other security features implemented below the Android framework configurable to the user or app developer.

So, in fact, there is no auditctl command in Android world. And, it seems, there never will be an official tool for users or developers to manipulate the selinux rule set for security auditing purposes.

But Android is a community full of hackers, and I'm not the first person wanting to modify the selinux ruleset. In fact, there's a tool called ` sepolicy-inject ` (and a newer fork) that exists for this exact purpose.

While the Android selinux documentation spends a lot of time talking about how to craft selinux policies, it's unsatisfactorily abstract. They don't tell you which file the policies are eventually stored to on the andorid device. And (for reasons explained above), they certainly don't tell you how to modify that file with new rules.

Well friends, the selinux policies are compiled to a binary stored in the root of the android filesystem at " /sepolicy ". In absense of auditctl , sepolicy-inject exists to turn that binary back into a human-read/write-able policy file, re-compile it, and copy it back onto the android device--effectively allowing the user to update the selinux policies. There's a problem though: at the time of writing, sepolicy-inject hasn't been updated in over 2 years and, in order to use it, you have to download 40-100G worth of Android source code, provision a very specific OS build environment, and hope to not spend a week fighting with hair-pulling compilation errors.

Unlike some other folks, I was not so successful at this, and I completely abandoned the attempt to use selinux as a tool to figure out what process was taking screenshots of my phone.

Auditing Attempt #2, via File & Metadata Analysis

There's a couple interesting facts that readily jump out about the above screenshot files restored by DiskDigger. Namely:

The files are JPGs. But when I tell android to take a screenshot, it stores the image as a PNG. Unlike traditional screenshots, these images don't include [a] the top bar (where the battery/wifi/etc icons live), [b] the bottom bar (where the back/home/recent apps buttons live) or [c] the keyboard. In all cases, these are blacked-out.

I also checked for clues in the exif image metadata, and one thing jumped-out at me: the images had an attribute named "Profile Copyright" with the value "Google Inc. 2016"

michael@amy:/tmp$ exiftool 8064090112.jpg ... Profile Copyright: Google Inc. 2016

Another clue would be the directory where these images were stored before they were deleted. Unfortunately, DiskDigger doesn't provide this info, and it wasn't available in any metadata. So, I just did a search across the entire device for all jpg images. Excluding the files in the camera's DCIM directory, there weren't too many directories in question here. One that stood out was at the end of the file list in a directory named /data/system_ce/0/snapshots/ .

This information was critical.

bullhead:/ $ find / -name *.jpg 1>/sdcard/findJpgs.txt bullhead:/ # tail /sdcard/findJpgs.txt ... /data/system_ce/0/snapshots/3419_reduced.jpg /data/system_ce/0/snapshots/3419.jpg bullhead:/ #

I copied the contents of the /data/system_ce/0/snapshots/ directory to the /sdcard/ , copied that to my laptop via adb, and--lo & behold--it was a bunch of screenshots. The images matched all the observations above: format was JPG, top and bottom bars were blacked-out, and exif copyright by Google, 2016.

Root Cause

Googling for the /data/system_ce/0/snapshots/ directory yields a few results of people who experienced the same issue as me, and the root cause is clear: It's persisted images taken by android for the "recent apps" navigation.

In iOS, there's also an "app switcher" that caches images of the app for quickly switching between apps. Indeed, this is the "screenshot" images I discovered--they're the Recent Task List "snapshot" images stored to disk, not requiring the top/bottom bars, copyright google, deleted when no longer needed.

It's also worth noting that the directory /data/system_ce/ is the so-called "Credential Encrypted" directory, whoose encryption key includes the user-specific password. And these files are certainly inaccessible to most apps, except those to which I grant root access.

Take Away

Between hardware/supply chain issues, zero-days being irresponsibly held in cyberwarfare arsenals instead of responsible disclosure, and incessant attempts to backdoor critical security software, it's easy to develop paranoia. That said, it's always good to keep in mind that not everything is as it seems, and sometimes seemingly-sketchy behavior is actually well-designed and benign.

Also, while I understand that Google shouldn't make it exceedingly easy for users to modify their ' /sepolicy ' file, I do think that AOSP should include an off-by-default tool for powers users to be able to manipulate their selinux policy for the purposes of security auditing.

And, to the Android Developers out there: If your app has sensitive data in any way, consider setting FLAG_SECURE, which prevents android from taking snapshots of your app.