In an attempt to get back into blogging, I’m publishing some notes on a current research project I’m working on, rather than waiting until I have fully chased down every research lead like the other projects I’ve been working on-and-off for over the years.

While looking for a new research project, I recently watched an excellent talk by Matthias Frielingsdorf on the Current State Of IOS Malware Detection. While my career thus far has focussed on desktop and server security, I had heard from friends who work in the mobile antimalware space that iOS presents a unique challenge to defenders.

To summarise my friends’ opinions, Apple’s strong focus on security and privacy has created devices that are difficult to exploit (especially with advanced features such as Lockdown Mode), but that once an attacker has compromised a device it was difficult for the user or external security team to detect an attack, due to the difficulties in getting actionable ‘live’ data from the device at runtime. Matthias’ talk convinced me to learn about iOS security, and how I could apply my expertise in protecting other Operating Systems to this problem.

Table of Contents

Current Detection Efforts

The most common technique defenders have in uncovering intrusions on iOS devices is to create a forensic snapshot of the device using the Sysdiagnose feature. This has been used by Amnesty International, The Citizen Lab, and others to great success in the past. But they suffer from two major flaws:

1. Snapshots give time for attackers to hide

As snapshots have to be manually triggered, can take up to 30 minutes to collect, and only offer a view into the device at that specific point in time, they give the attacker time to ‘clean up’ their footprint on the device, removing forensic artefacts their initial exploitation and execution may have created.

2. You need to know when to make a snapshot

It is unlikely a user will know exactly when they are compromised. So unless the user employs a routine of proactively and constantly taking snapshots, they might never know the ‘right’ time to take one.

In Kaspersky’s reporting on the TriangleDB Malware, they discussed initially being tipped off to the infection via network monitoring. But network monitoring is far from a reliable detection mechanism, especially in the age of Global Content Delivery Networks, TLS encryption, and the increasing use of encrypted DNS via DNS-Over-TLS.

I wanted to investigate if there was a way to get realtime information from an iOS device, that would either reveal an attack, or at least let a user know that something suspicious has occurred, and that they should consider making a forensic snapshot to investigate.

Enter Lockdown

Remote machines can communicate to a special daemon running on iOS devices called lockdownd. This communication requires that the remote machine is first trusted by a human physically agreeing to ‘pair’ the iOS and remote machine together, as well as the devices being able to communicate over either physical USB, or local WiFi. For WiFi to work, the devices must be on the same subnet (more on that later).

To explain both how the pairing and communication works, here is 2 excellent writeups that will explain it far better than I can:

Once the remote machine is paired with the iOS device, a surprising amount of both snapshot and realtime data becomes available to query via the creation one of a number of ‘Lockdown Services’. These services became the focus of my research.

Screenshot of an iOS device, with a prompt asking if the human trusts the connected device

Screenshot of an iOS device, with a prompt asking if the human trusts the connected device

Working Remotely

The USB/Local WiFi limitation initially seemed like a big blow the practicality of using Lockdown Services for detections ‘in the real world’. Initially I had assumed this limitation was due to the fact the initial device handshake was setup via the Bonjour/Multicast DNS protocol. This protocol by design can’t traverse across network subnets, which means the only option for an iOS device outside of local Wi-Fi range is to connect is to carry around something like a portable Linux Wi-Fi router that has the ability create a low-level L2 VPN tunnel to the remote machine, then connect the iOS to the router via Wi-Fi. Kinda lame.

However, it turns out the initial handshake isn’t strictly necessary every connection. An excellent tool for communicating with lockdownd and Lockdown Services is pyMobileDevice3, a pure-python implementation of the older libmobiledevice library. This tools provides the ability to communicate with a any remote device that is accessible over TCP, provided it has first been connected ‘locally’. In my situation I first connected my iOS device to my remote machine via USB and ran:

# First, get device identifier from:
pymobiledevice3 usbmux list

# Ensure devices are paired:
pymobiledevice3 lockdown pair

# Turn connections via 'Wi-Fi' on
pymobiledevice3 lockdown wifi-connections on

# Save pair record for future access:
# This contains sensitive data so don't post to Twitter or something
pymobiledevice3 lockdown save-pair-record pair_record.plist

Once that was done, I was able to connect to my device while it was fully remote, as long as it was accessible over TCP, for example using a ‘normal’ VPN like Tailscale to link the 2 devices together. Using Python on my remote machine, I did this by:

import plistlib
from pymobiledevice3.lockdown import create_using_tcp

# Open and parse saved pair record
with open("pair_record.plist", "rb") as f:
  plist = plistlib.load(f)

lockdown = create_using_tcp(
  # IP Address or hostname of iOS device
  hostname="10.11.22.33",
  # Identifier from 'pymobiledevice3 usbmux list'
  identifier="11111111-2222222222222222",
  # plist from disk
  pair_record=plist,
)

# Can now use lockdown to connect to services, see next paragraphs
trace_service = OsTraceService(lockdown)

There is a massive caveat with this technique, however - It still requires the iOS device to be connected to some type of Wi-Fi network. It doesn’t have to be the same Wi-Fi network as the remote machine, but unless the device is connected to something over Wi-Fi, the lockdownd daemon will refuse any connection. So you could walk around tethered to something like a small Android Phone, and it’d work I guess?

That still isn’t ideal, and I feel there is more research to understand the exact limitation here. I decided to call this “good enough” for now, and started to look into what you can actually do with access the Lockdown services.

Unified Logging

Like most UNIXes, iOS has a centralised system-wide logging system. Unlike other UNIXs that primarily use syslog, modern iOS uses a system called Unified Logging, which enables applications and programs to produce structured log entries in that can be performantly written and read in real-time. Managed by the logd daemon on the device, there is no on-device UI to read these logs, mostly because they are more useful to developers and apple rather than the end-user.

Applications can’t access the logs of other applications or system components, but a paired remote device can read logs from the whole system, by using lockdownd to connect to the os_trace_relay Lockdown Service. This is helpfully implemented in pyMobileDevice3, where we can simply run this to start reading and parsing log messages in real-time:


import plistlib
from pymobiledevice3.lockdown import create_using_tcp
from pymobiledevice3.services.os_trace import OsTraceService

# Connect to os_trace_relay service and start reading entries:
# lockdown = create_using_tcp(...)
trace_service = OsTraceService(lockdown)
for entry in trace_service.syslog():
  # pyMobileDevice converts this data to a Python object.
  # To write to disk, the below code converts this into JSON:
  entry_json = {
        "pid": entry.pid,
        "timestamp": datetime.isoformat(entry.timestamp),
        "level": entry.level.name,
        "image_name": entry.image_name,
        "filename": entry.filename,
        "message": entry.message,
        "label": {
            "category": "",
            "subsystem": "",
        },
    }
    if entry.label is not None:
        entry_json["label"]["category"] = entry.label.category
        entry_json["label"]["subsystem"] = entry.label.subsystem
  
  # You can now print or filter the JSON
  print(json.dumps(entry_json))

When using this script, we can capture events that look like this:

{
  "pid": 65424,
  "timestamp": "2024-10-04T14:52:28.100478",
  "level": "INFO",
  "image_name": "/System/Library/PrivateFrameworks/TCC.framework/TCC",
  "filename": "/Applications/MobileSlideShow.app/MobileSlideShow",
  "message": "SEND: 0/7 synchronous to com.apple.tccd: request: msgID=65424.1, function=TCCAccessRequest, service=kTCCServicePhotos,",
  "label": { "category": "access", "subsystem": "com.apple.TCC" },
}

pid and filename are the Process ID and program responsible the event, in this case the default Photos app (called MobileSlideShow.app to iOS). image_name Will be where the log message was actually generated from, which can be the main process binary, or like in this case an external Library the process has loaded.

The label can help link logs messages from the same Application/Library together, however they are ‘arbitrarily’ named by the developers, so the same category name across different Libraries might not mean the same thing. And the message field can also be any sort of data, including milti-line stack traces, encoded JSON, hex- or base64-encoded data, or really anything else the developer found worth logging.

There are plenty of other Lockdown services that are worth investigating (some of which are mentioned below), but for the most part my research focussed on parsing events from this log, and understanding what events get created when I undertake various suspicious and malicious actions on my devices.

Configuration Profiles

Before detailing what data I was able to collect from my device’s logs, it’s useful to talk about Configuration Profiles. Designed to either be installed ‘manually’ on a device or via a Mobile Device Management (MDM) solution, these (usually signed) profiles are designed to ensure a device is setup consistently across an organisation. For example, they can ensure certain applications are installed or specific settings are turned on or off. When deployed via an MDM no user interaction is required, but they can also be installed manually by the user physically clicking through a number of security prompts:

Screenshot of an iOS device, with a prompt asking if the human wants to install the 'Facetime and Call Activity Logging' Configuration Profile

They have been used for malicious purposes, which requires the user to be tricked/phished/coerced into installing them. The reason Configuration Profiles are relevant to this research is that Apple publishes a number of Special Configuration Profiles, designed specifically for developers to help diagnose difficult problems with their applications. Most of the profiles supplied by Apple accomplish this by enabling extra verbose logging across the system, and oftentimes also disabling special checks that redact sensitive information from the logs. For example the CFNetwork Diagnostics profile states:

The CFNetwork Diagnostics profile generates files that allow Apple to troubleshoot issues with your device and help Apple to improve its products and services.

The generated files will contain all data transferred over the Internet to or from your device, including personal information such as the contents of the emails and iMessage texts you send and receive, the websites you visit, your Maps searches, and your requests to Siri. The generated files will contain account passwords that websites and your apps transfer over the Internet to access secure resources.

The profile will expire after 7 days.

In my testing, the settings these profiles alter can only be changed by Configuration Profiles specifically signed by Apple, and as the profiles are signed they cannot be altered, although they can be parsed to see exactly what settings they change. I’ve uploaded the parsed plist data to this repo for reference.

One other interesting factor is the note about the expiry. While the CFNetwork description states the profile will expire in 7 days, in reality the actual expiry appears to be much longer. For example, below is the XML plist data from the CFNetwork profile currently downloadable from Apple. As you can see it has a ‘Removal Date’ of July 30th 2025, 10 months after I initially downloaded it:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>ConsentText</key>
  <dict>
    <key>default</key>
    <string>The CFNetwork Diagnostics profile generates files that allow Apple to troubleshoot issues with your device and help Apple to improve its products and services. The generated files will contain all data transferred over the Internet to or from your device, including personal information such as the contents of the emails and iMessage texts you send and receive, the websites you visit, your Maps searches, and your requests to Siri. The generated files will contain account passwords that websites and your apps transfer over the Internet to access secure resources. The profile will expire after 7 days.

You will be able to turn on and off logging at any time while the Profile is installed, and will be able to review the log files on your computer prior to sending them to Apple.  To turn off logging, open Settings, tap General, tap "Profiles," tap "CFNetwork Diagnostics," and tap "Delete Profile."

By enabling this diagnostic tool and sending a copy of the generated files to Apple, you are consenting to Apple's use of the content of such files in accordance with its privacy policy (http://www.apple.com/legal/privacy).

Apple Confidential Profile. Do not distribute. Not to be used or disclosed without permission from Apple. Copyright © 2024, Apple Inc. All rights reserved.</string>
  </dict>
  <key>DurationUntilRemoval</key>
  <real>604800.0</real>
  <key>PayloadContent</key>
  <array>
    <dict>
      <key>PayloadContent</key>
      <array>
        <dict>
          <key>DefaultsData</key>
          <dict>
            <key>AppleCFNetworkDiagnosticLogging</key>
            <integer>3</integer>
          </dict>
          <key>DefaultsDomainName</key>
          <string>.GlobalPreferences</string>
        </dict>
      </array>
      <key>PayloadDescription</key>
      <string>Enables CFNetwork diagnostic logging</string>
      <key>PayloadDisplayName</key>
      <string>CFNetwork Extended Logging</string>
      <key>PayloadIdentifier</key>
      <string>com.apple.defaults.managed.cfnetworklogging</string>
      <key>PayloadOrganization</key>
      <string>Apple, Inc</string>
      <key>PayloadType</key>
      <string>com.apple.defaults.managed</string>
      <key>PayloadUUID</key>
      <string>3A614BF2-2651-4A2A-9FA1-7556444401E9</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
    </dict>
  </array>
  <key>PayloadDescription</key>
  <string>Enables CFNetwork logging. Reboot required.</string>
  <key>PayloadDisplayName</key>
  <string>CFNetwork Diagnostics</string>
  <key>PayloadIdentifier</key>
  <string>com.apple.cfnetworklogging</string>
  <key>PayloadOrganization</key>
  <string>Apple Inc.</string>
  <key>PayloadRebootSuggested</key>
  <true/>
  <key>PayloadType</key>
  <string>Configuration</string>
  <key>PayloadUUID</key>
  <string>E524E135-447D-439D-BBDD-F7E49FCE36E5</string>
  <key>PayloadVersion</key>
  <integer>1</integer>
  <key>RemovalDate</key>
  <date>2025-06-30T07:00:00Z</date>
</dict>
</plist>

This is similar for most of the profiles I investigated.

Another important note is Configuration Profiles cannot be installed while a device is in Lockdown Mode. While a smart decision by Apple as part of Lockdown Mode’s efforts to reduce possible attack surface, it means that to load a new Configuration profile, you need to:

  1. Go into settings to turn off Lockdown Mode
  2. Reboot the device
  3. Install the new Configuration Profile (which may require another reboot)
  4. Turn Lockdown Mode back on
  5. Reboot the device again

While fine for research purposes, this isn’t ideal to have to do ‘in the real world’, especially if it needs to be done semi-reguarly to ‘reload’ expired Profiles. So during my research I checked what data was generated with and without any profiles, to see if they were actually needed.

Collecting Data

My research was conducted using 2 iOS devices that I happen to have on hand:

  • A Jailbroken iPhoneX running iOS 16
  • A non-Jailbroken iPhone 14 running iOS 18

On both devices I tested various activities such as starting Applications, browsing to websites, receiving messages, etc., with and without Configuration Profiles. On the Jailbroken device I also used SSH to launch a few ‘native’ executables outside any Application sandbox.

I started looking at the ‘normal’ types of events used by security platforms on other Operating Systems, i.e. events related to Process, Networking, and File activity.

Next, I looked at more iOS-specific events, such as events related to common exploitation vectors, inter-process communications, and various post-exploitation activities undertaken by known iOS malware such as NSO’s Pegasus and TriangleDB.

Process Creation

When launching an Application, I observed the following event being logged, that contained the Application name and some other details (Safari in this case):

'application<com.apple.mobilesafari>' Constructed job description (context <RBSLaunchContext: 0xc5ae2e080>):
<dictionary: 0xc5aa4a930> { count = 18, transaction: 0, voucher = 0x0, contents =
  "ProcessType" => <string: 0xc5aa6cdd0> { length = 9, contents = "SystemApp" }
  "EnableTransactions" => <bool: 0x200b33fa0>: false
  "_ManagedBy" => <string: 0xc5aa6b4d0> { length = 22, contents = "com.apple.runningboard" }
  "CFBundleIdentifier" => <string: 0xc5aa6e1d0> { length = 22, contents = "com.apple.mobilesafari" }
  "_ResourceCoalition" => <string: 0xc5aa3bc90> { length = 35, contents = "application<com.apple.mobilesafari>" }
  "ThrottleInterval" => <int64: 0x918c4438e8499dd7>: 2147483647
  "PersonaEnterprise" => <int64: 0x918c443b17b67d6f>: 1000
  "MachServices" => <dictionary: 0xc5aa6c310> { count = 0, transaction: 0, voucher = 0x0, contents =
  }
  "EnablePressuredExit" => <bool: 0x200b33fa0>: false
  "InitialTaskRole" => <int64: 0x918c443b17b66227>: 1
  "UserName" => <string: 0xc5aa6d5a0> { length = 6, contents = "mobile" }
  "EnvironmentV<…>

Unfortunately, I did not observe these or similar events when launching ‘native’ executables. As an alternative to relying on just the logs, the os_trace_relay Lockdown Service does allow you to query the current list of running processes. This is done with pyMobileDevice3 by doing:

# lockdown = create_using_tcp(...)
trace_service = OsTraceService(lockdown)
pids = trace_service.get_pid_list().get("Payload")
print(json.dumps(pids, indent=2))

The results contains just the PID and Executable:

{
  "172": {
    "ProcessName": "itunescloudd"
  },
  "142": {
    "ProcessName": "analyticsd"
  },
  "34": {
    "ProcessName": "mediaserverd"
  },
  "112": {
    "ProcessName": "nehelper"
  },
  ...
}

While the outputted data is missing information typically seen in a process listing on other Operating Systems, such as commandline arguments or username, this technique could enable you to periodically query the list to try to discover new or unusual processes. This isn’t a great solution, as it will miss anything that runs in between queries, and it may be hard to tell legitimate from illegitimate processes.

It could also be possible to simply keep track of all processes that generate any type of log entry. While malware authors are unlikely to directly write log entries, the dynamic libraries the malware imports, or the processes they inject their malware into, just might. This could lead to the process “leaking” events that give away the malware’s presence or intentions. This would require keeping a list of all processes previously seen in the logs, which might get quite long the longer a phone is on. Neither of these are great solutions.

Networking

Logging Network activity is where things get a bit more interesting. By default, without any extra Configuration Profile I oberved a number of DNS-related events. However the specific details about what host was looked up was hashed for privacy:

// Request
{
  "pid": 68558,
  "timestamp": "2024-10-07T16:24:12.388888",
  "level": "NOTICE",
  "image_name": "/usr/sbin/mDNSResponder",
  "filename": "/usr/sbin/mDNSResponder",
  "message": "[R337] getaddrinfo start -- flags: 0xC000D000, ifindex: 0, protocols: 0, hostname: <mask.hash: 'OzCZtqbB+AClqJaZLncCgg=='>, options: 0xC {in-app-browser, use-failover}, client pid: 288 (com.apple.WebKi), delegator pid: 282 (MobileSafari)",
  "label": { "category": "dnssd_server", "subsystem": "com.apple.mdns" }
}

// Response
{
  "pid": 68558,
  "timestamp": "2024-10-07T16:23:47.588887",
  "level": "NOTICE",
  "image_name": "/usr/sbin/mDNSResponder",
  "filename": "/usr/sbin/mDNSResponder",
  "message": "[R335->Q40326] getaddrinfo result -- event: add, ifindex: 0, name: <private>, type: A, rdata: <private>, expired: no",
  "label": { "category": "dnssd_server", "subsystem": "com.apple.mdns" },
}

These events show that MobileSafari with PID 282 looked up a domain using the device’s central DNS service, but not what it looked up. However, on the iOS16 an event did contain the domain requested:

{
  "pid": 175,
  "timestamp": "2024-10-07T17:09:17.579674",
  "level": "DEBUG",
  "image_name": "/usr/lib/libnetworkextension.dylib",
  "filename": "/usr/sbin/mDNSResponder",
  "message": "NEHelperTrackerGetDisposition: lookup for <ec2-54-215-0-19.us-west-1.compute.amazonaws.com> length 47 (app info ref C2910FF0 pid 282 for Web)",
  "label": { "category": "", "subsystem": "com.apple.networkextension" }
}

This event (or anything similar) was missing on the iOS18 device. If you install the CFNetwork Diagnostics Configuration Profile, the DNS lookups are logged unredacted:

// Request
{
  "pid": 144,
  "timestamp": "2024-10-02T16:55:35.665548",
  "level": "NOTICE",
  "image_name": "/usr/sbin/mDNSResponder",
  "filename": "/usr/sbin/mDNSResponder",
  "message": "[R26617] getaddrinfo start -- flags: 0xC000D000, ifindex: 0, protocols: 0, hostname: ec2-54-215-0-19.us-west-1.compute.amazonaws.com, options: 0xC {in-app-browser, use-failover}, client pid: 47601 (com.apple.WebKi), delegator pid: 47598 (MobileSafari)",
  "label": { "category": "dnssd_server", "subsystem": "com.apple.mdns" }
}

// Response
{
    "pid": 144,
    "timestamp": "2024-10-02T16:55:35.666772",
    "level": "NOTICE",
    "image_name": "/usr/sbin/mDNSResponder",
    "filename": "/usr/sbin/mDNSResponder",
    "message": "[R26617->Q25753] getaddrinfo result -- event: add, ifindex: 0, name: ec2-54-215-0-19.us-west-1.compute.amazonaws.com., type: A, rdata: 13.57.37.227, expired: no",
    "label": {
      "category": "dnssd_server",
      "subsystem": "com.apple.mdns"
    }
}

In terms of actual network connection information, without needing a Configuration Profile these events were periodically sent detailing some network information, but not actual IP addresses etc:

{
  "pid": 68550,
  "timestamp": "2024-10-07T16:51:34.222655",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/Symptoms.framework/Frameworks/SymptomEvaluator.framework/SymptomEvaluator",
  "filename": "/usr/libexec/symptomsd",
  "message": "Data Usage for wget on flow 7176 - WiFi in/out: 1080/757, WiFi delta_in/delta_out: 270/171, Cell in/out: 572451/1563, Cell delta_in/delta_out: 0/0, RNF: 0, subscriber tag: 0",
  "label": { "category": "analytics", "subsystem": "com.apple.symptomsd" }
}

While not useful for telling exactly what hosts the device is talking to, these events may be useful to spot unusual processes transferring an unusual amount of data over the network.

I also observed these events while using commandline tools on the jailbroken iOS16 device, e.g. when using wget from “Procursus Team” on Sileo Store:

{
  "pid": 69302,
  "timestamp": "2024-10-07T16:44:45.586970",
  "level": "DEBUG",
  "image_name": "/usr/lib/system/libsystem_info.dylib",
  "filename": "/private/preboot/DEE19D683D13EA5F16C71B676AF1D2C3C0F84C8F33659BE2BEE207865AA501FC9B38E5051439961162D5A61A2AC4415B/jb-qL8pPRKx/procursus/usr/bin/wget",
  "message": "nat64_v4_requires_synthesis(54.215.0.19) == false",
  "label": {
    "category": "getaddrinfo",
    "subsystem": "com.apple.network.libinfo"
  }
}

None of this contains port information, but there is an easier way to collect network information from a device…

Networking - Using PCAPs

Starting with iOS 5, apple added a remote virtual interface (RVI) facility that allows mirroring networks traffic from an iOS device. This is done via another Lockdown Service com.apple.pcapd. Helpfully, unlike like a typical external network tap, the events received from pcapd contain both the raw traffic and information on what Process is responsible for the traffic.

As a demonstration, this code combines pyMobileDevice3 with Scapy to parse unencrypted DNS traffic:

from pymobiledevice3.services.pcapd import PcapdService
from scapy.all import sr1,IP,ICMP, Ether, DNS

# lockdown = create_using_tcp(...)
service = PcapdService(lockdown)
    for packet in service.watch():
        e = Ether(packet.data)
        if e.haslayer(DNS):
            print(f"epid: {packet.epid} | pid: {packet.pid} | comm: {packet.comm}")
            print(e.show())

An example of this code’s output is:

epid: 49370 | pid: 144 | comm: mDNSResponder
###[ Ethernet ]###
  dst       = 64:5a:ed:12:5a:4f
  src       = 74:ac:b9:37:87:04
  type      = IPv4
###[ IP ]###
     version   = 4
     ihl       = 5
     tos       = 0x0
     len       = 110
     id        = 29395
     flags     = DF
     frag      = 0
     ttl       = 64
     proto     = udp
     chksum    = 0x4377
     src       = 192.168.1.1
     dst       = 192.168.1.227
     \options   \
###[ UDP ]###
        sport     = domain
        dport     = 57369
        len       = 90
        chksum    = 0x3703
###[ DNS ]###
           id        = 39303
           qr        = 1
           opcode    = QUERY
           aa        = 0
           tc        = 0
           rd        = 1
           ra        = 1
           z         = 0
           ad        = 0
           cd        = 0
           rcode     = ok
           qdcount   = 1
           ancount   = 1
           nscount   = 0
           arcount   = 0
           \qd        \
            |###[ DNS Question Record ]###
            |  qname     = b'ec2-13-57-37-224.us-west-1.compute.amazonaws.com.'
            |  qtype     = A
            |  unicastresponse= 0
            |  qclass    = IN
           \an        \
            |###[ DNS Resource Record ]###
            |  rrname    = b'ec2-13-57-37-224.us-west-1.compute.amazonaws.com.'
            |  type      = A
            |  cacheflush= 0
            |  rclass    = IN
            |  ttl       = 86400
            |  rdlen     = None
            |  rdata     = 13.57.37.224
           \ns        \
           \ar        \

The pid:144 and comm:mDNSResponder point to the service doing the actual lookup, which in this case is the device’s centralised DNS service But the epid:49370 is the Process ID that initiated the DNS lookup, which in this example was curl. By parsing more packets, we would see both the initial request, and the actual network connection to 13.57.37.224.

The caveat to this method is most network data would still be encrypted under TLS, including DNS traffic if using DNS-Over-HTTPS/TLS), but the addition of process information does make this more useful than regular external network monitoring, as we can can spot situations where an unusual process is talking to a ‘normal’ remote server.

File Access

The last of the ‘usual’ events to capture on a system, getting information on interesting files being written to disk also proved difficult.

One of the more interesting files to observe being written is Crash Logs, which might be an indication of a process being exploited and left in an unstable state, for example the BLASTPASS attack. Helpfully we get events whenever a new crash report is generated, for example when I accidentally made Safari crash:

{
  "pid": 213,
  "timestamp": "2024-10-20T09:21:51.268620",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/OSAnalytics.framework/OSAnalytics",
  "filename": "/System/Library/CoreServices/osanalyticshelper",
  "message": "Saved type '309(<private>)' report (4 of max 25) at /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports/MobileSafari-2024-10-20-092151.ips",
  "label": { "category": "", "subsystem": "" }
}
{
  "pid": 7551,
  "timestamp": "2024-10-20T09:21:52.048922",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/OSAnalytics.framework/OSAnalytics",
  "filename": "/System/Library/CoreServices/OTACrashCopier",
  "message": "Retiring (309) submitted '/private/var/mobile/Library/Logs/CrashReporter/Retired/MobileSafari-2024-10-20-092151.ips': success",
  "label": { "category": "", "subsystem": "" }
}

As the filename of the crashdump (MobileSafari-2024-10-20-092151.ips in this case) relates to the name of the process that crashed, and depending on what crashed this might be an excellent reason to trigger a forensic snapshot. This crashdump can be collected remotely from the device, either as part of a sysdiagnose snapshot, or directly from the com.apple.crashreportcopymobile Lockdown Service/ with pyMobileDevice’s CrashReportsManager class:

from pymobiledevice3.services.crash_reports import CrashReportsManager

# lockdown = create_using_tcp(...)
crash_service = CrashReportsManager(lockdown)
crash_service.pull("output.ips", "/Retired/MobileSafari-2024-10-20-092151.ips")

To crack the creation or access of other files, on iOS the fseventsd daemon keeps track of filesystem changes, storing the logs on the device in the /private/var/db/fseventsd directory. These logs can collected from Jailbroken devices as the root user, which can then be parsed by tools such as this one from David Cowen. I believe collecting these logs from a non-jailbroken device can only be done as part of the sysdiagnose snapshot.

Whenever fseventsd went to create a new logfile on the jailbroken iOS16 device I saw this event, however this appears to be a jailbreak artefact and not something seen normally:

{
  "pid": 65285,
  "timestamp": "2024-10-04T14:49:36.798972",
  "level": "ERROR",
  "image_name": "/usr/libexec/fseventsd",
  "filename": "/usr/libexec/fseventsd",
  "message": "scan_old: bailing out because device mounted @ <private> has dls 0x0 and dls->fci 0x0",
  "label": { "category": "daemon", "subsystem": "com.apple.fsevents" }
}

Messaging

The next part of my research focussed on more specific signs of initial exploitation or malicious data collection.

Based on the Research into iOS malware such as Pegasus and TriangleDB, a common way to exploit an iOS device appears to be sending a malicious message to the device using formats like iMessage or SMS, oftentimes exploiting iOS’ parsing of files attached to the messages. I wanted to look for events related to incoming messages, as well as any attachments that may have been sent, so I tested sending a different types of messages to the devices, including SMS, iMessage, and MMS. For iMessage I sent messages with and without file attachments.

iMessage

On iOS16, without a Configuration Profile, no logs contained the sender information or message text unredacted. When sending various types of file attachments, I would occasionally see the following error log that does contain the filename, although no other useful information:

{
  "pid": 0,
  "timestamp": "2024-10-07T17:23:56.047715",
  "level": "ERROR",
  "image_name": "/System/Library/Extensions/Sandbox.kext/Sandbox",
  "filename": "/kernel",
  "message": "System Policy: suggestd(200) deny(1) file-read-metadata /private/var/mobile/Library/SMS/Attachments/74/04/D9C5BF2C-1C0A-4260-BA3D-6459FFA6D961/Form-10-Inventory-of-assets-and-liabilities-1.docx",
  "label": { "category": "", "subsystem": "" }
}

On iOS18, without Profile I observed an event that contained the sender info (+61411111111 in this example) :

{
  "pid": 78921,
  "timestamp": "2024-10-07T23:03:32.005659",
  "level": "INFO",
  "image_name": "/System/Library/PrivateFrameworks/IMCore.framework/IMCore",
  "filename": "/Applications/MobileSMS.app/MobileSMS",
  "message": "--> Chat iMessage;-;+61411111111 has 0 remaining holds: <private>",
  "label": { "category": "IMChat", "subsystem": "com.apple.Messages" }
}

When sending an attachment, this event was also created, but no other information:

{
  "pid": 220,
  "timestamp": "2024-10-07T23:03:31.900702",
  "level": "INFO",
  "image_name": "/System/Library/PrivateFrameworks/IMCore.framework/IMCore",
  "filename": "/System/Library/PrivateFrameworks/SOS.framework/sosd",
  "message": "Posting transfer, guid: <private>",
  "label": {
    "category": "IMFileTransferCenter",
    "subsystem": "com.apple.Messages"
  }
}

Adding the FaceTime and Call Activity Logging Configuration Profile is where things got interesting. This profile unredacts a lot of information in the logs, for example this event that contains the sender and receiver’s details:

{
    "pid": 69,
    "timestamp": "2024-10-07T17:48:37.770384",
    "level": "INFO",
    "image_name": "/System/Library/Messages/PlugIns/iMessage.imservice/iMessage",
    "filename": "/System/Library/PrivateFrameworks/IMCore.framework/imagent.app/imagent",
    "message": "Starting processing post blastdoor for messageItem: IMMessageItem[outgoing: NO sender=+61411111111; service=iMessage; handle=(null); destinationCallerID= +61422222222, unformatted=(null); country=(null); roomName='(null)'; flags=1; subject='(null)' text='(null)' messageID: 0 GUID:'3DFC4B49-2D9A-4C45-8D1C-8624D4339D3F' sortID: 0 date:'749976517.015652' date-delivered:'0.000000' date-read:'0.000000' date-played:'0.000000' transfer guids: '(\n    \"BFF68274-7B55-46E6-AC74-3A0B70F88504\"\n)' empty: NO finished: YES sent: NO read: NO delivered: NO audio: NO played: NO from-me: NO DD results: NO DD Scanned: NO Downgraded: NO emote: NO expirable: NO expire-state: 0 balloon-bundle-id: (null) expressive-send-style-id: (null) time-expressive-send-played: (null) bizIntent: (null) locale: (null) error: 0 sync-state 0 corrupt: NO shouldSendMeCard: NO isSpam: NO hasUnseenMention: NO threadIdentifier: (null), threadOriginator: (null), replyCountsByPart: (null), isChoros: NO, chorosConversationID: -1, syndicationRanges: (null), syncedSyndicationRanges: (null), dateEdited",
    "label": {
      "category": "MessageService",
      "subsystem": "com.apple.Messages"
    }
  }

While this event doesn’t contain much information about the attachment, if you lookup the ‘transfer guid’ in other events (BFF68274-7B55-46E6-AC74-3A0B70F88504 in this example), you would find information on other events about both the filename and the iCloud URL used to facilitate the file transfer:

{
    "pid": 272,
    "timestamp": "2024-10-07T17:48:37.831417",
    "level": "INFO",
    "image_name": "/System/Library/PrivateFrameworks/IMCore.framework/IMCore",
    "filename": "/System/Library/PrivateFrameworks/SOS.framework/sosd",
    "message": "Posting transfer created: [IMFileTransfer: 0x66eb4a410  state: Waiting for Accept  sync state: Not synced  local path: (null)  transferred name: (null)  guid: BFF68274-7B55-46E6-AC74-3A0B70F88504  error: -1  total bytes: 2427577  created: 2024-10-07 06:48:37 +0000 commSafety: 0]  transferName: apple-platform-security-guide.pdf",
    "label": {
      "category": "IMFileTransferCenter",
      "subsystem": "com.apple.Messages"
    }
}
{
    "pid": 69,
    "timestamp": "2024-10-07T17:48:37.834690",
    "level": "INFO",
    "image_name": "/System/Library/Messages/PlugIns/iMessage.imservice/iMessage",
    "filename": "/System/Library/PrivateFrameworks/IMCore.framework/imagent.app/imagent",
    "message": "    user info: {\n    \"decryption-key\" = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;\n    \"file-size\" = 2427577;\n    \"mime-type\" = \"application/pdf\";\n    \"mmcs-owner\" = \"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDD.EEEEEEEE\";\n    \"mmcs-signature-hex\" = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB;\n    \"mmcs-url\" = \"https://p40-content.icloud.com/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDD.EEEEEEEE\";\n    name = \"apple-platform-security-guide.pdf\";\n    \"uti-type\" = \"com.adobe.pdf\";\n}",
    "label": {
      "category": "Attachments",
      "subsystem": "com.apple.Messages"
    }
}
{
    "pid": 69,
    "timestamp": "2024-10-07T17:48:38.076867",
    "level": "INFO",
    "image_name": "/System/Library/PrivateFrameworks/IMDaemonCore.framework/IMDaemonCore",
    "filename": "/System/Library/PrivateFrameworks/IMCore.framework/imagent.app/imagent",
    "message": "Local path: (null), filename: apple-platform-security-guide.pdf",
    "label": {
      "category": "Attachments",
      "subsystem": "com.apple.Messages"
    }
}

The initial event does make mention as occurring after running the file is run through Blastdoor, a security service designed to deal with untrusted data in a secure way. This means exploitation could occur prior to this event being created, in which case even with a Configuration Profile we only have this message to indicate blastdoor is about the be activated:

{
  "pid": 61,
  "timestamp": "2024-10-07T13:23:08.237016",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/IDS.framework/identityservicesd.app/identityservicesd",
  "filename": "/System/Library/PrivateFrameworks/IDS.framework/identityservicesd.app/identityservicesd",
  "message": "IDSBlastDoor: Incoming message <private> has constraint type 0 and BlastDoor context <private>",
  "label": { "category": "IDSDaemon", "subsystem": "com.apple.IDS" }
}

In theory, either a program crash, or otherwise lack of follow-up event after this initial blast door event, could mean something suspicious happened during blast door processing.

Finally, with the Configuration Profile, the message content was sometimes available in the following message under CKBBContentKeyAttachmentSummary (I sent the message “AAAA” in this example):

{
  "pid": 185,
  "timestamp": "2024-10-07T17:48:38.783849",
  "level": "INFO",
  "image_name": "/System/Library/PrivateFrameworks/IMDPersistence.framework/IMDPersistence",
  "filename": "/System/Library/PrivateFrameworks/IMDPersistence.framework/XPCServices/IMDPersistenceAgent.xpc/IMDPersistenceAgent",
  "message": "Set userInfo for dictionaries {\n    CKBBContentKeyAttachmentCount = 1;\n    CKBBContentKeyAttachmentSummary = AAAA;\n    CKBBContentKeyCountByAttachmentType =     {\n        \"%lu Files\" = 1;\n    };\n    CKBBContextKeyChatGUIDs =     (\n        \"iMessage;-;+6142222222\"\n    );\n    CKBBContextKeyMessageGUID = \"3DFC4B49-2D9A-4C45-8D1C-8624D4339D3F\";\n    CKBBContextKeySenderName = Host;\n    CKBBContextKeySenderPersonCentricID = \"9EFC13F1-34E0-44EA-A3E8-D63A574CA9B3:ABPerson\";\n    CKBBUserInfoKeyChatIdentifier = \"+614222222222\";\n}",
  "label": {
    "category": "IMDNotificationsController",
    "subsystem": "com.apple.Messages"
  }
}

And in this event (I sent “Apple pie”):

{
  "pid": 185,
  "timestamp": "2024-10-07T18:04:41.387819",
  "level": "DEBUG",
  "image_name": "/System/Library/PrivateFrameworks/IMFoundation.framework/IMFoundation",
  "filename": "/System/Library/PrivateFrameworks/IMDPersistence.framework/XPCServices/IMDPersistenceAgent.xpc/IMDPersistenceAgent",
  "message": " => Out attributed string: Apple pie{\n    \"__kIMMessagePartAttributeName\" = 0;\n}",
  "label": {
    "category": "DataDetector",
    "subsystem": "com.apple.IDS"
  }
}

SMS

Without any Configuration Profile, on iOS18 I observed the following event that contained the sender’s phone number:

{
  "pid": 88956,
  "timestamp": "2024-10-22T16:16:31.305446",
  "level": "INFO",
  "image_name": "/System/Library/PrivateFrameworks/IMCore.framework/IMCore",
  "filename": "/Applications/MobileSMS.app/MobileSMS",
  "message": "--> Chat SMS;-;+61411111111 has 0 remaining holds: <private>",
  "label": { "category": "IMChat", "subsystem": "com.apple.Messages" }
}

Again without any profile, when sending an image with MMS I would observe the following event that contains both the the destination filename and sender information:

{
    "pid": 32,
    "timestamp": "2024-10-07T17:32:24.931615",
    "level": "NOTICE",
    "image_name": "/System/Library/PrivateFrameworks/UserNotificationsServer.framework/UserNotificationsServer",
    "filename": "/System/Library/CoreServices/SpringBoard.app/SpringBoard",
    "message": "Successfully resolved request: resolvedRequest=<UNNotificationRequest: 0x281955500; identifier: BE51960E-42EA-4F9F-987C-15280DDF9E96, content: <UNNotificationContent: 0x4d8e3db10; title: <redacted>, subtitle: (null), body: <redacted>, summaryArgument: , summaryArgumentCount: 0, categoryIdentifier: MessageExtension-SMS, launchImageName: , threadIdentifier: +61411111111, attachments: (\n    \"<UNNotificationAttachment: 0x280fd4740; identifier: 5C508F6F-91A9-4BDE-9444-38979CC9FC9B, URL: file:///var/mobile/Library/UserNotifications/B952B9DA-F6F9-42D5-9F32-56424BC55671/Attachments/2ce27f09c7a260e0e6d8cf9ec4a90f2c09bc5c04.jpeg, type: public.jpeg, options: <UNNotificationAttachmentOptions: 0x2814fe920; displayLocation: default; thumbnailGeneratorUserInfo: {\\n}>>\"\n), badge: (null), sound: <UNNotificationSound: 0x283ea68b0>, realert: 1, interruptionLevel: 1, relevanceScore: 0.00, filterCriteria: (null), trigger: (null)>, resolutionSuccess=YES, continueOnFailure=NO",
    "label":
      {
        "category": "AttachmentsService",
        "subsystem": "com.apple.UserNotifications",
      },
  }

Other Activity

Lastly, I wanted to look for any other smaller indicators of post-exploitation activity. While many of these didn’t result in hard ‘proof’ of an attack, like detection engineering for other Operating Systems when small events are coupled together with other pieces of information they might just form enough of a pattern to prompt for a more in-depth look or forensic snapshot.

Enabling Ad Tracking

Kaspersky observed that the TriangleDB malware would enable personalized ad tracking. While I am unsure of exactly how the malware achieved this, when I toggled the option in the Setting UI it created this event:

{
  "pid": 128,
  "timestamp": "2024-10-04T13:31:35.345429",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/ManagedConfiguration.framework/ManagedConfiguration",
  "filename": "/usr/libexec/adprivacyd",
  "message": "Set Bool value YES for settings: allowApplePersonalizedAdvertising",
  "label": {
    "category": "ProfileConnection",
    "subsystem": "com.apple.ManagedConfiguration"
  }
}

Location

Without a Profile, when I used an App that requested the device’s location I observed this event:

{
  "pid": 70,
  "timestamp": "2024-10-04T13:59:24.527649",
  "level": "NOTICE",
  "image_name": "/usr/libexec/locationd",
  "filename": "/usr/libexec/locationd",
  "message": "client '[mobile]com.apple.Maps' authorized for location; starting now, desiredAccuracy, -1.0, distanceFilter, -1.0, operatingMode 0, dynamicAccuracyReductionEnabled 0, allowsAlteredAccessoryLocations 1, activityType 0",
  "label": { "category": "Client", "subsystem": "com.apple.locationd.Core" }
}

With the Location and Motion Configuration profile, I observed the actual devices location in the logs, which is expected given the Profile’s description. I am unsure if other methods of getting the device’s location produce similar events.

Entitlements

Without a profile I observed these events when a App was requesting certain Entitlements from the Transparency, Consent and Control (TCC) framework:

{
  "pid": 65424,
  "timestamp": "2024-10-04T14:52:28.100478",
  "level": "INFO",
  "image_name": "/System/Library/PrivateFrameworks/TCC.framework/TCC",
  "filename": "/Applications/MobileSlideShow.app/MobileSlideShow",
  "message": "SEND: 0/7 synchronous to com.apple.tccd: request: msgID=65424.1, function=TCCAccessRequest, service=kTCCServicePhotos,",
  "label": { "category": "access", "subsystem": "com.apple.TCC" },
}
{
  "pid": 44,
  "timestamp": "2024-10-04T14:49:49.519418",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/TCC.framework/Support/tccd",
  "filename": "/System/Library/PrivateFrameworks/TCC.framework/Support/tccd",
  "message": "Granting TCCDProcess: identifier=com.apple.mobileslideshow, pid=65396, auid=501, euid=501, binary_path=/Applications/MobileSlideShow.app/MobileSlideShow access to kTCCServicePhotos via entitlement 'com.apple.private.tcc.allow'",
  "label": { "category": "access", "subsystem": "com.apple.TCC" }
}

In this example the photos app MobileSlideshow was requesting access to the devices photo library. Similar to getting the device’s location, I am unsure if other methods of interacting with TCC produce similar events.

KeyChain access

When I used Safari to request my saved passwords from the iOS Keychain, I observed this event:

{
  "pid": 66574,
  "timestamp": "2024-10-04T16:46:49.079914",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/SafariCore.framework/SafariCore",
  "filename": "/Applications/SafariViewService.app/SafariViewService",
  "message": "-[WBSSavedAccountStore _savedAccounts]: Returning 2 saved accounts",
  "label": { "category": "Keychain", "subsystem": "com.apple.SafariShared" },
}

While this event didn’t require any Configuration profile, malware commonly collects this data from disk or other lower-level APIs, in which case this event would probably not be created.

Cookies

Similarly to keychain access, malware typically collects cookies are by reading the database stored on the devices disk, requiring file access information to detect. However in 2021 Google Observed Russia’s SVR deploy a javascript cookie stealer, that coupled with an exploit to the underlying WebKit engine was used to send sensitive cookies to an SVR-controlled website using ‘regular’ javascript.

Using the CFNetwork Configuration Profile, I observed this event when I visited some websites using Safari that read my saved cookies, that contained information on the website visited:

{
  "pid": 42891,
  "timestamp": "2024-10-04T13:52:24.335812",
  "level": "NOTICE",
  "image_name": "/System/Library/Frameworks/CFNetwork.framework/CFNetwork",
  "filename": "/System/Library/PrivateFrameworks/CoreParsec.framework/parsecd",
  "message": "CFNetwork Diagnostics [3:758] 13:52:24.335 {\nHTTPCookieStorage::copyCookiesForURL: <CFHTTPCookieStorage 0xb7c843620 [0xb7c843630]>\n                         Request URL: https://api-glb-aapse2c.smoot.apple.com/<redacted>\n                    MainDocument URL: null\n} [3:758]",
  "label": { "category": "Diagnostics", "subsystem": "com.apple.CFNetwork" },
}

Whether the SVR technique was ‘regular’ enough to generate these events is unknown.

XPC

XPC is the main inter-process communication format on iOS. This format was explicitly used by NSO’s FORCEDENTRY exploit to escape the Application Sandbox. Without any Configuration Profile I observed a number of XPC related messages being regularly created by my devices. These events contain information on what processes are being communicated with and by whom:

{
  "pid": 223,
  "timestamp": "2024-10-04T13:43:24.510463",
  "level": "NOTICE",
  "image_name": "/System/Library/PrivateFrameworks/SPShared.framework/SPShared",
  "filename": "/usr/libexec/searchpartyd",
  "message": "XPCSessionManager(BeaconManagerService): New XPC connection: <NSXPCConnection: 0xae0a5a440> connection from pid 42697 on mach service named com.apple.icloud.searchpartyd.pairingmanager",
  "label":
    {
      "category": "xpcSessionManager",
      "subsystem": "com.apple.icloud.spshared",
    },
}
{
  "pid": 68634,
  "timestamp": "2024-10-07T14:08:06.935270",
  "level": "INFO",
  "image_name": "/System/Library/Frameworks/LocalAuthentication.framework/Support/coreauthd",
  "filename": "/System/Library/Frameworks/LocalAuthentication.framework/Support/coreauthd",
  "message": "MainServiceListener has accepted a new connection: <NSXPCConnection: 0x710d047d0> connection from pid 68598 on mach service named com.apple.CoreAuthentication.daemon hash:10d047d0",
  "label": {
    "category": "Server,LAContext",
    "subsystem": "com.apple.LocalAuthentication"
  }
}

I am uncertain exactly what messages would have been created during FORCEDENTRY’s exploitation.

Conclusions and Future work

While this research is still very much an ongoing project, I feel there is already enough threads to pull on to warrant more investigation. A lot of ‘normal’ events used by endpoint security systems on other Operating Systems are lacking, which does make a system build solely on Unified Logging data unlikely to be that useful as a security solution. Especially given that a proper security system would be expected to do something about a detected threat or anomalous activity, beyond just a notification.

But, there are enough events, that with a proper understanding of what is ‘normal’ for that device (e.g. who that phone usually receives messages from, or what Apps ask for what data), it might be possible to at least inform a user that they should take and analyse a forensics snapshot, which might then might contain more concrete indicators of compromise. An especially interesting detection strategy might be to look for unusual process crashes, then pulling and analysing the Crash Logs to determine if a full forensic snapshot is needed.

I do plan to continue more research into this topic, starting with understanding the Wi-Fi limitation. The next will probably be around choosing specific events to collect, parse, and send to a SIEM, to understand the volume and usefulness of data collected from an actual in-use device. The third will be looking for more interesting events that might contain atypical-but-useful data that could show signs of exploitation or post-exploitation activity. This includes looking at other Lockdown Services for other interesting sources of data.

Epilogue - Offensive Uses

I want to touch on a few final thoughts on the topic of Offensive uses of this technique.

Firstly, sending log data to a remote machine does does come with some security risk. Due to their less locked-down architectures, other Operating Systems such as MacOS, Windows, or Linux, may be more open to attack by malicious actors. If the remote machine is compromised, the attackers would be able to collect the logs sent from the iOS device, which if certain Configuration Profiles are used could contain sensitive personal information.

The positive take, however, is that these other Operating Systems also allow advanced security products to detect sophisticated attacks, in ways not possible on iOS. In fact in some scenarios, with the increased visibility enabled by these security products, ‘forcing’ the attacker onto the other operating systems might even be preferable.

Secondly, there is a question about whether a malicious actor could use this technique in replacement of deploying their own malware on the device. There are documented attacks where a user is phished or coerced into installing malicious Configuration Profiles, which require a similar amount of physical interacting with the device to setup, and would therefore require a similar amount of deception/coercion to setup malicious Unified Logging collection.

As a more sophisticated hypothetical, an attacker who has exploited a device to gain root privileges would have the ability to both perform log collection on the device, as well as unredact any sensitive information before sending the logs to their own (untrusted) Command-And-Control servers. Any malware that employs this may be able to reduce its on-device footprint, as it would not need to touch or hook certain parts of the device in order to collect information available in the logs.

Lastly, if this technique becomes ubiquitous enough for defenders to regularly use, it may push malicious actors to develop techniques to tamper with the Unified Logging system, to either remove their activity from the logs, or intercept the reading of the logs and ‘filter out’ or obfuscate their activity. The advantage of reading and reacting to the logs in real-time could mean that enough ‘suspicious’ events might be generated during the attackers initial attack stages, before the attacker has enough control over the device to scrub the logs. If this is the case, the user may be notified to take a forensic snap before the malware can react. And as evidenced by similar log-scrubbing attacks on other Operating Systems, removing log entries in a non-suspicious way can be more difficult than it appears, as gaps in logs may be just as suspicious as the log entries themselves.