Inside SafetyNet - part 2

SafetyNet: Google's tamper detection for Android - part 2

This post is part of a series:

It’s been six months since my last blog post on Android’s SafetyNet. I was then examining a mid-July 2015 version of the system. As expected, there have been updates since then; the last was released mid-December 2015. I’ll briefly describe the differences in this post; for a more complete overview of the checks inside the SafetyNet system and its usage please read through my previous posts.

SafetyNet changes

A few but important new modules have been added in recent versions and some older ones were restructured.

Dalvik Cache module

This module attempts to find modified dalvik cache files. As is known, dex code inside an APK gets optimised during installation and is kept in a separate folder in “odex” files [on old Android versions that still use Dalvik]. Malicious actors could modify these optimized files directly instead of modifying APKs, in order to evade detection. The module monitors /data/dalvik-cache/arm or /data/dalvik-cache and maintains the results, comparing the hashes of odexed files with their stored versions.


This module retrieves a few system properties from android.os.SystemProperties and sends them back:

  • ro.boot.verifiedbootstate
  • ro.boot.veritymode
  • ro.oem_unlock_supported
  • ro.boot.flash.locked

This module has been previously discussed. A new submodule has been now been added, named SystemIntegrityChecker (SIC). This attempts to remotely verify the state of the /system partition; an interesting concept from many aspects.

SIC retrieves the SHA256 hash oof the /system entry from SafetyNet’s data store. It then performs a HTTPS request to a SIC server containing the hash and some meta-information about the directory. The response will contain a hashMatches integer flag. SafetyNet will use this flag and report through the appropriate SafetyNet APIs.

As far as I can tell the SIC system is not yet in use. I am not sure why a request to a separate SIC server needs to happen; the only reasonable explanation seems to be that entities other than Google might need to maintain their own SIC servers, e.g. device manufacturers. Still, the whole process could possibly happen through backend APIs instead. In any case, someone is going to have to maintain a list of hashes of the system partitions of various devices/configurations or the “last seen hash” for each user, so that changes are detected. We’ll know soon enough I guess.

How is the /system hash created?

SafetyNet runs an process that recursively walks “/system” and calculates a HashTree over its contents. For every file it encounters it captures meta-information (timestamps, permissions, selinux context etc) and its SHA256 hash into a local data store. For every directory, it generates a hash that considers the store entry of every file inside the directory. If there are hash mismatches between previous and current recursive walks over /system, the offending files are entered in separate lists to be audited.

The LOG SYSTEM PARTITION FILES module continues to include the results of the SystemPartitionFileFinder sub-module. As a reminder, this module retrieves the status of various files in /system. The list of “files of interest” is configured over the air. Currently, the following files are checked, along with 5 random files:


SafetyNet modules

Here is an up-to-date list of all SafetyNet logging modules. My previous blog post describes most of these.

LOG_APPS_TAG = "apps";
LOG_CAPTIVE_PORTAL_TEST_TAG = "captive_portal_test";
LOG_DALVIK_CACHE_TAG = "dalvik_cache_monitor";
LOG_DEVICE_ADMIN_TAG = "device_admin_deactivator";
LOG_DEVICE_STATE_TAG = "device_state";
LOG_EVENT_LOG_TAG = "event_log";
LOG_FILES_TAG = "su_files";
LOG_GOOGLE_PAGE_INFO_TAG = "google_page_info";
LOG_GOOGLE_PAGE_TAG = "google_page";
LOG_HANDSHAKE_TAG = "ssl_handshake";
LOG_LOCALE_TAG = "locale";
LOG_LOGCAT_TAG = "logcat";
LOG_MX_RECORDS_TAG = "mx_record";
LOG_PACKAGES_TAG = "default_packages";
LOG_PROXY_TAG = "proxy";
LOG_REDIRECT_TAG = "ssl_redirect";
LOG_SD_CARD_TAG = "sd_card_test";
LOG_SELINUX_TAG = "selinux_status";
LOG_SETTINGS_TAG = "settings";
LOG_SETUID_TAG = "setuid_files";
LOG_SSLV3_TAG = "sslv3_fallback";
LOG_SUSPICIOUS_PAGE_TAG = "suspicious_google_page";
LOG_SYSTEM_CA_CERT_STORE_TAG = "system_ca_cert_store";
LOG_SYSTEM_PARTITION_FILES_TAG = "system_partition_files";


SafetyNet is not just about the modules described here. During the attestation process some other checks happen via different systems; for example there is code that acts as old-fashioned root-detection, trying to figure out if the following files/directories exist in the filesystem (or if traces of them appear in device logs).

I do hope that the output of the rest of the SafetyNet modules is also taken into account during the calculation the ctsCompatibility response.


Over-the-air configuration

As mentioned above, SafetyNet is configured by Google at runtime; even though the code itself is also updated once every three months on average.

The following are some of the more interesting configuration options:

Signal Tags Whitelist - Idle Mode

This configures which modules are used by the SafetyNet “idle mode” logger.

  n: "snet_idle_tags_whitelist"
  v: "system_partition_files,
Signal Tags Whitelist - Normal Mode

This configures which modules are used by the SafetyNet “normal mode” logger.

  n: "snet_tags_whitelist"
  v: "default_packages,
Event Log Tags

This is used by the Event Logger module. The SafetyNet service is configured to retrieve and log the following event tags:

  n: "snet_report_event_logs"
  v: "50125:2,

The tags correspond to /system/etc/event-log-tags:

  • 50125:2
    • SMS denied by user
    • exp_det_sms_denied_by_user (app_signature|3)
  • 50128:2
    • SMS denied by user
    • exp_det_sms_sent_by_user (app_signature|3)
  • conscrypt:3
    • unexpected (early) ChangeCipherSpec message
  • 78001:2
    • FrameworkListener dispatchCommand overflow
    • exp_det_dispatchCommand_overflow
  • 65537:2
    • FrameworkListener dispatchCommand overflow
    • exp_det_netlink_failure (uid|1)
  • 90201:2
    • log whether user accepted and activated device admin
    • exp_det_device_admin_activated_by_user (app_signature|3)
  • 90202:2
    • log whether user declined activation of device admin
    • exp_det_device_admin_declined_by_user (app_signature|3)
  • 70151:2
    • exp_det_attempt_to_call_object_getclass (app_signature|3)
SIC Server URL
  n: "snet_sic_server_url"
  v: ""

This is currently empty, but will eventually be the server URL for the “System Integrity Checker” service described above.


These posts are aimed primarily at providing some clarity over the SafetyNet system to developers who wish to adopt attestation APIs in their applications. It must be noted that attestation is just a small aspect of the SafetyNet system; the main use is to retrieve data so that Google can monitor the security of the Android ecosystem and track on-going incidents.

As I’ve hinted in my previous post, while performing this investigation I stumbled upon DroidGuard, a set of components that communicates with remote Google Play APIs and is used for fraud detection, anti-abuse and operations like DRM.

SafetyNet interacts, along with many other components, with DroidGuard. Although these two systems may co-operate for some checks, DroidGuard is an independent system that serves different purposes, more inline with Google’s anti-malware efforts. I will not be revealing details about this system; as I think that such details would only benefit malware authors, not application developers that want to keep their Android apps protected. Similarly, revealing details on ‘how to bypass SafetyNet’ is not the goal here. Such details are shared directly with Google and enterprise developers interested in assessing the system before using it.

Improving SafetyNet

Here’s a bucket list of things I’d like to see in SafetyNet and some thoughts.

  • SafetyNet is not a root detection system although it goes a long way towards that goal. It suffers from some early symptoms of more traditional on-device checking systems: It’s designed for large scale data gathering and does not adequately protect itself against targeted attacks. It will tell Google that X% of devices are tampered, but, for now, it will stop short of trying to actively resist tampering by malware that specifically wants to present a false image to the checkers. Of course this is an ultimately futile effort, but the bar can be raised.
  • I’d like to see at least some degree of code protection for the checkers.
  • It’d be great if checks were performed using a range of high-level and low-level APIs.
  • I’d also be good if more SafetyNet checkers influence the compatibility decision; more than the straightforward su binary tests.
  • How much the compatibility decision is influenced by historical data about a device is an open question. Moving away from point-in-time checks could be a worthwhile goal.
  • Some clarity around the SIC server system would be nice to have.
  • Making use of trustzone
  • Multi-platform support for the Attestation APIs would be interesting to see (iOS attestation…)

mobile security, static & dynamic analysis, automation, payments

London, UK