<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.tofile.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.tofile.dev/" rel="alternate" type="text/html" /><updated>2026-04-27T07:34:16+00:00</updated><id>https://blog.tofile.dev/feed.xml</id><title type="html">pat_h/to/file</title><subtitle>PatH&apos;s infosec blog</subtitle><entry><title type="html">Investigating realtime detections on iOS using Unified Logging</title><link href="https://blog.tofile.dev/2024/10/24/ios-detections.html" rel="alternate" type="text/html" title="Investigating realtime detections on iOS using Unified Logging" /><published>2024-10-24T12:00:00+00:00</published><updated>2024-10-24T12:00:00+00:00</updated><id>https://blog.tofile.dev/2024/10/24/ios-detections</id><content type="html" xml:base="https://blog.tofile.dev/2024/10/24/ios-detections.html"><![CDATA[<p><em>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.</em></p>

<p>While looking for a new research project, I recently watched an excellent talk by <a href="https://x.com/Helthydriver">Matthias Frielingsdorf</a> on the <a href="https://www.youtube.com/watch?v=3gnGPfKu7FE">Current State Of IOS Malware Detection</a>. 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.</p>

<p>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 <a href="https://support.apple.com/en-au/105120">Lockdown Mode</a>), 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.</p>

<h3 id="table-of-contents">Table of Contents</h3>
<ul>
  <li><a href="#current-detection-efforts">Current Detection Efforts</a></li>
  <li><a href="#enter-lockdown">Enter Lockdown</a></li>
  <li><a href="#working-remotely">Working Remotely</a></li>
  <li><a href="#unified-logging">Unified Logging</a></li>
  <li><a href="#configuration-profiles">Configuration Profiles</a></li>
  <li><a href="#collecting-data">Collecting Data</a>
    <ul>
      <li><a href="#process-creation">Process Creation</a></li>
      <li><a href="#networking">Networking</a></li>
      <li><a href="#networking---using-pcaps">Networking - Using PCAPs</a></li>
      <li><a href="#file-access">File Access</a></li>
    </ul>
  </li>
  <li><a href="#messaging">Messaging</a>
    <ul>
      <li><a href="#imessage">iMessage</a></li>
      <li><a href="#sms">SMS</a></li>
    </ul>
  </li>
  <li><a href="#other-activity">Other Activity</a>
    <ul>
      <li><a href="#enabling-ad-tracking">Enabling Ad Tracking</a></li>
      <li><a href="#location">Location</a></li>
      <li><a href="#entitlements">Entitlements</a></li>
      <li><a href="#keychain-access">KeyChain access</a></li>
      <li><a href="#cookies">Cookies</a></li>
      <li><a href="#xpc">XPC</a></li>
    </ul>
  </li>
  <li><a href="#conclusions-and-future-work">Conclusions and Future work</a></li>
  <li><a href="#epilogue---offensive-uses">Epilogue - Offensive Uses</a></li>
</ul>

<h1 id="current-detection-efforts">Current Detection Efforts</h1>
<p>The most common technique defenders have in uncovering intrusions on iOS devices is to create a forensic snapshot of the device using the <a href="https://it-training.apple.com/tutorials/support/sup075/">Sysdiagnose</a> feature. This has been used by <a href="https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/">Amnesty</a> <a href="https://github.com/mvt-project/mvt/tree/main">International</a>, <a href="https://citizenlab.ca/2021/09/forcedentry-nso-group-imessage-zero-click-exploit-captured-in-the-wild/">The Citizen Lab</a>, and others to great success in the past. But they suffer from two major flaws:</p>
<h3 id="1-snapshots-give-time-for-attackers-to-hide">1. Snapshots give time for attackers to hide</h3>
<p>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.</p>
<h3 id="2--you-need-to-know-when-to-make-a-snapshot">2.  You need to know when to make a snapshot</h3>
<p>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.</p>

<p>In Kaspersky’s reporting on the <a href="https://securelist.com/operation-triangulation-catching-wild-triangle/110916/">TriangleDB Malware</a>, 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 <a href="https://www.cloudflare.com/application-services/products/cdn/">Content Delivery Networks</a>, TLS encryption, and the increasing use of encrypted DNS via <a href="https://www.cloudflare.com/en-gb/learning/dns/dns-over-tls/">DNS-Over-TLS</a>.</p>

<p>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.</p>

<h1 id="enter-lockdown">Enter Lockdown</h1>
<p>Remote machines can communicate to a special daemon running on iOS devices called <code class="language-plaintext highlighter-rouge">lockdownd</code>. 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).</p>

<p>To explain both how the pairing and communication works, here is 2 excellent writeups that will explain it far better than I can:</p>
<ul>
  <li><a href="https://jon-gabilondo-angulo-7635.medium.com/understanding-usbmux-and-the-ios-lockdown-service-7f2a1dfd07ae">Understanding usbmux and the iOS lockdown service</a> - Jon Gabilondo</li>
  <li><a href="https://github.com/doronz88/pymobiledevice3/blob/master/misc/understanding_idevice_protocol_layers.md">Understanding iDevice protocol layers</a> - PyMobileDevice3 dev team</li>
</ul>

<p>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.</p>

<p><img src="/assets/ios_trust_computer.jpg" alt="Screenshot of an iOS device, with a prompt asking if the human trusts the connected device" style="display:block; margin-left:auto; margin-right:auto" /></p>

<p><em>Screenshot of an iOS device, with a prompt asking if the human trusts the connected device</em></p>

<h1 id="working-remotely">Working Remotely</h1>
<p>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 <a href="https://developer.apple.com/bonjour/">Bonjour/Multicast DNS protocol</a>. 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 href="https://www.gl-inet.com/products/gl-x3000/">a portable Linux Wi-Fi router</a> 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.</p>

<p>However, it turns out the initial handshake isn’t strictly necessary every connection. An excellent tool for communicating with <code class="language-plaintext highlighter-rouge">lockdownd</code> and Lockdown Services is <a href="https://github.com/doronz88/pymobiledevice3">pyMobileDevice3</a>, a pure-python implementation of the older  <a href="https://libimobiledevice.org/">libmobiledevice</a> 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:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># First, get device identifier from:</span>
pymobiledevice3 usbmux list

<span class="c"># Ensure devices are paired:</span>
pymobiledevice3 lockdown pair

<span class="c"># Turn connections via 'Wi-Fi' on</span>
pymobiledevice3 lockdown wifi-connections on

<span class="c"># Save pair record for future access:</span>
<span class="c"># This contains sensitive data so don't post to Twitter or something</span>
pymobiledevice3 lockdown save-pair-record pair_record.plist
</code></pre></div></div>

<p>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 <a href="https://tailscale.com">Tailscale</a> to link the 2 devices together. Using Python on my remote machine, I did this by:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">plistlib</span>
<span class="kn">from</span> <span class="nn">pymobiledevice3.lockdown</span> <span class="kn">import</span> <span class="n">create_using_tcp</span>

<span class="c1"># Open and parse saved pair record
</span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"pair_record.plist"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">plist</span> <span class="o">=</span> <span class="n">plistlib</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>

<span class="n">lockdown</span> <span class="o">=</span> <span class="n">create_using_tcp</span><span class="p">(</span>
  <span class="c1"># IP Address or hostname of iOS device
</span>  <span class="n">hostname</span><span class="o">=</span><span class="s">"10.11.22.33"</span><span class="p">,</span>
  <span class="c1"># Identifier from 'pymobiledevice3 usbmux list'
</span>  <span class="n">identifier</span><span class="o">=</span><span class="s">"11111111-2222222222222222"</span><span class="p">,</span>
  <span class="c1"># plist from disk
</span>  <span class="n">pair_record</span><span class="o">=</span><span class="n">plist</span><span class="p">,</span>
<span class="p">)</span>

<span class="c1"># Can now use lockdown to connect to services, see next paragraphs
</span><span class="n">trace_service</span> <span class="o">=</span> <span class="n">OsTraceService</span><span class="p">(</span><span class="n">lockdown</span><span class="p">)</span>
</code></pre></div></div>

<p>There is a <em>massive</em> caveat with this technique, however - It still requires the iOS device to be connected to <em>some type</em> 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 <code class="language-plaintext highlighter-rouge">lockdownd</code> daemon will refuse any connection. So you could walk around tethered to something like a <a href="https://www.unihertz.com/products/jelly-2">small Android Phone</a>, and it’d work I guess?</p>

<p>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 <em>actually do</em> with access the Lockdown services.</p>

<h1 id="unified-logging">Unified Logging</h1>
<p>Like most UNIXes, iOS has a centralised system-wide logging system. Unlike other UNIXs that primarily use syslog, modern iOS uses a system called <a href="https://developer.apple.com/documentation/os/logging/">Unified Logging</a>, which enables applications and programs to produce structured log entries in that can be performantly written and read in real-time. Managed by the <code class="language-plaintext highlighter-rouge">logd</code> 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.</p>

<p>Applications can’t access the logs of other applications or system components, but a paired remote device <em>can</em> read logs from the whole system, by using <code class="language-plaintext highlighter-rouge">lockdownd</code> to connect to the <a href="https://github.com/doronz88/pymobiledevice3/blob/master/pymobiledevice3/services/os_trace.py#L104">os_trace_relay</a> Lockdown Service. This is helpfully implemented in pyMobileDevice3, where we can simply run this to start reading and parsing log messages in real-time:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kn">import</span> <span class="nn">plistlib</span>
<span class="kn">from</span> <span class="nn">pymobiledevice3.lockdown</span> <span class="kn">import</span> <span class="n">create_using_tcp</span>
<span class="kn">from</span> <span class="nn">pymobiledevice3.services.os_trace</span> <span class="kn">import</span> <span class="n">OsTraceService</span>

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

<p>When using this script, we can capture events that look like this:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">65424</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T14:52:28.100478"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/TCC.framework/TCC"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Applications/MobileSlideShow.app/MobileSlideShow"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SEND: 0/7 synchronous to com.apple.tccd: request: msgID=65424.1, function=TCCAccessRequest, service=kTCCServicePhotos,"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"access"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.TCC"</span><span class="w"> </span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">pid</code> and <code class="language-plaintext highlighter-rouge">filename</code> are the Process ID and program responsible the event, in this case the default Photos app (called <code class="language-plaintext highlighter-rouge">MobileSlideShow.app</code> to iOS). <code class="language-plaintext highlighter-rouge">image_name</code> 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.</p>

<p>The <code class="language-plaintext highlighter-rouge">label</code> 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 <code class="language-plaintext highlighter-rouge">message</code> 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.</p>

<p>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.</p>

<h1 id="configuration-profiles">Configuration Profiles</h1>
<p>Before detailing what data I was able to collect from my device’s logs, it’s useful to talk about <a href="https://support.apple.com/en-au/guide/deployment/depc0aadd3fe/web">Configuration Profiles</a>. Designed to either be installed ‘manually’ on a device or via a <a href="https://support.apple.com/en-au/guide/deployment/depc0aadd3fe/web">Mobile Device Management (MDM) solution</a>, 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:</p>

<p><img src="/assets/ios_config_profile.jpg" alt="Screenshot of an iOS device, with a prompt asking if the human wants to install the 'Facetime and Call Activity Logging' Configuration Profile" style="display:block; margin-left:auto; margin-right:auto" /></p>

<p><a href="https://www.jamf.com/blog/malicious-profiles-come/">They have been used for malicious purposes</a>, 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 <a href="https://developer.apple.com/bug-reporting/profiles-and-logs/">Special Configuration Profiles</a>, 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:</p>

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

  <p>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.</p>

  <p>The profile will expire after 7 days.</p>
</blockquote>

<p>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 <a href="https://github.com/pathtofile/ios_configuration_profiles">this repo</a> for reference.</p>

<p>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:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
  <span class="nt">&lt;key&gt;</span>ConsentText<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>default<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>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.<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;/dict&gt;</span>
  <span class="nt">&lt;key&gt;</span>DurationUntilRemoval<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;real&gt;</span>604800.0<span class="nt">&lt;/real&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadContent<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;array&gt;</span>
    <span class="nt">&lt;dict&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadContent<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;dict&gt;</span>
          <span class="nt">&lt;key&gt;</span>DefaultsData<span class="nt">&lt;/key&gt;</span>
          <span class="nt">&lt;dict&gt;</span>
            <span class="nt">&lt;key&gt;</span>AppleCFNetworkDiagnosticLogging<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;integer&gt;</span>3<span class="nt">&lt;/integer&gt;</span>
          <span class="nt">&lt;/dict&gt;</span>
          <span class="nt">&lt;key&gt;</span>DefaultsDomainName<span class="nt">&lt;/key&gt;</span>
          <span class="nt">&lt;string&gt;</span>.GlobalPreferences<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;/dict&gt;</span>
      <span class="nt">&lt;/array&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadDescription<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;string&gt;</span>Enables CFNetwork diagnostic logging<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadDisplayName<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;string&gt;</span>CFNetwork Extended Logging<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadIdentifier<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;string&gt;</span>com.apple.defaults.managed.cfnetworklogging<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadOrganization<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;string&gt;</span>Apple, Inc<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadType<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;string&gt;</span>com.apple.defaults.managed<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadUUID<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;string&gt;</span>3A614BF2-2651-4A2A-9FA1-7556444401E9<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;key&gt;</span>PayloadVersion<span class="nt">&lt;/key&gt;</span>
      <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
    <span class="nt">&lt;/dict&gt;</span>
  <span class="nt">&lt;/array&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadDescription<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>Enables CFNetwork logging. Reboot required.<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadDisplayName<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>CFNetwork Diagnostics<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadIdentifier<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>com.apple.cfnetworklogging<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadOrganization<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>Apple Inc.<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadRebootSuggested<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadType<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>Configuration<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadUUID<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>E524E135-447D-439D-BBDD-F7E49FCE36E5<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>PayloadVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>RemovalDate<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;date&gt;</span>2025-06-30T07:00:00Z<span class="nt">&lt;/date&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>This is similar for most of the profiles I investigated.</p>

<p>Another important note is Configuration Profiles cannot be installed while a device is in <a href="https://support.apple.com/en-au/105120">Lockdown Mode</a>. 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:</p>
<ol>
  <li>Go into settings to turn off Lockdown Mode</li>
  <li>Reboot the device</li>
  <li>Install the new Configuration Profile (which may require another reboot)</li>
  <li>Turn Lockdown Mode back on</li>
  <li>Reboot the device again</li>
</ol>

<p>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.</p>

<h1 id="collecting-data">Collecting Data</h1>
<p>My research was conducted using 2 iOS devices that I happen to have on hand:</p>
<ul>
  <li>A Jailbroken iPhoneX running iOS 16</li>
  <li>A non-Jailbroken iPhone 14 running iOS 18</li>
</ul>

<p>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.</p>

<p>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.</p>

<p>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.</p>

<h2 id="process-creation">Process Creation</h2>
<p>When launching an Application, I observed the following event being logged, that contained the Application name and some other details (Safari in this case):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'application&lt;com.apple.mobilesafari&gt;' Constructed job description (context &lt;RBSLaunchContext: 0xc5ae2e080&gt;):
&lt;dictionary: 0xc5aa4a930&gt; { count = 18, transaction: 0, voucher = 0x0, contents =
  "ProcessType" =&gt; &lt;string: 0xc5aa6cdd0&gt; { length = 9, contents = "SystemApp" }
  "EnableTransactions" =&gt; &lt;bool: 0x200b33fa0&gt;: false
  "_ManagedBy" =&gt; &lt;string: 0xc5aa6b4d0&gt; { length = 22, contents = "com.apple.runningboard" }
  "CFBundleIdentifier" =&gt; &lt;string: 0xc5aa6e1d0&gt; { length = 22, contents = "com.apple.mobilesafari" }
  "_ResourceCoalition" =&gt; &lt;string: 0xc5aa3bc90&gt; { length = 35, contents = "application&lt;com.apple.mobilesafari&gt;" }
  "ThrottleInterval" =&gt; &lt;int64: 0x918c4438e8499dd7&gt;: 2147483647
  "PersonaEnterprise" =&gt; &lt;int64: 0x918c443b17b67d6f&gt;: 1000
  "MachServices" =&gt; &lt;dictionary: 0xc5aa6c310&gt; { count = 0, transaction: 0, voucher = 0x0, contents =
  }
  "EnablePressuredExit" =&gt; &lt;bool: 0x200b33fa0&gt;: false
  "InitialTaskRole" =&gt; &lt;int64: 0x918c443b17b66227&gt;: 1
  "UserName" =&gt; &lt;string: 0xc5aa6d5a0&gt; { length = 6, contents = "mobile" }
  "EnvironmentV&lt;…&gt;
</code></pre></div></div>

<p>Unfortunately, I did not observe these or similar events when launching ‘native’ executables. As an alternative to relying on just the logs, the <code class="language-plaintext highlighter-rouge">os_trace_relay</code> Lockdown Service does allow you to query the current list of running processes. This is done with pyMobileDevice3 by doing:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lockdown = create_using_tcp(...)
</span><span class="n">trace_service</span> <span class="o">=</span> <span class="n">OsTraceService</span><span class="p">(</span><span class="n">lockdown</span><span class="p">)</span>
<span class="n">pids</span> <span class="o">=</span> <span class="n">trace_service</span><span class="p">.</span><span class="n">get_pid_list</span><span class="p">().</span><span class="n">get</span><span class="p">(</span><span class="s">"Payload"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">pids</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">2</span><span class="p">))</span>
</code></pre></div></div>

<p>The results contains just the PID and Executable:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"172"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ProcessName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"itunescloudd"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"142"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ProcessName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"analyticsd"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"34"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ProcessName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mediaserverd"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"112"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ProcessName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nehelper"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="err">...</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>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.</p>

<p>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.</p>

<h2 id="networking">Networking</h2>
<p>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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">Request</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">68558</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T16:24:12.388888"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[R337] getaddrinfo start -- flags: 0xC000D000, ifindex: 0, protocols: 0, hostname: &lt;mask.hash: 'OzCZtqbB+AClqJaZLncCgg=='&gt;, options: 0xC {in-app-browser, use-failover}, client pid: 288 (com.apple.WebKi), delegator pid: 282 (MobileSafari)"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dnssd_server"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.mdns"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">Response</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">68558</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T16:23:47.588887"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[R335-&gt;Q40326] getaddrinfo result -- event: add, ifindex: 0, name: &lt;private&gt;, type: A, rdata: &lt;private&gt;, expired: no"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dnssd_server"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.mdns"</span><span class="w"> </span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>These events show that <code class="language-plaintext highlighter-rouge">MobileSafari</code> 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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">175</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:09:17.579674"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DEBUG"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/lib/libnetworkextension.dylib"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NEHelperTrackerGetDisposition: lookup for &lt;ec2-54-215-0-19.us-west-1.compute.amazonaws.com&gt; length 47 (app info ref C2910FF0 pid 282 for Web)"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.networkextension"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This event (or anything similar) was missing on the iOS18 device. If you install the <code class="language-plaintext highlighter-rouge">CFNetwork Diagnostics</code> Configuration Profile, the DNS lookups are logged unredacted:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">Request</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">144</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-02T16:55:35.665548"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[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)"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dnssd_server"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.mdns"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">Response</span><span class="w">
</span><span class="p">{</span><span class="w">
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">144</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-02T16:55:35.666772"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/sbin/mDNSResponder"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[R26617-&gt;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"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dnssd_server"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.mdns"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">68550</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T16:51:34.222655"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/Symptoms.framework/Frameworks/SymptomEvaluator.framework/SymptomEvaluator"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/symptomsd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"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"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"analytics"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.symptomsd"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>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.</p>

<p>I also observed these events while using commandline tools on the jailbroken iOS16 device, e.g. when using <code class="language-plaintext highlighter-rouge">wget</code> from “Procursus Team” on Sileo Store:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">69302</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T16:44:45.586970"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DEBUG"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/lib/system/libsystem_info.dylib"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/private/preboot/DEE19D683D13EA5F16C71B676AF1D2C3C0F84C8F33659BE2BEE207865AA501FC9B38E5051439961162D5A61A2AC4415B/jb-qL8pPRKx/procursus/usr/bin/wget"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nat64_v4_requires_synthesis(54.215.0.19) == false"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"getaddrinfo"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.network.libinfo"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

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

<h2 id="networking---using-pcaps">Networking - Using PCAPs</h2>
<p>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 <code class="language-plaintext highlighter-rouge">com.apple.pcapd</code>. 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.</p>

<p>As a demonstration, this code combines pyMobileDevice3 with <a href="https://scapy.net/">Scapy</a> to parse unencrypted DNS traffic:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pymobiledevice3.services.pcapd</span> <span class="kn">import</span> <span class="n">PcapdService</span>
<span class="kn">from</span> <span class="nn">scapy.all</span> <span class="kn">import</span> <span class="n">sr1</span><span class="p">,</span><span class="n">IP</span><span class="p">,</span><span class="n">ICMP</span><span class="p">,</span> <span class="n">Ether</span><span class="p">,</span> <span class="n">DNS</span>

<span class="c1"># lockdown = create_using_tcp(...)
</span><span class="n">service</span> <span class="o">=</span> <span class="n">PcapdService</span><span class="p">(</span><span class="n">lockdown</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">packet</span> <span class="ow">in</span> <span class="n">service</span><span class="p">.</span><span class="n">watch</span><span class="p">():</span>
        <span class="n">e</span> <span class="o">=</span> <span class="n">Ether</span><span class="p">(</span><span class="n">packet</span><span class="p">.</span><span class="n">data</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="n">haslayer</span><span class="p">(</span><span class="n">DNS</span><span class="p">):</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"epid: </span><span class="si">{</span><span class="n">packet</span><span class="p">.</span><span class="n">epid</span><span class="si">}</span><span class="s"> | pid: </span><span class="si">{</span><span class="n">packet</span><span class="p">.</span><span class="n">pid</span><span class="si">}</span><span class="s"> | comm: </span><span class="si">{</span><span class="n">packet</span><span class="p">.</span><span class="n">comm</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">show</span><span class="p">())</span>
</code></pre></div></div>

<p>An example of this code’s output is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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        \
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">pid:144</code> and <code class="language-plaintext highlighter-rouge">comm:mDNSResponder</code> point to the service doing the actual lookup, which in this case is the device’s centralised DNS service But the <code class="language-plaintext highlighter-rouge">epid:49370</code> is the Process ID that initiated the DNS lookup, which in this example was <code class="language-plaintext highlighter-rouge">curl</code>. By parsing more packets, we would see both the initial request, and the actual network connection to <code class="language-plaintext highlighter-rouge">13.57.37.224</code>.</p>

<p>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.</p>

<h2 id="file-access">File Access</h2>
<p>The last of the ‘usual’ events to capture on a system, getting information on interesting files being written to disk also proved difficult.</p>

<p>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 <a href="https://securitylab.amnesty.org/latest/2023/12/pegasus-zero-click-exploit-threatens-journalists-in-india/">BLASTPASS attack</a>. Helpfully we get events whenever a new crash report is generated, for example when I accidentally made Safari crash:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">213</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-20T09:21:51.268620"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/OSAnalytics.framework/OSAnalytics"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/CoreServices/osanalyticshelper"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Saved type '309(&lt;private&gt;)' report (4 of max 25) at /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports/MobileSafari-2024-10-20-092151.ips"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">7551</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-20T09:21:52.048922"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/OSAnalytics.framework/OSAnalytics"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/CoreServices/OTACrashCopier"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Retiring (309) submitted '/private/var/mobile/Library/Logs/CrashReporter/Retired/MobileSafari-2024-10-20-092151.ips': success"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>As the filename of the crashdump (<code class="language-plaintext highlighter-rouge">MobileSafari-2024-10-20-092151.ips</code> 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 <code class="language-plaintext highlighter-rouge">com.apple.crashreportcopymobile</code> Lockdown Service/ with pyMobileDevice’s <code class="language-plaintext highlighter-rouge">CrashReportsManager</code> class:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pymobiledevice3.services.crash_reports</span> <span class="kn">import</span> <span class="n">CrashReportsManager</span>

<span class="c1"># lockdown = create_using_tcp(...)
</span><span class="n">crash_service</span> <span class="o">=</span> <span class="n">CrashReportsManager</span><span class="p">(</span><span class="n">lockdown</span><span class="p">)</span>
<span class="n">crash_service</span><span class="p">.</span><span class="n">pull</span><span class="p">(</span><span class="s">"output.ips"</span><span class="p">,</span> <span class="s">"/Retired/MobileSafari-2024-10-20-092151.ips"</span><span class="p">)</span>
</code></pre></div></div>

<p>To crack the creation or access of other files, on iOS the <code class="language-plaintext highlighter-rouge">fseventsd</code> daemon keeps track of filesystem changes, storing the logs on the device in the <code class="language-plaintext highlighter-rouge">/private/var/db/fseventsd</code> directory. These logs can collected from Jailbroken devices as the root user, which can then be parsed by tools such as <a href="https://github.com/dlcowen/FSEventsParser">this one from David Cowen</a>. I believe collecting these logs from a non-jailbroken device can only be done as part of the sysdiagnose snapshot.</p>

<p>Whenever <code class="language-plaintext highlighter-rouge">fseventsd</code> 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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">65285</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T14:49:36.798972"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ERROR"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/fseventsd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/fseventsd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"scan_old: bailing out because device mounted @ &lt;private&gt; has dls 0x0 and dls-&gt;fci 0x0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"daemon"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.fsevents"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h1 id="messaging">Messaging</h1>
<p>The next part of my research focussed on more specific signs of initial exploitation or malicious data collection.</p>

<p>Based on the Research into iOS malware such as Pegasus and TriangleDB, a common way to exploit an iOS device appears to be <a href="https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html">sending a malicious message to the device</a> 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.</p>
<h2 id="imessage">iMessage</h2>
<p>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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:23:56.047715"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ERROR"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/Extensions/Sandbox.kext/Sandbox"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/kernel"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"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"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>On iOS18, without Profile I observed an event that contained the sender info (<code class="language-plaintext highlighter-rouge">+61411111111</code> in this example) :</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">78921</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T23:03:32.005659"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/IMCore"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Applications/MobileSMS.app/MobileSMS"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"--&gt; Chat iMessage;-;+61411111111 has 0 remaining holds: &lt;private&gt;"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IMChat"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>When sending an attachment, this event was also created, but no other information:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">220</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T23:03:31.900702"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/IMCore"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/SOS.framework/sosd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Posting transfer, guid: &lt;private&gt;"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IMFileTransferCenter"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Adding the <code class="language-plaintext highlighter-rouge">FaceTime and Call Activity Logging</code> 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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">69</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:48:37.770384"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/Messages/PlugIns/iMessage.imservice/iMessage"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/imagent.app/imagent"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"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: '(</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">BFF68274-7B55-46E6-AC74-3A0B70F88504</span><span class="se">\"\n</span><span class="s2">)' 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"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MessageService"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>While this event doesn’t contain much information about the attachment, if you lookup the ‘transfer guid’ in other events (<code class="language-plaintext highlighter-rouge">BFF68274-7B55-46E6-AC74-3A0B70F88504</code> in this example), you would find information on other events about both the filename and the iCloud URL used to facilitate the file transfer:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">272</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:48:37.831417"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/IMCore"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/SOS.framework/sosd"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"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"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IMFileTransferCenter"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">69</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:48:37.834690"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/Messages/PlugIns/iMessage.imservice/iMessage"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/imagent.app/imagent"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"    user info: {</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">decryption-key</span><span class="se">\"</span><span class="s2"> = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">file-size</span><span class="se">\"</span><span class="s2"> = 2427577;</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">mime-type</span><span class="se">\"</span><span class="s2"> = </span><span class="se">\"</span><span class="s2">application/pdf</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">mmcs-owner</span><span class="se">\"</span><span class="s2"> = </span><span class="se">\"</span><span class="s2">CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDD.EEEEEEEE</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">mmcs-signature-hex</span><span class="se">\"</span><span class="s2"> = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB;</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">mmcs-url</span><span class="se">\"</span><span class="s2"> = </span><span class="se">\"</span><span class="s2">https://p40-content.icloud.com/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDD.EEEEEEEE</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">    name = </span><span class="se">\"</span><span class="s2">apple-platform-security-guide.pdf</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">uti-type</span><span class="se">\"</span><span class="s2"> = </span><span class="se">\"</span><span class="s2">com.adobe.pdf</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">}"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Attachments"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">69</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:48:38.076867"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMDaemonCore.framework/IMDaemonCore"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/imagent.app/imagent"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Local path: (null), filename: apple-platform-security-guide.pdf"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Attachments"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The initial event does make mention as occurring after running the file is run through <a href="https://support.apple.com/en-au/guide/security/secd3c881cee/web">Blastdoor</a>, 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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">61</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T13:23:08.237016"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IDS.framework/identityservicesd.app/identityservicesd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IDS.framework/identityservicesd.app/identityservicesd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IDSBlastDoor: Incoming message &lt;private&gt; has constraint type 0 and BlastDoor context &lt;private&gt;"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IDSDaemon"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.IDS"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>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.</p>

<p>Finally, with the Configuration Profile, the message content was sometimes available in the following message under <code class="language-plaintext highlighter-rouge">CKBBContentKeyAttachmentSummary</code> (I sent the message “AAAA” in this example):</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">185</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:48:38.783849"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMDPersistence.framework/IMDPersistence"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMDPersistence.framework/XPCServices/IMDPersistenceAgent.xpc/IMDPersistenceAgent"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Set userInfo for dictionaries {</span><span class="se">\n</span><span class="s2">    CKBBContentKeyAttachmentCount = 1;</span><span class="se">\n</span><span class="s2">    CKBBContentKeyAttachmentSummary = AAAA;</span><span class="se">\n</span><span class="s2">    CKBBContentKeyCountByAttachmentType =     {</span><span class="se">\n</span><span class="s2">        </span><span class="se">\"</span><span class="s2">%lu Files</span><span class="se">\"</span><span class="s2"> = 1;</span><span class="se">\n</span><span class="s2">    };</span><span class="se">\n</span><span class="s2">    CKBBContextKeyChatGUIDs =     (</span><span class="se">\n</span><span class="s2">        </span><span class="se">\"</span><span class="s2">iMessage;-;+6142222222</span><span class="se">\"\n</span><span class="s2">    );</span><span class="se">\n</span><span class="s2">    CKBBContextKeyMessageGUID = </span><span class="se">\"</span><span class="s2">3DFC4B49-2D9A-4C45-8D1C-8624D4339D3F</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">    CKBBContextKeySenderName = Host;</span><span class="se">\n</span><span class="s2">    CKBBContextKeySenderPersonCentricID = </span><span class="se">\"</span><span class="s2">9EFC13F1-34E0-44EA-A3E8-D63A574CA9B3:ABPerson</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">    CKBBUserInfoKeyChatIdentifier = </span><span class="se">\"</span><span class="s2">+614222222222</span><span class="se">\"</span><span class="s2">;</span><span class="se">\n</span><span class="s2">}"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IMDNotificationsController"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>And in this event (I sent “Apple pie”):</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">185</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T18:04:41.387819"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DEBUG"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMFoundation.framework/IMFoundation"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMDPersistence.framework/XPCServices/IMDPersistenceAgent.xpc/IMDPersistenceAgent"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">" =&gt; Out attributed string: Apple pie{</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">__kIMMessagePartAttributeName</span><span class="se">\"</span><span class="s2"> = 0;</span><span class="se">\n</span><span class="s2">}"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DataDetector"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.IDS"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="sms">SMS</h2>
<p>Without any Configuration Profile, on iOS18 I observed the following event that contained the sender’s phone number:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">88956</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-22T16:16:31.305446"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/IMCore.framework/IMCore"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Applications/MobileSMS.app/MobileSMS"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"--&gt; Chat SMS;-;+61411111111 has 0 remaining holds: &lt;private&gt;"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IMChat"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.Messages"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">32</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T17:32:24.931615"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/UserNotificationsServer.framework/UserNotificationsServer"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/CoreServices/SpringBoard.app/SpringBoard"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Successfully resolved request: resolvedRequest=&lt;UNNotificationRequest: 0x281955500; identifier: BE51960E-42EA-4F9F-987C-15280DDF9E96, content: &lt;UNNotificationContent: 0x4d8e3db10; title: &lt;redacted&gt;, subtitle: (null), body: &lt;redacted&gt;, summaryArgument: , summaryArgumentCount: 0, categoryIdentifier: MessageExtension-SMS, launchImageName: , threadIdentifier: +61411111111, attachments: (</span><span class="se">\n</span><span class="s2">    </span><span class="se">\"</span><span class="s2">&lt;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: &lt;UNNotificationAttachmentOptions: 0x2814fe920; displayLocation: default; thumbnailGeneratorUserInfo: {</span><span class="se">\\</span><span class="s2">n}&gt;&gt;</span><span class="se">\"\n</span><span class="s2">), badge: (null), sound: &lt;UNNotificationSound: 0x283ea68b0&gt;, realert: 1, interruptionLevel: 1, relevanceScore: 0.00, filterCriteria: (null), trigger: (null)&gt;, resolutionSuccess=YES, continueOnFailure=NO"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"label"</span><span class="p">:</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AttachmentsService"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.UserNotifications"</span><span class="p">,</span><span class="w">
      </span><span class="p">},</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h1 id="other-activity">Other Activity</h1>
<p>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 <a href="https://cyb3rops.medium.com/about-detection-engineering-44d39e0755f0">detection engineering for other Operating Systems</a> 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.</p>

<h2 id="enabling-ad-tracking">Enabling Ad Tracking</h2>
<p>Kaspersky observed that the TriangleDB malware would <a href="https://securelist.com/triangulation-validators-modules/110847/">enable personalized ad tracking</a>. While I am unsure of exactly how the malware achieved this, when I toggled the option in the Setting UI it created this event:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">128</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T13:31:35.345429"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/ManagedConfiguration.framework/ManagedConfiguration"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/adprivacyd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Set Bool value YES for settings: allowApplePersonalizedAdvertising"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ProfileConnection"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.ManagedConfiguration"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="location">Location</h2>
<p>Without a Profile, when I used an App that requested the device’s location I observed this event:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">70</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T13:59:24.527649"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/locationd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/locationd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"client '[mobile]com.apple.Maps' authorized for location; starting now, desiredAccuracy, -1.0, distanceFilter, -1.0, operatingMode 0, dynamicAccuracyReductionEnabled 0, allowsAlteredAccessoryLocations 1, activityType 0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Client"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.locationd.Core"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>With the <code class="language-plaintext highlighter-rouge">Location and Motion</code> 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.</p>

<h2 id="entitlements">Entitlements</h2>
<p>Without a profile I observed these events when a App was requesting certain Entitlements from the <a href="https://eclecticlight.co/2023/02/10/privacy-what-tcc-does-and-doesnt/">Transparency, Consent and Control (TCC) framework</a>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">65424</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T14:52:28.100478"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/TCC.framework/TCC"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Applications/MobileSlideShow.app/MobileSlideShow"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SEND: 0/7 synchronous to com.apple.tccd: request: msgID=65424.1, function=TCCAccessRequest, service=kTCCServicePhotos,"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"access"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.TCC"</span><span class="w"> </span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T14:49:49.519418"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/TCC.framework/Support/tccd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/TCC.framework/Support/tccd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"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'"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"access"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.TCC"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>In this example the photos app <code class="language-plaintext highlighter-rouge">MobileSlideshow</code> 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.</p>

<h2 id="keychain-access">KeyChain access</h2>
<p>When I used Safari to request my saved passwords from the <a href="https://support.apple.com/en-au/guide/security/secb0694df1a/web">iOS Keychain</a>, I observed this event:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">66574</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T16:46:49.079914"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/SafariCore.framework/SafariCore"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Applications/SafariViewService.app/SafariViewService"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-[WBSSavedAccountStore _savedAccounts]: Returning 2 saved accounts"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Keychain"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.SafariShared"</span><span class="w"> </span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>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.</p>

<h2 id="cookies">Cookies</h2>
<p>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 <a href="https://blog.google/threat-analysis-group/how-we-protect-users-0-day-attacks/">deploy a javascript cookie stealer</a>, that coupled with an exploit to the underlying WebKit engine was used to send sensitive cookies to an SVR-controlled website using ‘regular’ javascript.</p>

<p>Using the <code class="language-plaintext highlighter-rouge">CFNetwork</code> 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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">42891</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T13:52:24.335812"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/Frameworks/CFNetwork.framework/CFNetwork"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/CoreParsec.framework/parsecd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CFNetwork Diagnostics [3:758] 13:52:24.335 {</span><span class="se">\n</span><span class="s2">HTTPCookieStorage::copyCookiesForURL: &lt;CFHTTPCookieStorage 0xb7c843620 [0xb7c843630]&gt;</span><span class="se">\n</span><span class="s2">                         Request URL: https://api-glb-aapse2c.smoot.apple.com/&lt;redacted&gt;</span><span class="se">\n</span><span class="s2">                    MainDocument URL: null</span><span class="se">\n</span><span class="s2">} [3:758]"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Diagnostics"</span><span class="p">,</span><span class="w"> </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.CFNetwork"</span><span class="w"> </span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

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

<h2 id="xpc">XPC</h2>
<p><a href="https://developer.apple.com/documentation/xpc">XPC</a> is the main inter-process communication format on iOS. This format was explicitly used by <a href="https://googleprojectzero.blogspot.com/2022/03/forcedentry-sandbox-escape.html">NSO’s FORCEDENTRY exploit</a> 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:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">223</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-04T13:43:24.510463"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NOTICE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/PrivateFrameworks/SPShared.framework/SPShared"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/libexec/searchpartyd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"XPCSessionManager(BeaconManagerService): New XPC connection: &lt;NSXPCConnection: 0xae0a5a440&gt; connection from pid 42697 on mach service named com.apple.icloud.searchpartyd.pairingmanager"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xpcSessionManager"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.icloud.spshared"</span><span class="p">,</span><span class="w">
    </span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">68634</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-10-07T14:08:06.935270"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INFO"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/Frameworks/LocalAuthentication.framework/Support/coreauthd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"filename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/System/Library/Frameworks/LocalAuthentication.framework/Support/coreauthd"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MainServiceListener has accepted a new connection: &lt;NSXPCConnection: 0x710d047d0&gt; connection from pid 68598 on mach service named com.apple.CoreAuthentication.daemon hash:10d047d0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Server,LAContext"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"subsystem"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.apple.LocalAuthentication"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

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

<h1 id="conclusions-and-future-work">Conclusions and Future work</h1>
<p>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 <em>do something</em> about a detected threat or anomalous activity, beyond just a notification.</p>

<p>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.</p>

<p>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.</p>

<h1 id="epilogue---offensive-uses">Epilogue - Offensive Uses</h1>
<p>I want to touch on a few final thoughts on the topic of Offensive uses of this technique.</p>

<p>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.</p>

<p>The positive take, however, is that these other Operating Systems also allow <a href="https://www.crowdstrike.com/en-us/press-releases/2024-crowdstrike-threat-hunting-report-highlights-nation-states-exploits/">advanced security products to detect sophisticated attacks</a>, 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.</p>

<p>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 <a href="https://pages.nist.gov/mobile-threat-catalogue/stack-threats/STA-7.html">malicious Configuration Profiles</a>, 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.</p>

<p>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.</p>

<p>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.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Investigating realtime detections on iOS using Unified Logging]]></summary></entry><entry><title type="html">Gaining Threat-Intelligence the REALLY dodgy way</title><link href="https://blog.tofile.dev/2022/11/30/kdu_sealighter.html" rel="alternate" type="text/html" title="Gaining Threat-Intelligence the REALLY dodgy way" /><published>2022-11-30T12:00:00+00:00</published><updated>2022-11-30T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/11/30/kdu_sealighter</id><content type="html" xml:base="https://blog.tofile.dev/2022/11/30/kdu_sealighter.html"><![CDATA[<h2 id="tldr">tl;dr</h2>
<p>I forked and used <a href="https://github.com/hfiref0x/KDU">hfiref0x’s awesome Kernel Driver Utility project</a> to subscribe to the Microsft-Threat-Intelligence feed without using a signed binary or test machine.</p>

<h2 id="back-to-etw-ti">Back to ETW-TI</h2>
<p>Despite spending most of my time away from Windows these days, I was recently motivated to check back in with ETW, especially after the exploit used by <a href="https://blog.tofile.dev/2021/05/12/sealighterti.html">SealighterTI</a> was patched.</p>

<p>To refresh your memory on the systems and acronyms covered in this blog series so far (or <a href="#even-dodgier-method---vulnerable-drivers">skip to the new content</a>):</p>

<h3 id="event-tracing-for-windows-etw">Event Tracing for Windows (ETW)</h3>
<p>ETW is a system on Microsoft Windows that provides tracing and event logs for a variety of kernel- and user-mode systems, both from Microsoft and other developers. Grouped into ‘Providers’, these events can provide a wealth of information for anyone looking to understand what is happening on a system, from Security products looking to detect malicious activity to bug hunters looking for vulnerabilities.</p>

<h3 id="krabsetw-and-sealighter">KrabsETW and Sealighter</h3>
<p><a href="https://github.com/microsoft/krabsetw">KrabsETW</a> is a C++ and .NET library that makes it very easy to subscribe to and receive ETW events.</p>

<p><a href="https://github.com/pathtofile/Sealighter">Sealighter</a> is a tool I built on top of Krabs to provide a basic commandline interface into ETW, making it easy to explore ETW events and data.</p>

<h3 id="protected-processes-ppl-and-early-launch-anti-malware">Protected Processes, PPL, and Early Launch Anti-Malware</h3>
<p>Starting with Windows 8, Microsoft started developing new ways to provide extra protection to critical and sensitive information, such as user credentials or Anti-Malware processes.</p>

<p>One of these mechanisms is called <a href="https://www.crowdstrike.com/blog/protected-processes-part-3-windows-pki-internals-signing-levels-scenarios-signers-root-keys/">Protected Proceses</a>, and its sibling ‘Protected Process - Light’, or PPL. These are user-mode programs that have extra protections and security to prevent tampering but are also allowed access to data that typical programs cannot.</p>

<p>They require (among other things) that the program binary and libraries are checked and signed by Microsoft, which will then mark the binaries with a special entitlement indicating they can be run as a protected process.</p>

<p>One of these entitlements is called ‘Early Launch Anti-Malware’ or ELAM. Designed for security products, it allows the programs to both protect themselves from attackers, and gain special access to the system, such as the ability to scan Kernel activity early in the machine’s boot process. ELAM user-mode programs have to be loaded alongside a similarly checked-and-signed Kernel Driver, and there is a vetting process in place before a person or company can create ELAM programs.</p>

<p>Protected processes with the ELAM entitlement are sometimes called ‘PPL-AntiMalware’.</p>

<h3 id="microsft-security-threat-intelligence">Microsft-Security-Threat-Intelligence</h3>
<p><code class="language-plaintext highlighter-rouge">Microsft-Security-Threat-Intelligence</code> is the name of a special ETW Provider, that can only be read by PPL-AntiMalware processes.</p>

<p>It contains lots of information that is very interesting to security software, such as suspicious memory allocations that could be evidence of process injection or Kernel exploitation.</p>

<h2 id="previous-attempts">Previous attempts</h2>

<h3 id="test-signing-mode">Test-Signing Mode</h3>
<p>As mentioned above, to run a PPL process and subscribe to <code class="language-plaintext highlighter-rouge">Microsft-Security-Threat-Intelligence</code> the legitimate way on a normal Windows machine, you need a driver checked and signed by Microsoft with the ELAM entitlement.</p>

<p>If you put the Machine into ‘Test Signing’ or ‘Debug’ mode, while the program is still required to be signed and accompanied by an ELAM driver, it does not need to be signed by Microsoft.</p>

<p>I covered the steps to collect data from the <code class="language-plaintext highlighter-rouge">Threat-Intelligence</code> Provider on debug machine in my blog <a href="https://blog.tofile.dev/2020/12/16/elam.html">Experimenting with Protected Processes and Threat-Intelligence
</a>.</p>

<h3 id="exploitation">Exploitation</h3>
<p>Sometimes an exploit is discovered allowing you to inject code into an existing PPL process.</p>

<p>Although strong security mechanisms typically prevent this, back in 2018 <a href="https://twitter.com/aionescu">Alex Ionescu</a> and <a href="https://twitter.com/tiraniddo">James Forshaw</a> presented a <a href="http://publications.alex-ionescu.com/Recon/Recon%202018%20-%20Unknown%20Known%20DLLs%20and%20other%20code%20integrity%20trust%20violations.pdf">series of talks</a>, and <a href="https://googleprojectzero.blogspot.com/2018/08/windows-exploitation-tricks-exploiting.html">blogs</a>, covering many ways you could trick Windows into illegitimately running arbitrary code at the PPL level.</p>

<p>One of their techniques was worked up by <a href="https://twitter.com/itm4n">Clément Labro</a> into <a href="https://github.com/itm4n/PPLdump">PPLDump</a>, which would trick certain PPL Programs into loading an unsigned driver.</p>

<p>In my blog <a href="https://blog.tofile.dev/2021/05/12/sealighterti.html">Gaining Threat-Intelligence the dodgy way</a>, I built off Labro’s code to create <a href="https://github.com/pathtofile/SealighterTI">SealighterTI</a>, which uses the same exploit to subscribe to the <code class="language-plaintext highlighter-rouge">Threat-Intelligence</code> feed and send the results to the Event Log to be read and analysed.</p>

<p>Earlier this year, <a href="https://itm4n.github.io/the-end-of-ppldump/">Microsoft patched the exploit</a> used by PPLDump and SealighterTI, so these no longer work on up-to-date systems.</p>

<h2 id="even-dodgier-method---vulnerable-drivers">Even Dodgier Method - Vulnerable Drivers</h2>
<p>Recently there has been growing public interest and research into utilising old drivers with known vulnerabilities for attack purposes.</p>

<p>As these old drivers are legitimetly signed by Microsoft, they can be used to exploit the system and gain Kernel-level privileges on up-to-date systems, and can circumvent a lot of Windows security mechanisms such as Secure Boot.</p>

<p>Microsoft can revoke the certificate of the drivers to prevent them from loading, and both Microsoft and 3rd-party security vendors provide ways to block these drivers based on file hash or certificate, but this is often a difficult cat-and-mouse game, as driver developers might not be forthcoming with details about exactly what versions of their driver are vulnerable, and what file hashes and signatures to block. And there is a lot of drivers out there to monitor and write signatures for.</p>

<p>For defensive purposes, the best protection is to know what drivers are normally installed on a system (to spot anomalies), and to closely monitor who, when, and how new drivers are installed, as installing new drivers does require Administrator privileges and should not be a common administrator task.</p>

<p>But for research purposes, vulnerable drivers provide an excellent way to explore Windows from one of the highest privilege levels, without altering system behaviour by putting it into a non-production debug state.</p>

<h2 id="enter-the-kernel-driver-utility">Enter the Kernel Driver Utility</h2>
<p><a href="https://github.com/hfiref0x/KDU">The Kernel Driver Utility (KDU) from hfiref0x</a> is an excellent project to explore the possibilities of vulnerable drivers.</p>

<p>Hfiref0x has collected a range of known vulnerable drivers (30 at the time of this blog), and abstracted exploiting each driver into a common interface to be able to read and write arbitrary kernel memory from user-land.</p>

<p>It also comes with several shellcode-based Kernel Driver loaders, making it trivial to write your own unsigned driver (perhaps first testing it normally on a debug system), and deploy it to a fully patched and protected system.</p>

<h2 id="kdu-and-sealighter">KDU and Sealighter</h2>
<p>For interacting with PPL processes, KDU originally could only strip protections from processes, a not-uncommon tactic to be able to dump PPL memory or kill the process.</p>

<p>For Sealighter and The <code class="language-plaintext highlighter-rouge">Threat-Intelligence</code> Provider, I <a href="https://github.com/hfiref0x/KDU/pull/44">added a new feature to KDU</a> to enable it to launch new processes as PPL-AntiMalware.</p>

<p>To achieve this, KDU first creates the process suspended usinh <a href="https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags">CREATE_SUSPENDED</a>, so that while the process object in the kernel is created, the program hasn’t started to run. Then KDU uses a vulnerable driver to edit kernel memory, altering <a href="https://github.com/hfiref0x/KDU/blob/master/Source/Hamakaze/ps.cpp#L176">the process object in memory</a> to change its <a href="https://itm4n.github.io/lsass-runasppl/#protection-levels">protection type and signer</a> to be <code class="language-plaintext highlighter-rouge">PsProtectedTypeProtectedLight</code> and <code class="language-plaintext highlighter-rouge">PsProtectedSignerAntimalware</code>, i.e. PPL-AntiMalware.</p>

<p>With this new feature, it was easy to then use KDU to run Sealighter and get <code class="language-plaintext highlighter-rouge">Threat-Intelligence</code> events. I simply created this Sealighter config:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">

  </span><span class="nl">"session_properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"session_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sealighter-Trace"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"output_format"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stdout"</span><span class="w">
  </span><span class="p">},</span><span class="w">

  </span><span class="nl">"user_traces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"trace_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"TI-Trace"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"provider_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Microsoft-Windows-Threat-Intelligence"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Then I built and ran KDU like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kdu.exe -pse "C:\path\to\Sealighter.exe C:\path\to\config.json"
</code></pre></div></div>

<p>And just like that, we get <code class="language-plaintext highlighter-rouge">Threat-Intelligence</code> events from a fully patched, non-debug Windows environment:</p>

<p><img src="/assets/kdu_sealighter.png" alt="Sealighter being launched by KDU and printing out Threat-Intelligence PROTECTVM events" /></p>

<p>As you can see from the picture, the machine is running the latest Windows 22H2, Secure Boot is enables, and Driver signing using <a href="https://en.wikipedia.org/wiki/WHQL_Testing">WHQL</a> is enforced. Despite all that, KDU was able to load an old version of the legitimate MSI Afterburner RTCore driver, which is vulnerable to the exploit <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16098">CVE-2019-16098</a> . It used this driver and the exploit to edit the <code class="language-plaintext highlighter-rouge">EPROCESS-&gt;PS_PROTECTION</code> object in the kernel’s memory to set the protection level of the process to be PPL-AntiMalware.</p>

<p>Once KDU does it’s job, the Sealighter Process is un-suspended, and it can now subscribe to and log events from <code class="language-plaintext highlighter-rouge">Microsoft-Windows-Threat-Intelligence</code>.</p>

<h1 id="conclusion">Conclusion</h1>
<p>This blog doesn’t cover anything particularly new but does demonstrate yet another way to explore PPL Processes and the <code class="language-plaintext highlighter-rouge">Threat-Intelligence</code> ETW Provider. Vulnerable drivers will always be a security problem, but for both defensive and offensive research they also provide an excellent resource to learn more about Windows.</p>

<p>My additions to KDU are now merged into the main branch, so you can explore creating PPL Processes yourself by cloning the latest version <a href="https://github.com/hfiref0x/KDU">from GitHub</a></p>

<p>Thanks go to hfiref0x for KDU, to lean and explore some more, I recommend you check out:</p>
<ul>
  <li><a href="https://github.com/hfiref0x/KDU">KDU from hfiref0x</a></li>
  <li><a href="https://itm4n.github.io/lsass-runasppl">Itm4n’s PPL blog</a></li>
  <li><a href="https://github.com/pathtofile/Sealighter">Sealighter</a></li>
  <li><a href="https://blog.tofile.dev/categories/#etw">Every other link in my ETW/PPL blogs</a></li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[Gaining Threat-Intelligence the REALLY dodgy way]]></summary></entry><entry><title type="html">Linux cloud memory forensics tutorial</title><link href="https://blog.tofile.dev/2022/08/22/cloud-forensics.html" rel="alternate" type="text/html" title="Linux cloud memory forensics tutorial" /><published>2022-08-22T12:00:00+00:00</published><updated>2022-08-22T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/08/22/cloud-forensics</id><content type="html" xml:base="https://blog.tofile.dev/2022/08/22/cloud-forensics.html"><![CDATA[<h2 id="tldr">tl;dr</h2>
<p>This is a quick and simple guide to getting and analysing volatile memory dumps from Linux VMs on Azure, AWS,
and other cloud providers.</p>

<ul>
  <li><a href="#tldr">tl;dr</a></li>
  <li><a href="#volatility-and-linux">Volatility and Linux</a></li>
  <li><a href="#a-tale-of-two-halves">A tale of two halves</a></li>
  <li><a href="#1-a-memory-capture">1. A Memory capture</a></li>
  <li><a href="#2-an-intermediate-symbol-file-isf">2. An Intermediate Symbol File (ISF)</a>
    <ul>
      <li><a href="#auto-generate-isf">Auto-Generate ISF</a></li>
      <li><a href="#manually-generating">Manually Generating</a>
        <ul>
          <li><a href="#ubuntu">Ubuntu:</a></li>
          <li><a href="#debian">Debian:</a></li>
          <li><a href="#centos8-fedora-amazon-linux">Centos8, Fedora, Amazon Linux:</a></li>
          <li><a href="#azure-cbl-mariner">Azure CBL-Mariner:</a></li>
          <li><a href="#generating-an-isf">Generating an ISF</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="#using-isf">Using ISF</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="volatility-and-linux">Volatility and Linux</h2>
<p><a href="https://github.com/volatilityfoundation/volatility3">Volatility</a> is the main open source tool to forensicically
analyse volatile memory captures. But using Volatility to analyse Linux images can be confusing, especially
as the process changed massivly from Volatility 2 to 3, so I thought I’d write up a quick guide explaining
how to go about getting and analysing memory captures of Linux Virtual Machines (VMs) when they are hosted on a cloud provider (Azure, AWS, etc.)</p>

<h2 id="a-tale-of-two-halves">A tale of two halves</h2>
<p>There are two components needed to analyse a Linux memory Image, A volatile memory capture and an Intermediate Symbol File, or ISF.</p>

<h2 id="1-a-memory-capture">1. A Memory capture</h2>
<p>The memory capture will be taken from the machine you wish to analyse.
The easiest way to do this is using <a href="https://github.com/microsoft/avml">AVML</a> from Microsoft. This script will download the latest version of avml, and write
a compressed memory capture to disk as <code class="language-plaintext highlighter-rouge">capture.lime.compressed</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://github.com/microsoft/avml/releases/latest/download/avml
<span class="c"># OR if wget is not installed:</span>
curl <span class="nt">-LO</span> https://github.com/microsoft/avml/releases/latest/download/avml

<span class="nb">chmod </span>ugo+x ./avml
<span class="nb">sudo</span> ./avml <span class="nt">--compress</span> capture.lime.compressed
</code></pre></div></div>

<p>I don’t do the analysis on the same machine, but instead transfer the image
to my workstation for further analysis. On some version of Linux and Volatility I
encounter an issue analysing compressed images, where the program hangs indefinetly. I work around this by using <code class="language-plaintext highlighter-rouge">avml-convert</code> to decompress the image on my workstation first:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://github.com/microsoft/avml/releases/latest/download/avml-convert
<span class="nb">chmod </span>u+x ./avml-convert
./avml-convert capture.lime.compressed capture.lime
</code></pre></div></div>

<h2 id="2-an-intermediate-symbol-file-isf">2. An Intermediate Symbol File (ISF)</h2>
<p>This is the part that has changed the most from Volatility 2, and is the part
that initially confuses most people (including me).</p>

<p>You need to generate a special JSON file that contains the symbols from a <em>matching</em> Linux kernel. This file is called an Intermediate Symbol File or ISF.
If they don’t match, Volatility won’t let you analyse the image, as it need to be able to precisly say where to look in the memory capture.
Even very minor version differences can move the location of kernel objects needed for the analysis, so it’s important to generate a matching ISF for each memory capture.</p>

<p>There are lists of pre-generated ISF files, such as <a href="https://isf-server.techanarchy.net/">KevTheHermits’s excelent site</a>. But as you kernel has to match exactly with the ISF file, it’s possible that you may need to generate your own.</p>

<h3 id="auto-generate-isf">Auto-Generate ISF</h3>
<p>Thankfully, KevTheHermit has also created a <a href="https://github.com/kevthehermit/volatility_symbols">super useful tool to generate new ISFs</a>. It can create ISF files for:</p>
<ul>
  <li>Ubuntu</li>
  <li>Debian</li>
  <li>Fedora</li>
  <li>AWS’ Amazon Linux 2</li>
  <li>Azure’s CBL-Mariner 2</li>
</ul>

<p>To use KevTheHermit’s tool, first run this command on the target image, to get the exact kernel version:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">uname</span> <span class="nt">-r</span>
<span class="c"># output should be something like:</span>
<span class="c"># "5.11.0-43-generic" or</span>
<span class="c"># "4.14.281-212.502.amzn2.x86_64"</span>
</code></pre></div></div>

<p>Then run the tool, selecting the Linux distribution and passing in the output from <code class="language-plaintext highlighter-rouge">uname</code>. e.g. to get a CBL-Mariner ISF:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python symbol_maker.py <span class="nt">--distro</span> cbl-mariner <span class="nt">--kernel</span> <span class="s2">"5.15.48.1-2.cm2"</span>
</code></pre></div></div>

<p>To generate an ISF for Debian or Ubuntu, use <code class="language-plaintext highlighter-rouge">--branch</code> to tell the tool what cloud service the image is on, either:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">linux</code> If a non-cloud image</li>
  <li><code class="language-plaintext highlighter-rouge">linux-aws</code> If on AWS</li>
  <li><code class="language-plaintext highlighter-rouge">linux-azure</code> If on Azure</li>
  <li><code class="language-plaintext highlighter-rouge">linux-gcp</code> If on Google Cloud Compute</li>
</ul>

<p>e.g. to generate an ISF for an Ubuntu machine running on AWS:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python symbol_maker.py <span class="nt">--distro</span> ubuntu <span class="nt">--branch</span> linux-aws <span class="nt">--kernel</span> <span class="s2">"5.13.0-1031-aws"</span>
</code></pre></div></div>

<p>If generating an Amazon Linux ISF, use <code class="language-plaintext highlighter-rouge">--branch 2</code> for Amazon Linux 2 (the only version supported).</p>

<p>The tool will take a while as it downloads about 1GB of data, before outputting
the ISF json file to disk in the  <code class="language-plaintext highlighter-rouge">symbol_files/&lt;distro&gt;/&lt;kernel&gt;</code> folder.</p>

<h3 id="manually-generating">Manually Generating</h3>
<p>Unfortunetly, sometimes even KevTheHermit’s tool won’t be able to get exactly the right kernel. Sometimes if the ‘banner’
(the string that identifies the exact kernel version) is <strong>extremely</strong> similar (e.g. only the build timestamp is different), you can
edit the Banner in the ISF JSON file to match your image. But typically, if KevTheHermit’s tool can’t get the same kernel,
it means you need to manually generate your own ISF.</p>

<p>The advantage of the cloud is that VMs should be running from a known and re-usable base image. I reccomend creating a new-but-matching VM to the one you caputred
memory from, so you don’t taint the machine with uneeded data.</p>

<p>In AWS, the ID of the image is the <code class="language-plaintext highlighter-rouge">AMI</code>, and you can list the AMI ID of a running VM using either the Web UI, or the aws commandline:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Look for "ImageId"</span>
aws ec2 describe-instances <span class="nt">--instance-ids</span> i-1234567890abcdef0
</code></pre></div></div>

<p>You can then use this exact AMI ID to create a new machine that should have a matching kernel.
Other cloud providers have different ways of getting the base Image ID.
You don’t need a particularly powerful VM (my tests were on a 1CPU 2GB RAM), but you will need ~15GB of disk space.</p>

<p>To generate an ISF, you need two files:</p>
<ul>
  <li>System.map</li>
  <li>A debug version of the current Linux kernel</li>
</ul>

<p>Most linux distributions should have the <code class="language-plaintext highlighter-rouge">System.Map</code> file under the <code class="language-plaintext highlighter-rouge">/boot/</code> directory with a name that matches the version of the kenrel, i.e.:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/boot/System.map-<span class="si">$(</span><span class="nb">uname</span> <span class="nt">-r</span><span class="si">)</span>
</code></pre></div></div>

<p>To download a debug kernel, the process is different depending on the Linux distribution. I’ve captured the most common ones below:</p>

<h4 id="ubuntu">Ubuntu:</h4>
<p>(Tested on AWS, Azure, and DigitalOcean)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">--yes</span> ubuntu-dbgsym-keyring
<span class="nb">sudo tee</span> /etc/apt/sources.list.d/debug.list <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
deb http://ddebs.ubuntu.com </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="sh"> main restricted universe multiverse
deb http://ddebs.ubuntu.com </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="sh">-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="sh">-proposed main restricted universe multiverse
</span><span class="no">EOF

</span><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">--yes</span> linux-image-<span class="si">$(</span><span class="nb">uname</span> <span class="nt">-r</span><span class="si">)</span><span class="nt">-dbgsym</span>
<span class="c"># Debug kernel is at: /usr/lib/debug/boot/vmlinux-$(uname -r)</span>
</code></pre></div></div>

<h4 id="debian">Debian:</h4>
<p>(Tested on AWS)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo tee</span> /etc/apt/sources.list.d/debug.list <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
deb http://deb.debian.org/debian-debug/ </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="sh">-debug main
deb http://deb.debian.org/debian-debug/ </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="sh">-proposed-updates-debug main
</span><span class="no">EOF

</span><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">--yes</span> linux-image-<span class="si">$(</span><span class="nb">uname</span> <span class="nt">-r</span><span class="si">)</span><span class="nt">-dbg</span>
<span class="c"># Debug kernel is at: /usr/lib/debug/boot/vmlinux-$(uname -r)</span>
</code></pre></div></div>

<h4 id="centos8-fedora-amazon-linux">Centos8, Fedora, Amazon Linux:</h4>
<p>(Tested on AWS, should work for other RHEL-like distros)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nt">--enablerepo</span><span class="o">=</span><span class="s1">'*-debuginfo'</span> <span class="nb">install </span>kernel-debuginfo-<span class="si">$(</span><span class="nb">uname</span> <span class="nt">-r</span><span class="si">)</span>
<span class="c"># Debug kernel is at: /usr/lib/debug/lib/modules/$(uname -r)/vmlinux</span>
</code></pre></div></div>

<h4 id="azure-cbl-mariner">Azure CBL-Mariner:</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nb">install </span>mariner-repos-debug
<span class="nb">sudo </span>yum <span class="nb">install </span>kernel-debuginfo-<span class="si">$(</span><span class="nb">uname</span> <span class="nt">-r</span><span class="si">)</span>
<span class="c"># Debug kernel is at: /usr/lib/debug/lib/modules/$(uname -r)/vmlinux</span>
</code></pre></div></div>

<h4 id="generating-an-isf">Generating an ISF</h4>
<p>Once I have a debug kernel, I transfer it from the VM to my workstation.
You can in theory do this next step on the VM, but it requires a machine with 4GB+ of
memory and a decent CPU, and I typically just run tiny VMs in the cloud and do bulky things on my workstation.</p>

<p>Build a copy of <a href="https://github.com/volatilityfoundation/dwarf2json">Dwarf2JSON</a>, a tool from the Volatility team. If you don’t already have Go installed, you
can grab a pre-built version by me <a href="https://github.com/pathtofile/dwarf2json/releases/latest">on GiHub here</a>. Then run dwarf2JSON to generate the ISF, pointing it at the debug kernel you downloaded from the VM:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://github.com/pathtofile/dwarf2json/releases/latest/download/dwarf2json-linux-amd64 <span class="nt">-O</span> dwarf2json
<span class="nb">chmod </span>u+x dwarf2json
./dwarf2json linux <span class="nt">--system-map</span> &lt;/path/to/System.map&gt; <span class="nt">--elf</span> &lt;/path/to/vmlinux_from_vm&gt; <span class="o">&gt;</span> dbgkernel_aws_ubuntu.json
</code></pre></div></div>

<h2 id="using-isf">Using ISF</h2>
<p>Once you’ve generated the ISF JSON file, Clone the Volatility3 repo and install the Python3 dependecies:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/volatilityfoundation/volatility3.git
<span class="nb">cd </span>volatility3
pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt
</code></pre></div></div>

<p>Next copy the ISF JSON file to <code class="language-plaintext highlighter-rouge">volatility3/volatility3/framework/symbols/linux/</code>. Run this command to check that Volatility
found your ISF file and was able to parse the banner text:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> python vol.py isfinfo.IsfInfo
Volatility 3 Framework 2.3.0
URI     Valid   Number of base_types    Number of types Number of symbols       Number of enums Windows info    Linux banner    Mac banner
file:///home/path/code/volatility/volatility3/volatility3/framework/symbols/linux/dbgkernel_aws_ubuntu.json     Unknown 19      11925   199625  2110    -       Linux version 5.15.0-1015-aws <span class="o">(</span>buildd@lcy02-amd64-063<span class="o">)</span> <span class="o">(</span>gcc <span class="o">(</span>Ubuntu 9.4.0-1ubuntu1~20.04.1<span class="o">)</span> 9.4.0, GNU ld <span class="o">(</span>GNU Binutils <span class="k">for </span>Ubuntu<span class="o">)</span> 2.34<span class="o">)</span> <span class="c">#19~20.04.1-Ubuntu SMP Wed Jun 22 19:07:51 UTC 2022 (Ubuntu 5.15.0-1015.19~20.04.1-aws 5.15.39)</span>
....
</code></pre></div></div>

<p>You can also check the banner in your memory capture by running:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> python vol.py <span class="nt">-f</span> &lt;/path/to/memory_capture.lime&gt; banners.Banners
Volatility 3 Framework 2.3.0
Progress:  100.00               Stacking attempts finished
Offset  Banner
0x5da8f38       Linux version 5.15.0-1015-aws <span class="o">(</span>buildd@lcy02-amd64-063<span class="o">)</span> <span class="o">(</span>gcc <span class="o">(</span>Ubuntu 9.4.0-1ubuntu1~20.04.1<span class="o">)</span> 9.4.0, GNU ld <span class="o">(</span>GNU Binutils <span class="k">for </span>Ubuntu<span class="o">)</span> 2.34<span class="o">)</span> <span class="c">#19~20.04.1-Ubuntu SMP Wed Jun 22 19:07:51 UTC 2022 (Ubuntu 5.15.0-1015.19~20.04.1-aws 5.15.39)</span>
...
</code></pre></div></div>

<p>If they match up, then you can start using Volatility to analyse the image. For example, to list the processes running at the
time of catprue:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> python vol.py <span class="nt">-f</span> &lt;/path/to/memory_capture.lime&gt; linux.pslist
Volatility 3 Framework 2.3.0
Progress:  100.00               Stacking attempts finished
OFFSET <span class="o">(</span>V<span class="o">)</span>      PID     TID     PPID    COMM

0x9ea501284b00  1       1       0       systemd
0x9ea501281900  2       2       0       kthreadd
0x9ea501280000  3       3       2       rcu_gp
...
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>
<p>Capturing and analysing memory from Linux machines does take a few steps, however hopefully this blog clears up some
of the confusion about Volatility 3 and Linux, and provides a useful guide to getting and analysing cloud images.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Linux cloud memory forensics tutorial]]></summary></entry><entry><title type="html">Tool - Use TouchID and the Secure Enclave from the commandline</title><link href="https://blog.tofile.dev/2022/08/21/toucli.html" rel="alternate" type="text/html" title="Tool - Use TouchID and the Secure Enclave from the commandline" /><published>2022-08-21T12:00:00+00:00</published><updated>2022-08-21T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/08/21/toucli</id><content type="html" xml:base="https://blog.tofile.dev/2022/08/21/toucli.html"><![CDATA[<p>I just released <a href="https://github.com/pathtofile/toucli">Toucli</a>, a simple tool to use TouchID and the Secure Enclave to encrypt data from the commandline.</p>

<p>See the details in the README for more information about how it works and how to use it.</p>

<p><img src="/assets/toucli.mov" alt="Toucli Demo video" /></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Tool - Use TouchID and the Secure Enclave from the commandline]]></summary></entry><entry><title type="html">Commandline Cloaking 2 - Tetragon and Nim</title><link href="https://blog.tofile.dev/2022/08/04/tetragon.html" rel="alternate" type="text/html" title="Commandline Cloaking 2 - Tetragon and Nim" /><published>2022-08-04T12:00:00+00:00</published><updated>2022-08-04T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/08/04/tetragon</id><content type="html" xml:base="https://blog.tofile.dev/2022/08/04/tetragon.html"><![CDATA[<h2 id="tldr">tl;dr</h2>
<p>I ported my <a href="https://github.com/pathtofile/commandline_cloaking">Commandline Cloaking</a> tests to Nim, and used it
to explore Tetragon, the latest eBPF security observability tool.</p>

<ul>
  <li><a href="#tldr">tl;dr</a></li>
  <li><a href="#tetragon">Tetragon</a></li>
  <li><a href="#nim">Nim</a></li>
  <li><a href="#lab-setup">Lab setup</a></li>
  <li><a href="#tests">Tests</a>
    <ul>
      <li><a href="#test-0---no-cloaking">Test #0 - No cloaking</a></li>
      <li><a href="#test-1---post-execution-cloaking">Test #1 - Post-execution cloaking</a></li>
      <li><a href="#tests-2-and-3---in-memory-loader-and-piping-input">Tests #2 and #3 - In-memory loader and Piping input</a></li>
      <li><a href="#test-4---ld_preload">Test #4 - LD_PRELOAD</a></li>
      <li><a href="#test-5---in-memory-patching">Test #5 - In-memory patching</a></li>
      <li><a href="#test-6---chroot-dorking">Test #6 - Chroot dorking</a></li>
    </ul>
  </li>
  <li><a href="#conclusions-and-final-thoughts">Conclusions and final thoughts</a>
    <ul>
      <li><a href="#writing-nim">Writing Nim</a></li>
      <li><a href="#using-tetragon">Using Tetragon</a></li>
    </ul>
  </li>
  <li><a href="#example-code">Example Code</a></li>
</ul>

<h2 id="tetragon">Tetragon</h2>
<p>Recently Isovalent, the company behind the eBPF-based network monitoring and security program Cilium released
<a href="https://isovalent.com/blog/post/2022-05-16-tetragon/">Tetragon</a>, an open source
“eBPF-based security observability and runtime enforcement platform”.</p>

<p>Isovalent has many very talented eBPF experts, so I thought it would be neat to compare Tetragon to the same
Commandline Cloaking techniques I tested <a href="https://blog.tofile.dev/2022/01/04/sysmonlinux.html">Sysmon for Linux</a> with,
another eBPF-based security tool.</p>

<p>Tetragon can do a lot more than just observe commandlines, but for this blog I will mostly stick to just commandline detections.</p>

<h2 id="nim">Nim</h2>
<p>As well as looking into Tetragon, I also checked out <a href="https://nim-lang.org/">Nim</a>, a statically typed programming language that has been growing in popularity
over the last few years. As a compiled system language, it appears to be striking a balance between
lower-level systems access and C-interoperability with higher-level conveniences like iterators and a rich standard library.</p>

<p>To me, it fits in a similar place as Go, without the large library ecosystem (due to its popularity Vs google-backed Go),
but also without some of the baggage that comes with Go, such as the super-thick static binaries.</p>

<p>As I don’t work as a low-level software engineer I’m not the right person to provide a full evaluation of Nim, but I
thought I’d share my thoughts on porting my <a href="https://github.com/pathtofile/commandline_cloaking">Commandline Cloaking</a>
toy programs from C/Go.</p>

<h2 id="lab-setup">Lab setup</h2>
<p>Install the Nim compiler was as simple as following <a href="https://nim-lang.org/install_unix.html">this guide</a>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://nim-lang.org/choosenim/init.sh <span class="nt">-sSf</span> | sh
</code></pre></div></div>

<p>Tetragon is mostly designed to be run as part of a Kubernetes cluster. I just wanted to run Tetragon by itself
(and didn’t have a Kubernetes cluster already setup for testing),
thankfully, <a href="https://twitter.com/tixxdz">Djalal Harouni</a> provided this method to run Tetragon as a standalone docker
container:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Run Tetragon in privliged Docker, mounting required folders</span>
docker run <span class="nt">--name</span> tetragon <span class="se">\</span>
<span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--pid</span><span class="o">=</span>host <span class="nt">--cgroupns</span><span class="o">=</span>host <span class="se">\</span>
<span class="nt">--privileged</span> <span class="nt">--detach</span> <span class="se">\</span>
<span class="nt">-v</span> /sys/kernel/btf/vmlinux:/var/lib/tetragon/btf <span class="se">\</span>
quay.io/cilium/tetragon:v0.8.0 <span class="se">\</span>
bash <span class="nt">-c</span> <span class="s2">"/usr/bin/tetragon"</span>

<span class="c">#2. Get events in pretty format:</span>
docker <span class="nb">exec</span> <span class="nt">-t</span> tetragon bash <span class="nt">-c</span> <span class="s2">"/usr/bin/tetra getevents -o compact"</span>
<span class="c">#   OR get full JSON of process ecex events:</span>
docker <span class="nb">exec</span> <span class="nt">-t</span> tetragon bash <span class="nt">-c</span> <span class="s2">"/usr/bin/tetra getevents"</span> | jq <span class="s1">'select(.process_exec)'</span>

<span class="c"># When finished:</span>
docker <span class="nb">rm</span> <span class="nt">-f</span> tetragon
</code></pre></div></div>

<h2 id="tests">Tests</h2>
<p>For more information on each of the test programs I created and ran, see my <a href="https://github.com/pathtofile/commandline_cloaking">previous blog</a>
where I go into more detail.</p>

<h3 id="test-0---no-cloaking">Test #0 - No cloaking</h3>
<p>This program doesn’t do any shenanigans, it just prints some basic commandline information
to the screen.</p>

<h4 id="nim-1">Nim</h4>
<p>This was understandably very straightforward to port to Nim:</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">os</span>
<span class="k">import</span> <span class="n">strformat</span>
<span class="k">import</span> <span class="n">posix</span>

<span class="k">proc </span><span class="nf">main</span><span class="p">()</span> <span class="o">=</span>
    <span class="n">echo</span><span class="p">(</span><span class="s">fmt"  PID     {getpid()}"</span><span class="p">)</span>
    <span class="n">echo</span><span class="p">(</span><span class="s">fmt"  PPID    {getppid()}"</span><span class="p">)</span>
    <span class="n">echo</span><span class="p">(</span><span class="s">fmt"  argc    {paramCount() + 1}"</span><span class="p">)</span>  <span class="c"># Nim doesn't count argv[0] in paramCount</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="mf">0</span><span class="p">..</span><span class="n">paramCount</span><span class="p">():</span>
        <span class="n">echo</span><span class="p">(</span><span class="s">fmt"  argv[{i}] {paramStr(i)}"</span><span class="p">)</span>

    <span class="n">echo</span><span class="p">(</span><span class="s">"  Sleeping for 60 seconds so you can lookup the PID"</span><span class="p">)</span>
    <span class="n">setControlCHook</span><span class="p">(</span><span class="k">proc</span><span class="p">()</span> <span class="p">{.</span><span class="n">noconv</span><span class="p">.}</span> <span class="o">=</span> <span class="k">discard</span><span class="p">)</span>
    <span class="n">sleep</span><span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span>

<span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<p>It is odd that the Nim built-in <code class="language-plaintext highlighter-rouge">paramCount()</code> isn’t exactly like C’s <code class="language-plaintext highlighter-rouge">argc</code> in that
it doesn’t count <code class="language-plaintext highlighter-rouge">argv[0]</code> (which is usually the program name), but accessing
arguments with <code class="language-plaintext highlighter-rouge">paramStr()</code> <em>does</em> include the 0th arg.</p>

<h4 id="tetragon-1">Tetragon</h4>
<p>Running <code class="language-plaintext highlighter-rouge">./basic_nim AAAA</code> produces a predictable output from Tetragon:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk1OTg2MDUwNTAyNzU3OjcwMDE="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">7001</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/basic_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AAAA"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T03:33:18.153Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T03:33:18.153Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Tetragon helpfully captures the full commandline, as well as
other useful information such as UserID, start time, parent process etc.
Tetragon maintains its own unique ID system, so you can link and track child
and parent processes, even in the event of PID reuse.</p>

<h3 id="test-1---post-execution-cloaking">Test #1 - Post-execution cloaking</h3>
<p>This test runs a few tricks to modify it’s commandline arguments
after it executes, to hide from tools such as <code class="language-plaintext highlighter-rouge">ps</code>. This includes:</p>
<ul>
  <li>Editing our <code class="language-plaintext highlighter-rouge">/proc/pid/cmdline</code> by editing our <code class="language-plaintext highlighter-rouge">argv</code> array</li>
  <li>Using <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl</a> and <code class="language-plaintext highlighter-rouge">PR_SET_NAME</code> to change the process name in <code class="language-plaintext highlighter-rouge">/proc/pid/comm</code></li>
  <li>Forking twice and killing parents, which sets the process’ Parent PID to PID 1</li>
</ul>

<h4 id="nim-2">Nim</h4>
<p>Writing this in Nim was also quite straightforward. To use syscalls such as <code class="language-plaintext highlighter-rouge">prctl</code>,
it was as simple as:</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Declare the c definitions in Nim </span>
<span class="k">proc </span><span class="nf">syscall</span><span class="p">(</span><span class="n">number</span><span class="p">:</span> <span class="n">clong</span><span class="p">):</span> <span class="n">clong</span> <span class="p">{.</span><span class="n">importc</span><span class="p">,</span> <span class="n">varargs</span><span class="p">,</span> <span class="n">header</span><span class="p">:</span> <span class="s">"sys/syscall.h"</span><span class="p">.}</span>
<span class="k">var</span> <span class="n">NR_PRCTL</span> <span class="p">{.</span><span class="n">importc</span><span class="p">:</span> <span class="s">"__NR_prctl"</span><span class="p">,</span> <span class="n">header</span><span class="p">:</span> <span class="s">"unistd.h"</span><span class="p">.}:</span> <span class="kt">int</span>
<span class="k">var</span> <span class="n">PR_SET_NAME</span> <span class="p">{.</span><span class="n">importc</span><span class="p">:</span> <span class="s">"PR_SET_NAME"</span><span class="p">,</span> <span class="n">header</span><span class="p">:</span> <span class="s">"sys/prctl.h"</span><span class="p">.}:</span> <span class="kt">int</span>

<span class="c"># Call syscall function to invoke prctl to set our /proc/self/comm</span>
<span class="k">var</span> <span class="n">err</span> <span class="o">=</span> <span class="n">syscall</span><span class="p">(</span><span class="n">NR_PRCTL</span><span class="p">,</span> <span class="n">PR_SET_NAME</span><span class="p">,</span> <span class="n">cstring</span><span class="p">(</span><span class="s">"faked"</span><span class="p">))</span>
</code></pre></div></div>

<p>This was the same for <code class="language-plaintext highlighter-rouge">memset</code>, which doesn’t have a Nim equivalent in the standard lib,
but was super easy to ‘import’ and call the C version.</p>

<p>To edit the raw <code class="language-plaintext highlighter-rouge">argv</code> array, I did have to ‘turn off’ some of Nim’s magic,
as the built-in argument array <code class="language-plaintext highlighter-rouge">paramStr()</code> isn’t the actual memory address of <code class="language-plaintext highlighter-rouge">/proc/self/cmdline</code>,
but the converted-to-Nim string version, and therefore changing that doesn’t actually
cloak the commandline from <code class="language-plaintext highlighter-rouge">ps</code>.</p>

<p>Working around this was a simple as compiling the program with the <code class="language-plaintext highlighter-rouge">--nomain</code> flag, then
implementing the Nim startup process ourselves:</p>

<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">proc </span><span class="nf">main</span><span class="p">(</span><span class="n">argc</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span> <span class="n">argv</span><span class="p">:</span> <span class="n">cstringArray</span><span class="p">,</span> <span class="n">envp</span><span class="p">:</span> <span class="n">cstringArray</span><span class="p">):</span> <span class="kt">int</span> <span class="p">{.</span><span class="n">cdecl</span><span class="p">,</span> <span class="n">exportc</span><span class="p">.}</span> <span class="o">=</span>
    <span class="c"># Need to call NimMain ourselves first to avoid explosions</span>
    <span class="n">NimMain</span><span class="p">()</span>

    <span class="c"># Do stuff with argv, etc. ...</span>
    <span class="k">discard</span> <span class="n">memset</span><span class="p">(</span><span class="n">argv</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">,</span> <span class="n">ord</span><span class="p">(</span><span class="sc">'F'</span><span class="p">),</span> <span class="n">csize_t</span><span class="p">(</span><span class="n">len</span><span class="p">(</span><span class="n">argv</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)))</span>
</code></pre></div></div>

<p>For a reason I’ll explain below, once this <code class="language-plaintext highlighter-rouge">dodgy_nim</code> program did
it’s double-fork-and-kill trick to make its parent PID 1, I then
made it call <code class="language-plaintext highlighter-rouge">execve</code> to run the <code class="language-plaintext highlighter-rouge">basic_nim</code> program.</p>

<h4 id="tetragon-2">Tetragon</h4>
<p>To test, I ran <code class="language-plaintext highlighter-rouge">dodgy_nim</code> like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> ./dodgy_nim AAAA
<span class="nt">--------</span> REAL <span class="nt">--------</span>
  PID     8883
  PPID    5873
  argc    2
  argv[0] ./dodgy_nim
  argv[1] AAAA
<span class="nt">----</span> FORK &amp; FAKE <span class="nt">-----</span>
  PID     8885
  PPID    1
  argc    2
  argv[0] FFFFFFFFFFF
  argv[1] BBBB
<span class="nt">----</span> EXECVE BASIC <span class="nt">----</span>
  PID     8885
  PPID    1
  argc    2
  argv[0] from_dodgy
  argv[1] CCCC
<span class="nt">----------------------</span>

<span class="c"># Check ps output</span>
<span class="nv">$&gt;</span> ps aux | <span class="nb">grep </span>7613
path        7613  0.0  0.0   3468   196 pts/1    S    14:02   0:00 FFFFFFFFFFF BBBB
</code></pre></div></div>

<p>Like Sysmon for Linux, Tetragon logs the process information before it runs, 
which means the original commandline that the process was started with.</p>

<p>But as you can see below, things do get a little confusion, as it marks the original
process as <code class="language-plaintext highlighter-rouge">exited</code>, and when <code class="language-plaintext highlighter-rouge">basic</code> starts it states its parent ID PID 1:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk4NDUxNDQ0MTgyMTYxOjg4ODM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">8883</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/dodgy_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AAAA"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:14:23.547Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"parent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">5873</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/bin/bash"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"procFS auid"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T03:15:36.983Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0NzcwMDAwMDAwOjU4NzI="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">4294967281</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:14:23.547Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exit"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk4NDUxNDQ0MTgyMTYxOjg4ODM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">8883</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/dodgy_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AAAA"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:14:23.547Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:14:23.548Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk4NDUyNDQ1NDI1MTg2Ojg4ODU="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">8885</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/basic_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CCCC"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:14:24.548Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjQzMDAwMDAwMDox"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"parent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjQzMDAwMDAwMDox"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/lib/systemd/systemd"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"auto noprompt splash"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"procFS auid rootcwd"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-15T00:53:32.533Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:14:24.548Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This is all <em>technically</em> correct from the kernel’s point of view,
but it does show that the double-fork technique is enough to break
the commandline chain for analysts. I hadn’t checked this with Sysmon for Linux
in my previous blog, so I went back and tried and it also failed to properly link <code class="language-plaintext highlighter-rouge">dodgy</code> to <code class="language-plaintext highlighter-rouge">basic</code>.</p>

<h3 id="tests-2-and-3---in-memory-loader-and-piping-input">Tests #2 and #3 - In-memory loader and Piping input</h3>
<p>This test uses <a href="https://man7.org/linux/man-pages/man2/memfd_create.2.html">memfd_create</a> to
create an in-memory file, writing an ELF program it gets from stdin, before running it with
<code class="language-plaintext highlighter-rouge">execve</code>.</p>

<h4 id="nim-3">Nim</h4>
<p>Again, thanks to being super easy to call libC functions, this was also straightforward in Nim:</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Define from C</span>
<span class="k">proc </span><span class="nf">memfd_create</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="n">cstring</span><span class="p">,</span> <span class="n">flags</span><span class="p">:</span> <span class="n">cuint</span><span class="p">):</span> <span class="n">cint</span> <span class="p">{.</span><span class="n">importc</span><span class="p">,</span> <span class="n">header</span><span class="p">:</span> <span class="s">"sys/mman.h"</span><span class="p">.}</span>
<span class="k">proc </span><span class="nf">execve</span><span class="p">(</span><span class="n">pathname</span><span class="p">:</span> <span class="n">cstring</span><span class="p">,</span> <span class="n">argv</span><span class="p">:</span> <span class="n">cstringArray</span><span class="p">,</span> <span class="n">envp</span><span class="p">:</span> <span class="n">cstringArray</span><span class="p">):</span> <span class="n">cint</span> <span class="p">{.</span><span class="n">importc</span><span class="p">:</span> <span class="s">"execve"</span><span class="p">,</span> <span class="n">header</span><span class="p">:</span> <span class="s">"stdlib.h"</span><span class="p">.}</span>

<span class="c"># Create in-memory file</span>
<span class="k">let</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">memfd_create</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>

<span class="c"># Read from stdin</span>
<span class="k">let</span> <span class="n">data</span> <span class="o">=</span> <span class="n">readAll</span><span class="p">(</span><span class="n">stdin</span><span class="p">)</span>

<span class="c"># Write data to in-memory file</span>
<span class="k">var</span> <span class="n">fwrite</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="n">fmWrite</span><span class="p">)</span>
<span class="n">fwrite</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">fwrite</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

<span class="c"># Create new argv</span>
<span class="k">var</span> <span class="n">args</span><span class="p">:</span> <span class="kt">seq</span><span class="o">[</span><span class="kt">string</span><span class="o">]</span>
<span class="n">args</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="s">"from_loader"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="mf">2</span><span class="p">..</span><span class="n">paramCount</span><span class="p">():</span>
    <span class="n">args</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">paramStr</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
<span class="k">let</span> <span class="n">argv</span> <span class="o">=</span> <span class="n">alloccstringArray</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>

<span class="c"># Exec program</span>
<span class="k">discard</span> <span class="n">execve</span><span class="p">(</span><span class="n">cstring</span><span class="p">(</span><span class="s">fmt"/proc/self/fd/{fd}"</span><span class="p">),</span> <span class="n">argv</span><span class="p">,</span> <span class="k">nil</span><span class="p">)</span>
</code></pre></div></div>

<h4 id="tetragon-3">Tetragon</h4>
<p>I ran the loader like this, to read and launch <code class="language-plaintext highlighter-rouge">basic_nim</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">cat</span> ./bin/basic_nim | ./bin/loader_nim -
</code></pre></div></div>

<p>Tetragon records both programs executing, and links them together using <code class="language-plaintext highlighter-rouge">exec_id</code>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk5OTE4Nzc1NzkwNDY1OjEwMzkw"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">10390</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/loader_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:38:50.879Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"parent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">5873</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/bin/bash"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"procFS auid"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T03:15:36.983Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0NzcwMDAwMDAwOjU4NzI="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">4294967293</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:38:50.879Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk5OTE4Nzc2Nzc1MjMzOjEwMzkw"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">10390</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/proc/self/fd/3"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:38:50.880Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk5OTE4Nzc1NzkwNDY1OjEwMzkw"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"parent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk5OTE4Nzc1NzkwNDY1OjEwMzkw"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">10390</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/loader_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:38:50.879Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:38:50.880Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>While Tetragon doesn’t show the 2nd process as being an in-memory file,
the binary path <code class="language-plaintext highlighter-rouge">/proc/self/fd/3</code> is probably the same level of suspiciousness.
We don’t know the in-memory file as the <code class="language-plaintext highlighter-rouge">basic</code> program, and while we do see the
<code class="language-plaintext highlighter-rouge">cat</code> executing, with in-memory file technique the file could have been read from
a veriety of sources that don’t leave a commandline, such as remote webserver or
hardcoded within the same binary.</p>

<h3 id="test-4---ld_preload">Test #4 - LD_PRELOAD</h3>
<p>One common technique used by malware to hide is to make use of
<a href="https://www.man7.org/linux/man-pages/man8/ld.so.8.html">LD_PRELOAD</a> and similar environment variables.</p>

<p>This test uses <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> to hook the libc function in between the programs initial execution
and it’s <code class="language-plaintext highlighter-rouge">main()</code> function, altering the input commandline arguments.</p>

<h4 id="nim-4">Nim</h4>
<p>Like in C, this was more straightforward than Go.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Declare function type matching hooked function signature</span>
<span class="k">type</span>
  <span class="n">LibCMain</span> <span class="o">=</span> <span class="k">proc</span> <span class="p">(</span>
    <span class="n">main</span><span class="p">:</span> <span class="n">pointer</span><span class="p">,</span> <span class="n">argc</span><span class="p">:</span> <span class="n">cint</span><span class="p">,</span> <span class="n">argv</span><span class="p">:</span> <span class="n">cstringArray</span><span class="p">,</span>
    <span class="n">init</span><span class="p">:</span> <span class="n">pointer</span><span class="p">,</span> <span class="n">fini</span><span class="p">:</span> <span class="n">pointer</span><span class="p">,</span> <span class="n">rtld_fini</span><span class="p">:</span> <span class="n">pointer</span>
    <span class="p">):</span> <span class="kt">int</span> <span class="p">{.</span><span class="n">cdecl</span><span class="p">}</span>

<span class="c"># This hooks __libc_start_main</span>
<span class="k">proc </span><span class="nf">hookedLibcMain</span><span class="p">(</span>
    <span class="n">main</span><span class="p">:</span> <span class="n">pointer</span><span class="p">,</span> <span class="n">argc</span><span class="p">:</span> <span class="n">cint</span><span class="p">,</span> <span class="n">argv</span><span class="p">:</span> <span class="n">cstringArray</span><span class="p">,</span>
    <span class="n">init</span><span class="p">:</span> <span class="n">pointer</span><span class="p">,</span> <span class="n">fini</span><span class="p">:</span> <span class="n">pointer</span><span class="p">,</span> <span class="n">rtld_fini</span><span class="p">:</span> <span class="n">pointer</span>
<span class="p">):</span> <span class="kt">int</span> <span class="p">{.</span><span class="n">cdecl</span><span class="p">,</span> <span class="n">exportc</span><span class="p">:</span><span class="s">"__libc_start_main"</span><span class="p">,</span> <span class="n">dynlib</span><span class="p">}</span> <span class="o">=</span>
    <span class="c"># Find original function</span>
    <span class="k">let</span> <span class="n">lib</span> <span class="o">=</span> <span class="n">loadLib</span><span class="p">(</span><span class="s">"libc.so.6"</span><span class="p">)</span>
    <span class="n">assert</span> <span class="n">lib</span> <span class="o">!=</span> <span class="k">nil</span><span class="p">,</span> <span class="s">"Error loading library"</span>
    <span class="k">let</span> <span class="n">origFunc</span> <span class="o">=</span> <span class="k">cast</span><span class="o">[</span><span class="n">LibCMain</span><span class="o">]</span><span class="p">(</span><span class="n">lib</span><span class="p">.</span><span class="n">symAddr</span><span class="p">(</span><span class="s">"__libc_start_main"</span><span class="p">))</span>
    <span class="n">assert</span> <span class="n">origFunc</span> <span class="o">!=</span> <span class="k">nil</span><span class="p">,</span> <span class="s">"Error loading function from library"</span>

    <span class="c"># Create new argv</span>
    <span class="k">let</span> <span class="n">newArgc</span> <span class="o">=</span> <span class="n">cint</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
    <span class="k">var</span> <span class="n">newArgv</span> <span class="o">=</span> <span class="n">alloccstringArray</span><span class="p">(</span><span class="o">[</span><span class="s">"from_preload"</span><span class="p">,</span> <span class="s">"BBBB"</span><span class="o">]</span><span class="p">)</span>
    
    <span class="c"># Call into real function</span>
    <span class="k">return</span> <span class="n">origFunc</span><span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">newArgc</span><span class="p">,</span> <span class="n">newArgv</span><span class="p">,</span> <span class="n">init</span><span class="p">,</span> <span class="n">fini</span><span class="p">,</span> <span class="n">rtld_fini</span><span class="p">)</span>
</code></pre></div></div>

<p>I had to use the <code class="language-plaintext highlighter-rouge">.exportc</code> declaration as Nim doesn’t allow
functions to start with an <code class="language-plaintext highlighter-rouge">_</code>. To use <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> on non-underscore
functions you simply have the name the function in Nim the same as the function to be hooked.</p>

<h4 id="tetragon-4">Tetragon</h4>
<p>I ran the program like this, hooking and replacing the commandline args to <code class="language-plaintext highlighter-rouge">basic</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nv">LD_PRELOAD</span><span class="o">=</span>./preload_nim.so ./basic_nim AAAA
  PID     11292
  PPID    5873
  argc    2
  argv[0] from_preload
  argv[1] BBBB
  Sleeping <span class="k">for </span>60 seconds, so you can look up the PID
</code></pre></div></div>

<p>Just like Sysmon, it records the arguments the program was launched with,
which is <em>technically</em> correct again, but not effectivley happened:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjEwMTAwMzk4Mzc5ODg1NToxMTI5Mg=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">11292</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/bin/basic_nim"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AAAA"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:56:56.087Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T04:56:56.087Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Unfortunately, Tetragon doesn’t log the environment variables a program was launched with,
but if it did so, you should be able to see the <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>, which is not commonly used in production
environments, and will be pointing to the malicious library.</p>

<h3 id="test-5---in-memory-patching">Test #5 - In-memory patching</h3>
<p>Using <a href="https://vxug.fakedoma.in/archive/VxHeaven/lib/vsc01.html">Silvio’s Parasite technique</a>, we
can patch a binary to edit its arguments without <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>. For more information, see
<a href="https://blog.tofile.dev/2022/01/04/sysmonlinux.html#test-5---in-memory-patching">this section in the previous blog</a>.</p>

<h4 id="nim-5">Nim</h4>
<p>I didn’t implement this in Nim due to time and motivational constraints.</p>

<h4 id="tetragon-5">Tetragon</h4>
<p>When patching and loading a binary in-memory, the output is similar to the LD_PRELOAD test.</p>

<p>A twist on the Parasite technique is that the program could write the patch binary back to disk (overwriting the original file),
run the on-disk binary, then replace it with the original binary once the program has run.</p>

<p>This would mean the binary is no longer run from in-memory.
Tetragon can <a href="https://github.com/cilium/tetragon#file-access">monitor file access</a> (and more) by supplying custom <code class="language-plaintext highlighter-rouge">TracingPolicies</code>,
so it could watch of any process writing to e.g. <code class="language-plaintext highlighter-rouge">/bin/</code> to detect the tampering. I ran Tetragon
with this policy to look for writes to files under <code class="language-plaintext highlighter-rouge">/bin/</code>:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">cilium.io/v1alpha1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TracingPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">sys-write-follow-fd-bin"</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">kprobes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">call</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fd_install"</span>
    <span class="na">syscall</span><span class="pi">:</span> <span class="no">false</span>
    <span class="na">args</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">0</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s">int</span>
    <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">1</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s2">"</span><span class="s">file"</span>
    <span class="na">selectors</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">matchArgs</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">1</span>
        <span class="na">operator</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Prefix"</span>
        <span class="na">values</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s2">"</span><span class="s">bin"</span>
      <span class="na">matchActions</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">action</span><span class="pi">:</span> <span class="s">FollowFD</span>
        <span class="na">argFd</span><span class="pi">:</span> <span class="m">0</span>
        <span class="na">argName</span><span class="pi">:</span> <span class="m">1</span>
  <span class="pi">-</span> <span class="na">call</span><span class="pi">:</span> <span class="s2">"</span><span class="s">__x64_sys_write"</span>
    <span class="na">syscall</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">args</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">0</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fd"</span>
    <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">1</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s2">"</span><span class="s">char_buf"</span>
      <span class="na">sizeArgIndex</span><span class="pi">:</span> <span class="m">3</span>
    <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">2</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s2">"</span><span class="s">size_t"</span>
</code></pre></div></div>

<p>To run Tetragon with a custom policy <code class="language-plaintext highlighter-rouge">policy_file.yml</code> in the current directory, run:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Start Tetragon with a custom policy file:</span>
docker run <span class="nt">--name</span> tetragon <span class="se">\</span>
<span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--pid</span><span class="o">=</span>host <span class="nt">--cgroupns</span><span class="o">=</span>host <span class="se">\</span>
<span class="nt">--privileged</span> <span class="se">\</span>
<span class="nt">-v</span> /sys/kernel/btf/vmlinux:/var/lib/tetragon/btf <span class="se">\</span>
<span class="nt">-v</span> <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>:/policies <span class="se">\</span>
quay.io/cilium/tetragon:v0.8.0 <span class="se">\</span>
bash <span class="nt">-c</span> <span class="s2">"/usr/bin/tetragon --config-file /policies/policy_file.yml"</span>

<span class="c"># 2. Get events in pretty format:</span>
docker <span class="nb">exec</span> <span class="nt">-t</span> tetragon bash <span class="nt">-c</span> <span class="s2">"/usr/bin/tetra getevents -o compact"</span>
<span class="c">#   OR get full JSON of process kprobe events:</span>
docker <span class="nb">exec</span> <span class="nt">-t</span> tetragon bash <span class="nt">-c</span> <span class="s2">"/usr/bin/tetra getevents"</span> | jq <span class="s1">'select(.process_kprobe)'</span>

<span class="c"># Cleanup when finished:</span>
docker <span class="nb">rm</span> <span class="nt">-f</span> tetragon
</code></pre></div></div>

<p>Which when I overwrote <code class="language-plaintext highlighter-rouge">/bin/bash</code> using Python I got this event:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_kprobe"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjEwNDg1OTU2ODY0NjQ2MDoxNTQzMQ=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">15431</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/path/code/commandline_cloaking/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/bin/python"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./overwrite_bash.py"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T06:01:11.671Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ojk0OTI0ODgwMDAwMDAwOjU4NzM="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"function_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"__x64_sys_write"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"file_arg"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/bin/bash"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"truncated_bytes_arg"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"bytes_arg"</span><span class="p">:</span><span class="w"> </span><span class="s2">"f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAgBIAAAAAAABAAAAAAAAAAAhqAgAAAAAAAAAAAEAAOAANAEAAIAAfAAYAAAAEAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAA2AIAAAAAAADYAgAAAAAAAAgAAAAAAAAAAwAAAAQAAAAYAwAAAAAAABgDAAAAAAAAGAMAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAAAAAAAAABAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMgKAAAAAAAAyAoAAAAAAAAAEAAAAAAAAAEAAAAFAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAA+Z0BAAAAAAD5nQEAAAAAAAAQAAAAAAAAAQAAAAQAAAAAsAEAAAAAAACwAQAAAAAAALABAAAAAABUVAAAAAAAAFRUAAAAAAAAABAAAAAAAAABAAAABgAAACgNAgAAAAAAKB0CAAAAAAAoHQIAAAAAADgDAAAAAAAAIBwBAAAAAAAAEAAAAAAAAAIAAAAGAAAAQA0CAAAAAABAHQIAAAAAAEAdAgAAAAAA8AEAAAAAAADwAQAAAAAAAAgAAAAAAAAABAAAAAQAAAA4AwAAAAAAADgDAAAAAAAAOAMAAAAAAAAwAAAAAAAAADAAAAAAAAAACAAAAAAAAAAEAAAABAAAAA=="</span><span class="p">,</span><span class="w">
          </span><span class="nl">"orig_size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"160264"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"size_arg"</span><span class="p">:</span><span class="w"> </span><span class="s2">"160264"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"KPROBE_ACTION_POST"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-06-16T06:01:11.677Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">bytes_arg</code> is indeed the first 520 bytes of the ELF I overrode bash with.</p>

<p>I found creating these custom TracingPolicies to require experienced knowledge of eBPF, Tetragon, and the Linux kernel.
But it is possible to detect this technique.
Sysmon for Linux also has a similar ability to track file writes as one of its default event types, although I didn’t test it out.</p>

<h3 id="test-6---chroot-dorking">Test #6 - Chroot dorking</h3>
<p>While reading <a href="https://twitter.com/lizrice">Liz Rice’s</a> Excellent <a href="https://www.oreilly.com/library/view/container-security/9781492056690/">book on container security</a>,
I started thinking about another commandline cloaking method using <a href="https://linux.die.net/man/2/chroot">chroot jails</a>.</p>

<p><code class="language-plaintext highlighter-rouge">chroot</code> is a system that allows you to change what a process sees as the ‘root’ (<code class="language-plaintext highlighter-rouge">/</code>) folder. This allows a level of isolation and abstraction
from the ‘real’ files on the host, and is a key concept in how containers work. For example, say a machine has this filesystem:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/
├── bin
│   ├── sh
│   ├── python
│   └── bash
├── home
│   └── user
│       └── fake_root
│           ├── bin
|           |    └── bash
|           └── fake_tmp
├── proc
└── tmp
</code></pre></div></div>

<p>If we run a program that calls <code class="language-plaintext highlighter-rouge">chroot</code> to set the root to <code class="language-plaintext highlighter-rouge">/home/user/fake_root</code>, it will see the filesystem like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/
├── bin
│   └── bash
└── fake_tmp
</code></pre></div></div>

<p>When the program attempts to run <code class="language-plaintext highlighter-rouge">/bin/bash</code>, it will actually launch <code class="language-plaintext highlighter-rouge">/home/user/fake_root/bin/bash</code>.
This is how containers work (well part of how they work, I can’t recommend Liz Rice’s book highly enough for a more in-depth explanation).</p>

<p>When you run <code class="language-plaintext highlighter-rouge">/bin/bash</code> inside a docker container, on the host machine, it is actually running something like <code class="language-plaintext highlighter-rouge">/var/lib/docker/overlay2/&lt;very_long_hash&gt;/merged/bin/bash</code>.</p>

<p>As a result, using <code class="language-plaintext highlighter-rouge">chroot</code> also has implications for Tetragon and commandline monitors.</p>

<p>To test how chroot can affect commandline monitors like Tetragon, I created this Nim program:</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">proc </span><span class="nf">main</span><span class="p">()</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">getuid</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">echo</span><span class="p">(</span><span class="s">"Need to run as root (technically just CAP_SYS_CHROOT but I'm lazy)"</span><span class="p">)</span>
        <span class="n">quit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

    <span class="c"># Create fake root and /bin folder with </span>
    <span class="n">createDir</span><span class="p">(</span><span class="s">"fake_root"</span><span class="p">)</span>
    <span class="n">createDir</span><span class="p">(</span><span class="s">"fake_root/bin"</span><span class="p">)</span>
    
    <span class="c"># Create a link from a dodgy program to fake_root/bin/bash</span>
    <span class="n">link</span><span class="p">(</span><span class="s">"/path/to/dodgy_nim"</span><span class="p">,</span> <span class="s">"fake_root/bin/bash"</span><span class="p">)</span>

    <span class="c"># Move into fake root and call chroot</span>
    <span class="n">setCurrentDir</span><span class="p">(</span><span class="s">"fake_root"</span><span class="p">)</span>
    <span class="n">chroot</span><span class="p">(</span><span class="s">"."</span><span class="p">)</span>

    <span class="c"># Run fake bin bash, which is now at /bin/bash</span>
    <span class="n">execve</span><span class="p">(</span><span class="s">"/bin/bash"</span><span class="p">,</span> <span class="k">nil</span><span class="p">,</span> <span class="k">nil</span><span class="p">)</span>
</code></pre></div></div>

<p>There are a few tricks to getting this program to work:</p>
<ul>
  <li>The program needs the <code class="language-plaintext highlighter-rouge">CAP_SYS_CHROOT</code> capability, which usually means running it at as root</li>
  <li>The faked <code class="language-plaintext highlighter-rouge">bash</code> program either needs to be statically compiled, or you have to also copy in all the libraries into the <code class="language-plaintext highlighter-rouge">fake_root</code></li>
</ul>

<p>But given those constraints, Tetragon’s logging records the activity like this:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_exec"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjI5Mzc2MTgxOTk3NTcxMzoyMTgzNg=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">21836</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/fake_root/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/bin/bash"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-07-05T03:13:04.511Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjI5Mzc2MTgxOTI1MDIwNzoyMTgzNg=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"parent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjI5Mzc2MTgxOTI1MDIwNzoyMTgzNg=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">21836</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/fake_root/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/chroot_parent"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-07-05T03:13:04.510Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjI5MzczOTUxNDI1NTM0MjoyMTc4Mw=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-07-05T03:13:04.511Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>According to Tetragon, <code class="language-plaintext highlighter-rouge">chroot_parent</code> launched <code class="language-plaintext highlighter-rouge">/bin/bash</code>, which sounds like a completely benign activity.
But this makes sense - Tetragon is designed to monitor containers as well as host applications, so recording the path as the ‘container’
sees it would help with observing and writing detections for containers.</p>

<p>Tetragon could also monitor and detect this type of suspicious <code class="language-plaintext highlighter-rouge">chroot</code> usage by hooking the <code class="language-plaintext highlighter-rouge">chmod</code> syscall.
After some reading, I created this very basic TracingPolicy:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">cilium.io/v1alpha1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TracingPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">sys-chroot"</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">kprobes</span><span class="pi">:</span>
  <span class="c1"># __x64_sys_chroot(const char *path)</span>
  <span class="pi">-</span> <span class="na">call</span><span class="pi">:</span> <span class="s2">"</span><span class="s">__x64_sys_chroot"</span>
    <span class="na">syscall</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">args</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">index</span><span class="pi">:</span> <span class="m">0</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s2">"</span><span class="s">string"</span>
</code></pre></div></div>

<p>Which produces this Tetragon event, highlighting the activity:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"process_kprobe"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"process"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjE4MTExMjk2ODkwMTE6NTkxMA=="</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">5910</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/chroot_parent"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"execve clone"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-08-02T03:40:48.950Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjMwNTI3MDAwMDAwMDoyNDQx"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"parent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjMwNTI3MDAwMDAwMDoyNDQx"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">2441</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"binary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/bin/bash"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"flags"</span><span class="p">:</span><span class="w"> </span><span class="s2">"procFS auid"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-08-02T03:15:43.090Z"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"auid"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"parent_exec_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OjMwNTI2MDAwMDAwMDoyNDQw"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refcnt"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"function_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"__x64_sys_chroot"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"string_arg"</span><span class="p">:</span><span class="w"> </span><span class="s2">"."</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"KPROBE_ACTION_POST"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-08-02T03:40:48.951Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Like the file monitor, this policy isn’t ‘on by default’ and requires someone to understand enough about Tetragon and the Linux kernel to write and share it.
In the real world, you would want to edit the policy to also filter out known-good binaries or chroot folders, to reduce the amount of false positives.</p>

<h2 id="conclusions-and-final-thoughts">Conclusions and final thoughts</h2>
<h3 id="writing-nim">Writing Nim</h3>
<p>I thoroughly enjoyed writing code in Nim, and really liked that it was ‘low enough’ to make it easy to do things like syscalls and memory operations,
while being ‘high enough’ to have nice things baked in like argument parsing, slices, and string manipulation.</p>

<p>I’m unsure how much I’ll keep writing in Nim as my main goal of programming nowadays is to share information, and that means sharing code in
a language more people use.</p>

<h3 id="using-tetragon">Using Tetragon</h3>
<p>Tetragon from a programmer’s perspective Tetragon is really cool, easy to run and attach Kprobes to observe all kinds of
activity. I could see how you could create some really crafty TracingPolicies to catch all sort of unusual behaviour.</p>

<p>Like Sysmon for Windows and other observability tools, it doesn’t come with its own detections, which means it’s on the users
and community to create and share new policies to detect and prevent suspicious behaviour. But 
writing new policies for Tetragon requires someone with close-to-expert knowledge and experience in Linux and Tetragon.</p>

<p>It reminded me a bit of AuditD rules, where there people who get the most out of AuditD are those whole
deeply understand the Linux kernel, or who are lucky enough that someone else’s rules work in their
environment without being flooded by false positives. But those without the luck or skills end up feeling
frustrated, or simply just don’t use it, losing out on what should be a great source of secutity data.</p>

<p>I hope the Tetragon team can create a community of detection writers, as I feel without that many people
won’t be able to get the most out of it.</p>

<p>A good addition to the work would be to be able to automatically translate <a href="https://github.com/SigmaHQ/sigma">Sigma rules</a>
to and from Tetragon TracePolicies. This would enable people to share detections in a common format beyond Tetragon,
and convert rules they may be using with other tools and systems into Tetragon-specific policies.</p>

<h2 id="example-code">Example Code</h2>
<p>I’ve updated my <a href="https://github.com/pathtofile/commandline_cloaking">Commandline Cloaking</a> repository with all the Nim examples,
the new Chroot example, as well as the Tetragon TracePolicies.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Commandline Cloaking 2 - Tetragon and Nim]]></summary></entry><entry><title type="html">Tool - Using AWS Lambdas to distribute WebRequests</title><link href="https://blog.tofile.dev/2022/07/21/aws-webrunner.html" rel="alternate" type="text/html" title="Tool - Using AWS Lambdas to distribute WebRequests" /><published>2022-07-21T12:00:00+00:00</published><updated>2022-07-21T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/07/21/aws-webrunner</id><content type="html" xml:base="https://blog.tofile.dev/2022/07/21/aws-webrunner.html"><![CDATA[<p>At times I have the need to spread out multiple web requets across many IPs or Geographic locations.</p>

<p>To do this I’ve been using Terraform, AWS, And Python templating to auto create and run Lambda Functions
to handle the requests for me, sending the data back to an AWS SQS Queue to read asynchornously.</p>

<p>I recently got around to cleaning up my code and making it public, which you can find here: <a href="https://github.com/pathtofile/aws-lambda-webrunner">https://github.com/pathtofile/aws-lambda-webrunner</a>.</p>

<p>This is far from the first system to do this, but perhaps the code may prove useful to others.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Tool - Using AWS Lambdas to distribute WebRequests]]></summary></entry><entry><title type="html">SIEMCraft - Security detection monitoring using Minecraft</title><link href="https://blog.tofile.dev/2022/06/10/siemcraft.html" rel="alternate" type="text/html" title="SIEMCraft - Security detection monitoring using Minecraft" /><published>2022-06-10T12:00:00+00:00</published><updated>2022-06-10T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/06/10/siemcraft</id><content type="html" xml:base="https://blog.tofile.dev/2022/06/10/siemcraft.html"><![CDATA[<h2 id="tldr">tl;dr</h2>
<p>I spent way too much time creating a Security Information and Event Monitoring experience (SIEM) in Minecraft <a href="https://github.com/pathtofile/SIEMCraft">code</a>, <a href="https://youtu.be/8Vyf8Y5wcRY">demo video</a>. The process was actually more complex and fun than I was expecting it to be, and could serve as a blueprint to understanding the detection lifecycle end-to-end.</p>

<p><img src="/assets/siemcraft_pig.png" alt="Picture of a pig in SIEMCRAFT with detection information above its head" /></p>

<ul>
  <li><a href="#tldr">tl;dr</a></li>
  <li><a href="#from-boredom-big-things-grow">From boredom, big things grow</a></li>
  <li><a href="#bursting-at-the-siem">Bursting at the SIEM</a>
    <ul>
      <li><a href="#requirement-1-must-have-the-ability-to-collect-raw-events">Requirement #1: Must have the ability to collect raw events</a></li>
      <li><a href="#requirement-2-must-generate-detections-based-on-raw-events">Requirement #2: Must generate detections based on raw events</a></li>
      <li><a href="#requirement-3-must-display-detections-to-player">Requirement #3: Must display detections to player</a></li>
      <li><a href="#requirement-4-must-allow-a-player-to-take-action">Requirement #4: Must allow a player to take action</a></li>
    </ul>
  </li>
  <li><a href="#1-collecting-raw-events">1. Collecting raw events</a></li>
  <li><a href="#2-generating-detections">2. Generating Detections</a></li>
  <li><a href="#3-send-detects-into-minecraft">3. Send detects into Minecraft</a></li>
  <li><a href="#4-allow-players-to-take-action">4. Allow players to take action</a>
    <ul>
      <li><a href="#playermessage">PlayerMessage</a></li>
    </ul>
  </li>
  <li><a href="#shaving-the-gametest">Shaving the GameTest</a></li>
  <li><a href="#finishing-siemcraft">Finishing SIEMCraft</a></li>
</ul>

<h2 id="from-boredom-big-things-grow">From boredom, big things grow</h2>
<p>At the start of the year, I found myself on PTO without the ability to travel to the place I had booked the PTO for.</p>

<p>With my kiddo in daycare, I had nothing of importance to do, so while idly browsing the internet I came across the awesome
<a href="https://github.com/erjadi/kubecraftadmin/">KubeCraftAdmin</a> by Eric Jadi, a project that allows you to manage Kubernetes clusters from
Minecraft. To be clear, Eric had put the correct amount of effort into KubeCraftAdmin, making it functional and portable enough
for the lighthearted project that it was.</p>

<p>But for me, with more time than sense, noticed a few ‘rough edges’ to the project that bugged me, namely that while
it could send data <em>from</em> Kubernetes into the Minecraft server, it couldn’t <em>receive</em> and data back from the Minecraft
player, at least not directly. Again for a project like Eric’s, this is fine, but I had nothing but time to devote to pedantic things.
And besides, it couldn’t be that hard, right? right….?</p>

<h2 id="bursting-at-the-siem">Bursting at the SIEM</h2>
<p>With the Kubernetes controller idea already being taken, I wanted to look for another project with the same basic idea of ‘controlling something unusual from Minecraft’. Back in 2014, I had created a controller in Minecraft for <a href="https://github.com/rapid7/metasploit-framework">Metasploit</a>, although the code has since been lost to time, and was written in LUA for a now very outdated Minecraft version.</p>

<p>So instead I turned from red to blue and decided to write
a Security Information and Event Monitoring system, aka a SIEM. But I wanted to write the project end-to-end, from raw events to detection, visualisation, and human interaction. So I came up
with the following requirements, putting on my (very ill-fitting) Business Analyst hat (TM) that I used to wear many years ago:</p>

<hr />

<h3 id="requirement-1-must-have-the-ability-to-collect-raw-events">Requirement #1: Must have the ability to collect raw events</h3>
<p>Business people tend to focus on CEOs, so I decided to focus solely on Windows, as the only CEO I know personally that uses Linux <a href="https://vxug.fakedoma.in/archive/VxHeaven/lib/vsc01.html">just rants about parasites all day</a>. I also chose to only focus on Process Creation events because I’m lazy. However, as a <em>stretch goal</em>, I wanted to be able to collect raw events from an entire domain of Machines, not just a single box.</p>

<h3 id="requirement-2-must-generate-detections-based-on-raw-events">Requirement #2: Must generate detections based on raw events</h3>
<p>Displaying a firehose of raw events isn’t very useful to anyone, so I wanted to be able to write detections based on the raw events, and only alert the User/Player if something suspicious is detected.</p>

<h3 id="requirement-3-must-display-detections-to-player">Requirement #3: Must display detections to player</h3>
<p>Pretty obvious, Player must be able to <em>see</em> the detection, be able to tell the difference between a high- and low-severity detection,
and be able to see all relevant process information to help them decide if it is a True or False Positive.</p>

<h3 id="requirement-4-must-allow-a-player-to-take-action">Requirement #4: Must allow a player to take action</h3>
<p>I thought it’d be <em>hilarious</em> to make it so that a player can kill the offending process from Minecraft if they detected it was a True Positive. Spoiler for the rest of the blog, this turned out to be <em>way</em> more complex than I expected it to be.</p>

<hr />

<h2 id="1-collecting-raw-events">1. Collecting raw events</h2>
<p>This turned out to be the easiest of steps. In the real world, the best free solution would be to use <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon">Sysmon for Windows</a>, which generates events about Process activity and writes these events to the Windows Event log.</p>

<p>But to truly create an end-to-end solution, I made my own event collector (or really used an event collector I already made) called <a href="https://github.com/pathtofile/Sealighter/">Sealighter</a>. Sealighter uses <a href="https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing">ETW</a> to collect raw information from Windows, so I used this basic Sealighter config to collect ‘Process Start’ events from the Kernel ETW Provider and send the events to the Windows Event Log as a JSON object:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"session_properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"session_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sealighter-Kernel-Process-Start"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"output_format"</span><span class="p">:</span><span class="w"> </span><span class="s2">"event_log"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"kernel_traces"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w">
    </span><span class="nl">"trace_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kernel_proc_start_trace"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"provider_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"process"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"any_of"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"opcode_is"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w">
  </span><span class="p">}]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>By using Event logs to store the raw events, we can also make use of another built-in feature of Windows, <a href="https://www.cyber.gov.au/acsc/view-all-content/publications/windows-event-logging-and-forwarding">Windows Event Forwarding</a>. This means that if we deploy Sealighter to every machine in
a Windows Domain, we can forward all the events to a single PC, where we can do the heavier and more complex detection parts of SIEMCraft from a single machine, without writing any extra code:</p>

<p><img src="/assets/siemcraft_wef.png" alt="Diagram showing Windows Events being forwarded to a central machine using WEF" /></p>

<p>Once WEF is set up, a SIEMCraft binary can make use of the <a href="https://docs.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtsubscribe">EvtSubscribe</a>
API to collect and process all the Sealighter events as they come in. I had planned to write SIEMCraft in Go and found
<a href="https://github.com/0xrawsec/golang-win32">this awesome library</a> from <a href="https://twitter.com/0xrawsec">RawSec</a> that made it easy
to collect and parse Windows Event logs, ready to generate detections from.</p>

<h2 id="2-generating-detections">2. Generating Detections</h2>
<p>The correct way to write and generate Open Source detections is to make use of the amazing <a href="https://github.com/SigmaHQ/sigma">Sigma project</a>, which led
by the incredibly dedicated <a href="https://twitter.com/cyb3rops">Florian Roth</a>.</p>

<p>Sigma defines a standard schema in which people can create and share detections across multiple operating systems and applications. SIEMCraft needed to be able to ingest the <a href="https://github.com/SigmaHQ/sigma/tree/master/rules/windows">100s of free Sigma detection rules</a>
(as well as any other rules people create) for its detection engine, rather than attempting to create my own proprietary format.</p>

<p>Thankfully <a href="https://twitter.com/bradleyjkemp">Bradley Kemp</a> had already created <a href="https://github.com/bradleyjkemp/sigma-go">a really neat open source Go library</a> that could run and check Sigma rules against arbitrary data. I had to do some data formatting to ingest a list of rules from the filesystem, convert the Data from Sealighter to Sigma-Go, and account for some <a href="https://github.com/bradleyjkemp/sigma-go/issues/9">differences of opinion on the Sigma schema</a>, but otherwise, Florian and Bradley also made this step super easy for me.</p>

<p>With these two pieces of the puzzle, I could write a basic SIGMA rule to detect the most well-known of attacker tools - <a href="https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/whoami">whoami.exe</a>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">title</span><span class="pi">:</span> <span class="s">Whoami Execution</span>
<span class="na">id</span><span class="pi">:</span> <span class="s">36de6a23-651e-485a-ba69-3966d66707af</span>
<span class="na">status</span><span class="pi">:</span> <span class="s">experimental</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">Whoami.exe runs</span>
<span class="na">references</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">https://blog.tofile.dev</span>
<span class="na">tags</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">attack.execution</span>
<span class="na">author</span><span class="pi">:</span> <span class="s">pathtofile</span>
<span class="na">date</span><span class="pi">:</span> <span class="s">2022/01/15</span>
<span class="na">logsource</span><span class="pi">:</span>
    <span class="na">category</span><span class="pi">:</span> <span class="s">process_creation</span>
    <span class="na">product</span><span class="pi">:</span> <span class="s">windows</span>
<span class="na">detection</span><span class="pi">:</span>
    <span class="na">selection</span><span class="pi">:</span>
        <span class="na">Image|endswith</span><span class="pi">:</span> <span class="s1">'</span><span class="s">\whoami.exe'</span>
    <span class="na">condition</span><span class="pi">:</span> <span class="s">selection</span>
<span class="na">falsepositives</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">unknown</span>
<span class="na">level</span><span class="pi">:</span> <span class="s">high</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Whoami</code> is used in almost every single intrusion I’ve observed, from eCrime to APT. <a href="https://attack.mitre.org/techniques/T1033/">Just look at the MiTRE page!</a>. And what <em>real</em> user doesn’t already know who they are, right?</p>

<p>So now we had raw Process events flowing from every domain PC into SIEMCraft, which uses SIGMA to look for the dastardly <code class="language-plaintext highlighter-rouge">whoami.exe</code> (as well as any other actual rules we might create). Once SIEMCraft detects the <code class="language-plaintext highlighter-rouge">whoami</code> intrusion, it was time to send that data into Minecraft.</p>

<h2 id="3-send-detects-into-minecraft">3. Send detects into Minecraft</h2>
<p>This step would have been easier if I had used the old, Java version of Minecraft, which allows Mods to do basically whatever they want.</p>

<p>But the multiplayer server of the modern, better version of Minecraft (“Bedrock edition”, send Minecraft opinions to <a href="https://twitter.com/maybe_sybr">@maybe-sybr</a>) isn’t designed to allow custom data into or out of a Minecraft server. Instead of ‘Mods’, Minecraft Bedrock has ‘Addons’, allowing you to do lots of cool things <em>inside</em> the world (new monsters, animations, etc.), while not allowing the Addons to read or write anything outside of Minecraft. This is great for security, but not great for SIEMCraft, which both needs to read Windows Events Logs <em>and</em> spawn creatures in Minecraft.</p>

<p>I could have written/used a custom server program, but after hanging out of the Minecraft hacker discords I learnt this would prevent other Mods from running at the same time as SIEMCraft, which wasn’t ideal.</p>

<p>But it turns out that in a very roundabout way, I could make use of a WebSocket designed for “Minecraft Educational Edition” to poke a hole in the security boundary. <a href="http://www.s-anand.net/blog/programming-minecraft-with-websockets/">Anand describes the process best</a>, but essentially Minecraft and a remote process can communicate over a WebSocket to send a receive several undocumented message types. This means we can run a SIEMCraft process ‘outside’ of Minecraft, to do the things Addons can’t do and use it to control an Addon inside of Minecraft.</p>

<p>The first step was to get the detection data into Minecraft, so I first made use of the ‘Command Request’ message type. This Message can be sent <em>to</em> a Minecraft server, allowing you to run any <code class="language-plaintext highlighter-rouge">/command</code> console from the player’s perspective.
For example, to spawn a pig named ‘snuffles’ right next to the Player, you send this JSON command:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
      </span><span class="nl">"header"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
        </span><span class="nl">"requestId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"36de6a23-651e-485a-ba69-3966d66707af"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"messagePurpose"</span><span class="p">:</span><span class="w"> </span><span class="s2">"commandRequest"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"messageType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"commandRequest"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"body"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
        </span><span class="nl">"commandLine"</span><span class="p">:</span><span class="w"> </span><span class="s2">"summon pig </span><span class="se">\"</span><span class="s2">snuffles</span><span class="se">\"</span><span class="s2"> ~ ~ ~"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"origin"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"player"</span><span class="w"> </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Using these messages (via a minecraft websocket library by <a href="https://twitter.com/SandertvNL">Sandertv</a>), I could Spawn an animal near the player with their ‘name’ containing all the important detection details:</p>

<p><img src="/assets/siemcraft_panda.png" alt="Minecraft Panda with its name containing a whoami detection" /></p>

<p>I decided to make different animals correspond to different detection severities, with “low” severity spawning cows and chickens, and “high”
spawning <a href="https://adversary.crowdstrike.com/en-US/adversary/carbon-spider/">spiders</a>, <a href="https://www.crowdstrike.com/blog/overwatch-exposes-aquatic-panda-in-possession-of-log-4-shell-exploit-tools/">pandas</a>, and <a href="https://www.crowdstrike.com/blog/how-crowdstrike-protects-against-recent-cozy-bear-phishing-campaign/?utm_campaign=blog&amp;utm_medium=soc&amp;utm_source=twtr&amp;utm_content=sprout">bears</a>.</p>

<p>I needed to create and use a new Addon to get this all to work,
to prevent the animal names/detection details from being hidden too quickly and to add bears to the game, as Minecraft only has polar bears.</p>

<p>But now, thanks to the Websocket, A player can be cruising around a Minecraft server doing Minecraft things, and if a detection comes in a chicken, spider, or bear will helpfully spawn nearby, alerting the user that a <code class="language-plaintext highlighter-rouge">whoami.exe</code> attack is afoot.</p>

<h2 id="4-allow-players-to-take-action">4. Allow players to take action</h2>
<p>The final piece of the puzzle was to allow the player to take action, and kill a process identified by a detection if they deem it suspicious enough. This would be accomplished by having the play kill the detection-named animal specifically with a diamond sword (any other type of death would be treated as the player ignoring the alert).</p>

<p>In theory, this should have been easy, as the WebSocket exposes a <code class="language-plaintext highlighter-rouge">MobKilled</code> event which is sent <em>from</em> Minecraft to the remote Process whenever
an animal or monster is killed. However, the message lacks any information to tell exactly <em>which</em> animal was killed, only the fact they
were killed by the player. This means that while SIEMCraft could tell the player wants to respond to a detection, if it has sent multiple detections to the player it can’t tell which one.</p>

<p>A Minecraft Addon <em>can</em> get more information from inside the game, however. Addons can run small JavaScript programs which have access to a wider array of message types they can subscribe to, including the <code class="language-plaintext highlighter-rouge">minecraft:entity_death</code> message. This message type does contain all the information we need about exactly what mob was killed by who and with what. But as we know Addons can’t send this data out of Minecraft, so after many days of hacking, I decided on a really dodgy workaround: the <code class="language-plaintext highlighter-rouge">PlayerMessage</code> event.</p>

<h3 id="playermessage">PlayerMessage</h3>
<p>This message is sent <em>from</em> the Minecraft WebSocket to the remote process and contains any text players (or other entities) send each other.
The messages contain a bunch of information about who was talking to who, where they were talking from,
and most importantly the exact text they typed. So I simply made the internal Javascript Addo ‘whisper’ the data to the player:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Listen for entity_death events</span>
<span class="kd">var</span> <span class="nx">system</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">registerSystem</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="nx">system</span><span class="p">.</span><span class="nx">initialize</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">listenForEvent</span><span class="p">(</span><span class="dl">"</span><span class="s2">minecraft:entity_death</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">eventData</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">onEntityDeath</span><span class="p">(</span><span class="nx">eventData</span><span class="p">));</span>
<span class="p">};</span>

<span class="nx">system</span><span class="p">.</span><span class="nx">onEntityDeath</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">eventData</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Get the name of the mob</span>
  <span class="kd">let</span> <span class="nx">killed_name</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getComponent</span><span class="p">(</span><span class="nx">eventData</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">entity</span><span class="p">,</span> <span class="dl">"</span><span class="s2">minecraft:nameable</span><span class="dl">"</span><span class="p">);</span>

  <span class="c1">// Get weapon in main hand, assume it was what was used to kill it</span>
  <span class="kd">let</span> <span class="nx">handContainer</span> <span class="o">=</span> <span class="nx">system</span><span class="p">.</span><span class="nx">getComponent</span><span class="p">(</span><span class="nx">eventData</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">killer</span><span class="p">,</span> <span class="dl">"</span><span class="s2">minecraft:hand_container</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">mainHandItem</span> <span class="o">=</span> <span class="nx">handContainer</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>

  <span class="c1">// Create JSON string to send</span>
  <span class="nx">less</span> <span class="nx">message</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">killed_name=</span><span class="dl">'</span><span class="o">+</span> <span class="nx">killed_name</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">, weapon=</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">mainHandItem</span><span class="p">.</span><span class="nx">item</span><span class="p">;</span>

  <span class="c1">// Send message by used the /say command</span>
  <span class="kd">let</span> <span class="nx">ExecuteEventData</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">createEventData</span><span class="p">(</span><span class="dl">"</span><span class="s2">minecraft:execute_command</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">ExecuteEventData</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">command</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/tell @a </span><span class="se">\"</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">message</span> <span class="o">+</span> <span class="dl">"</span><span class="se">\"</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">broadcastEvent</span><span class="p">(</span><span class="dl">"</span><span class="s2">minecraft:execute_command</span><span class="dl">"</span><span class="p">,</span> <span class="nx">ExecuteEventData</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

<p>And this results in the following message being sent to the SIEMCraft process over the Websocket:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"header"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"messagePurpose"</span><span class="p">:</span><span class="w"> </span><span class="s2">"event"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"requestId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"00000000-0000-0000-0000-000000000000"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"body"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"eventName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PlayerMessage"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"measurements"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="err">//</span><span class="w"> </span><span class="err">...</span><span class="w">
      </span><span class="nl">"Sender"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Script Engine"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"killed_name=snuffles, weapon=diamond_sword"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"MessageType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tell"</span><span class="p">,</span><span class="w">
      </span><span class="err">//</span><span class="w"> </span><span class="err">...</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>After some tweaking to do things like base64 and JSON encode the data to avoid parsing errors, I
now had the full loop of data going from <code class="language-plaintext highlighter-rouge">SIEMCraft -&gt; Minecraft -&gt; SIEMCraft</code>. I could then read the
weapon name to check it was a diamond sword, and check the mob name to extract the Process ID of the process
the player wished to kill. I could have implemented logic to kill remote processes across the domain,
but for now, I’m happy with just killing processes locally.</p>

<h2 id="shaving-the-gametest">Shaving the GameTest</h2>
<p>Everything was working fine, and I began writing up this blog and sharing screenshots with mates.</p>

<p>Unfortunetly one of them came across <a href="https://www.minecraft.net/en-us/creator/article/removing-the-additional-modding-capabilities-feature">this blog</a>
from the Minecraft developers, stating they were removing the Addons Javascript engine in … 1 month’s time.</p>

<p>This would completely destroy the addon’s ability to talk back to SIEMCraft to kill processes, so I had to find a
new solution. The replacement Javascript engine <code class="language-plaintext highlighter-rouge">Game Test framework</code> didn’t have the same events and had no events related
to a mob’s death. But just as I was ready to throw the feature in the bin, a new beta version of Minecraft dropped, and deep within its patch notes contained a new event, <code class="language-plaintext highlighter-rouge">beforeDataDrivenEntityTriggerEvent</code>:</p>

<p><img src="/assets/siemcraft_event.png" alt="Minecraft Beta release notes detailing new event" /></p>

<p>I have never <a href="https://en.wiktionary.org/wiki/yak_shaving">Yak Shaved</a> harder to solve a problem in my life.</p>

<p>In…short:</p>
<blockquote>
  <p>Combining this feature with a new Addon allowed me to define a new type of animal that looks like a chicken/cow/spider/etc. These animals would only ever be created by SIEMCraft, and unlike a normal animal, they had a special “event”, that trigged only when they were specifically killed by a player holding a diamond sword. <code class="language-plaintext highlighter-rouge">Events</code> are used by Minecraft to trigger behaviour, e.g. give an egg to a player when they pet a chicken. I created an event that didn’t actually <em>do anything</em>, but it was enough to trigger some <code class="language-plaintext highlighter-rouge">Game Test Framework</code> Javascript code that was listening to the new <code class="language-plaintext highlighter-rouge">beforeDataDrivenEntityTriggerEvent</code> event. This event contained the event and animal names, which I could then (by filtering only on my custom ‘killed by a diamond sword’ event) send back to SIEMCraft using the same <code class="language-plaintext highlighter-rouge">playerMessage</code> technique as before.</p>
</blockquote>

<p>In diagram form, it looks like this:
<img src="/assets/siemcraft_wth.png" alt="rediculous diagram explaining how SIEMCraft kills a process" /></p>

<p>While technically we’re past the deprecation date and the old version of the Javascript addon still works, SIEMCraft is now protected from future releases of Minecraft…almost.</p>

<hr />

<h2 id="finishing-siemcraft">Finishing SIEMCraft</h2>
<p>By this stage, SIEMCraft had consumed not only my PTO but <strong>many</strong> weeks of free time beyond that. I think
I yak-shaved too hard and ended up shaving part of my brain, so after I shared my journey of bullshit at <a href="https://www.bsidesau.com.au/csides.html">my local security meetup</a>, I took a long break before finalising the code and writing this blog. During this time Minecraft made another breaking change, requiring me to <a href="https://github.com/pathtofile/mcwss">fork and fix the Minecraft Websocket library</a> I was using.</p>

<p>But it’s now all completed and working (for now). You can get SIEMCraft code here: <a href="https://github.com/pathtofile/siemcraft">https://github.com/pathtofile/siemcraft</a></p>

<p>Instructions on how to set up Minecraft and run it are in the README, but please don’t use it for real things. Or do, and send me the screenshots of the ensuring dumpster fire. If Minecraft changes something again and breaks SIEMCRAFT, I am unlikley to fix it, as it has already taken up way too much of my time for a such a silly thing. So try it out while you can.</p>

<p>Many, many thanks to all the library authors I used for this project (<a href="https://twitter.com/bradleyjkemp">Bradley Kemp</a>, <a href="https://twitter.com/0xrawsec">RawSec</a>, <a href="https://twitter.com/cyb3rops">Florian Roth</a>, <a href="https://twitter.com/SandertvNL">Sander Ten Veldhuis</a>), <a href="https://www.s-anand.net/blog/about-me/">S Anand’s blog</a> as well as everyone who puts up with this garbage.</p>

<p>Oh, did I mention it works in Minecraft VR? <a href="https://youtu.be/8Vyf8Y5wcRY">https://youtu.be/8Vyf8Y5wcRY</a></p>

<p><img src="/assets/siemcraft_vr.png" alt="Minecraft VR" /></p>

<p>Is this the first Metaverse SIEM?</p>]]></content><author><name></name></author><summary type="html"><![CDATA[SIEMCraft - Security detection monitoring using Minecraft]]></summary></entry><entry><title type="html">Tool - Create run VPNs in various clouds</title><link href="https://blog.tofile.dev/2022/06/02/tf_wireguard.html" rel="alternate" type="text/html" title="Tool - Create run VPNs in various clouds" /><published>2022-06-02T12:00:00+00:00</published><updated>2022-06-02T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/06/02/tf_wireguard</id><content type="html" xml:base="https://blog.tofile.dev/2022/06/02/tf_wireguard.html"><![CDATA[<p>For a while I’ve been using Terraform to quickly create simple VPN endpoints on
various Cloud providers.</p>

<p>I recently cleaned up the documentation and variables to make it easy to spin up
the VMs in various locations, and uploaded the code to GitHub: <a href="https://github.com/pathtofile/tf_wireguard">https://github.com/pathtofile/tf_wireguard</a>.</p>

<p>As I state in the README, other projects like <a href="https://github.com/trailofbits/algo/">Algo</a> are probably a better choice, but for me
I wanted to have more understanding and control over what I was using.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Tool - Create run VPNs in various clouds]]></summary></entry><entry><title type="html">Tool - Use Terraform and Bitcoin to run VMs</title><link href="https://blog.tofile.dev/2022/04/24/tf_bitlaunch.html" rel="alternate" type="text/html" title="Tool - Use Terraform and Bitcoin to run VMs" /><published>2022-04-24T12:00:00+00:00</published><updated>2022-04-24T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/04/24/tf_bitlaunch</id><content type="html" xml:base="https://blog.tofile.dev/2022/04/24/tf_bitlaunch.html"><![CDATA[<p><a href="https://bitlaunch.io">Bitlaunch</a> is a way to pay for DigitalOcean VMs using Bitcoin.</p>

<p>I was interested in using Bitlaunch, and wanted to learn how to create a Terraform Provider,
so while I’m 100% unafilliated with Bitlaunch I made a Terraform Provider to make it easier
to create and manage Vms.</p>

<p>The provider is <a href="https://registry.terraform.io/providers/pathtofiletf/bitlaunch/latest/docs">here</a>,
and the sourcecode is <a href="https://github.com/pathtofile-tf/terraform-provider-bitlaunch">here</a>.</p>

<p>The repository is under my <code class="language-plaintext highlighter-rouge">pathtofile-tf</code> GitHub account, as in order to publish a Terraform
Provider you must give Hashicorp permission to use your GitHub account, which I didn’t want to do
on my main account.</p>

<p>Example:</p>
<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">terraform</span> <span class="p">{</span>
  <span class="nx">required_providers</span> <span class="p">{</span>
    <span class="nx">bitlaunch</span> <span class="p">=</span> <span class="p">{</span>
      <span class="nx">source</span>  <span class="p">=</span> <span class="s2">"pathtofile-tf/bitlaunch"</span>
      <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.4.0"</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">variable</span> <span class="s2">"token"</span> <span class="p">{</span> <span class="nx">sensitive</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">}</span>
<span class="k">variable</span> <span class="s2">"ssh_pubkey"</span> <span class="p">{</span> <span class="nx">type</span> <span class="p">=</span> <span class="nx">string</span> <span class="p">}</span>
<span class="k">variable</span> <span class="s2">"host"</span> <span class="p">{</span> <span class="nx">default</span> <span class="p">=</span> <span class="s2">"DigitalOcean"</span> <span class="p">}</span>

<span class="k">provider</span> <span class="s2">"bitlaunch"</span> <span class="p">{</span>
  <span class="nx">token</span> <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">token</span>
<span class="p">}</span>

<span class="c1">// Data</span>
<span class="k">data</span> <span class="s2">"bitlaunch_image"</span> <span class="s2">"image"</span> <span class="p">{</span>
  <span class="nx">host</span>         <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">host</span>
  <span class="nx">distro_name</span>  <span class="p">=</span> <span class="s2">"Ubuntu"</span>
  <span class="nx">version_name</span> <span class="p">=</span> <span class="s2">"20.04 (LTS) x64"</span>
<span class="p">}</span>

<span class="k">data</span> <span class="s2">"bitlaunch_region"</span> <span class="s2">"region"</span> <span class="p">{</span>
  <span class="nx">host</span>        <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">host</span>
  <span class="nx">region_name</span> <span class="p">=</span> <span class="s2">"San Francisco"</span>
  <span class="nx">slug</span>        <span class="p">=</span> <span class="s2">"sfo2"</span>
<span class="p">}</span>

<span class="k">data</span> <span class="s2">"bitlaunch_size"</span> <span class="s2">"size"</span> <span class="p">{</span>
  <span class="nx">host</span>      <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">host</span>
  <span class="nx">cpu_count</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="nx">memory_mb</span> <span class="p">=</span> <span class="mi">1024</span>
<span class="p">}</span>

<span class="c1">// Resources</span>
<span class="k">resource</span> <span class="s2">"bitlaunch_sshkey"</span> <span class="s2">"sshkey"</span> <span class="p">{</span>
  <span class="nx">name</span>    <span class="p">=</span> <span class="s2">"tf_sshkeys"</span>
  <span class="nx">content</span> <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">ssh_pubkey</span>
<span class="p">}</span>

<span class="k">resource</span> <span class="s2">"bitlaunch_server"</span> <span class="s2">"server"</span> <span class="p">{</span>
  <span class="nx">host</span>        <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">host</span>
  <span class="nx">name</span>        <span class="p">=</span> <span class="s2">"tf_server"</span>
  <span class="nx">image_id</span>    <span class="p">=</span> <span class="k">data</span><span class="p">.</span><span class="nx">bitlaunch_image</span><span class="p">.</span><span class="nx">image</span><span class="p">.</span><span class="nx">id</span>
  <span class="nx">size_id</span>     <span class="p">=</span> <span class="k">data</span><span class="p">.</span><span class="nx">bitlaunch_size</span><span class="p">.</span><span class="nx">size</span><span class="p">.</span><span class="nx">id</span>
  <span class="nx">region_id</span>   <span class="p">=</span> <span class="k">data</span><span class="p">.</span><span class="nx">bitlaunch_region</span><span class="p">.</span><span class="nx">region</span><span class="p">.</span><span class="nx">id</span>
  <span class="nx">ssh_keys</span>    <span class="p">=</span> <span class="p">[</span><span class="nx">bitlaunch_sshkey</span><span class="p">.</span><span class="nx">sshkey</span><span class="p">.</span><span class="nx">id</span><span class="p">]</span>
  <span class="nx">wait_for_ip</span> <span class="p">=</span> <span class="kc">true</span>
<span class="p">}</span>

<span class="c1">// Outputs</span>
<span class="k">output</span> <span class="s2">"ip_address"</span> <span class="p">{</span>
  <span class="nx">value</span> <span class="p">=</span> <span class="nx">bitlaunch_server</span><span class="p">.</span><span class="nx">server</span><span class="p">.</span><span class="nx">ipv4</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><summary type="html"><![CDATA[Tool - Use Terraform and Bitcoin to run VMs]]></summary></entry><entry><title type="html">Commandline Cloaking and Sysmon for Linux</title><link href="https://blog.tofile.dev/2022/01/04/sysmonlinux.html" rel="alternate" type="text/html" title="Commandline Cloaking and Sysmon for Linux" /><published>2022-01-04T12:00:00+00:00</published><updated>2022-01-04T12:00:00+00:00</updated><id>https://blog.tofile.dev/2022/01/04/sysmonlinux</id><content type="html" xml:base="https://blog.tofile.dev/2022/01/04/sysmonlinux.html"><![CDATA[<h2 id="tldr">tl;dr</h2>
<p>I investigated running Sysmon for Linux against a number of techniques to
fake a program’s commandline. Code and Sysmon config available <a href="https://github.com/pathtofile/commandline_cloaking">on GitHub</a>.</p>

<ul>
  <li><a href="#tldr">tl;dr</a></li>
  <li><a href="#sysmon-for-linux">Sysmon for Linux</a></li>
  <li><a href="#attack-scenario">Attack Scenario</a></li>
  <li><a href="#lab-setup">Lab setup</a></li>
  <li><a href="#tests">Tests</a>
    <ul>
      <li><a href="#test-0---no-cloaking">Test #0 - No cloaking</a></li>
      <li><a href="#test-1---post-execution-cloaking">Test #1 - Post-execution cloaking</a></li>
      <li><a href="#test-2---piping-input">Test #2 - Piping input</a></li>
      <li><a href="#test-3---in-memory-loader">Test #3 - In-memory loader</a></li>
      <li><a href="#test-4---ld_preload">Test #4 - LD_PRELOAD</a></li>
      <li><a href="#test-5---in-memory-patching">Test #5 - In-memory patching</a></li>
    </ul>
  </li>
  <li><a href="#conclusions-and-final-thoughts">Conclusions and final thoughts</a>
    <ul>
      <li><a href="#sysmon-for-linux-1">Sysmon for Linux</a></li>
      <li><a href="#writing-low-level-go">Writing low-level Go</a></li>
    </ul>
  </li>
  <li><a href="#example-code">Example Code</a></li>
</ul>

<h2 id="sysmon-for-linux">Sysmon for Linux</h2>
<p>Earlier this year, Microsoft released <a href="https://github.com/Sysinternals/SysmonForLinux">Sysmon for Linux</a>,
which is an awesome new addition to free Linux monitoring tools. Typically, sysadmins have used <a href="https://linux.die.net/man/8/auditd">AuditD</a>
(or tools that use AuditD under the hood) to monitor Linux systems. However, using AuditD can be tricky to configure, and can require a level of knowledge
of Linux internals that might put off administrators that just want basic process monitoring.</p>

<p>Sysmon for Linux conversely uses the same event schema as Sysmon on Windows, which abstracts away the internals
in how it generates events, instead groping them into “ProcessCreate”, “NetworkConnect “, “FileDelete”, etc.
Using the same schema as Windows is quite useful for people already familiar with the Windows version of Sysmon,
although documentation could be improved to help those who aren’t familiar, or only know AuditD.</p>

<p>I was interested in exploring what it’s like to set up and run Sysmon for Linux, and how it might fare against some
common techniques malware uses to hide from system administrators.</p>

<h2 id="attack-scenario">Attack Scenario</h2>
<p>A lot of common malware detections on Linux are based upon on pattern matching commandlines and program filenames.
I wanted to explore the various ways a program can alter or hide commandlines from Audit tools, and see what is 
recorded by Sysmon for Linux.</p>

<p>It was also an excuse to learn more about writing low-level programs in Go, which I’ve always wanted to
learn more about, instead of defaulting to C all the time.</p>

<h2 id="lab-setup">Lab setup</h2>
<p>To set things up, I used both Roberto Rodriguez’s dope <a href="https://techcommunity.microsoft.com/t5/microsoft-sentinel-blog/automating-the-deployment-of-sysmon-for-linux-and-azure-sentinel/ba-p/2847054">Sysmon Overview</a> and <a href="https://github.com/Sysinternals/SysmonForLinux/blob/main/INSTALL.md">the official instructions</a>.</p>

<p>As I was only interested in process events, I used this simple config to just get process creations:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Sysmon</span> <span class="na">schemaversion=</span><span class="s">"4.70"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;EventFiltering&gt;</span>
    <span class="c">&lt;!-- Event ID 1 == ProcessCreate. Log all newly created processes --&gt;</span>
    <span class="nt">&lt;RuleGroup</span> <span class="na">name=</span><span class="s">""</span> <span class="na">groupRelation=</span><span class="s">"or"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ProcessCreate</span> <span class="na">onmatch=</span><span class="s">"exclude"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/RuleGroup&gt;</span>
  <span class="nt">&lt;/EventFiltering&gt;</span>
<span class="nt">&lt;/Sysmon&gt;</span>
</code></pre></div></div>

<p>Once Sysmon was installed and running, to make things simple I didn’t forward the events to a SIEM, I just used the <code class="language-plaintext highlighter-rouge">sysmonLogView</code> helper tool to print events out to the console in a human-readable format:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>bash <span class="nt">-c</span> <span class="s1">'
    tail -f /var/log/syslog |
    /opt/sysmon/sysmonLogView -e 1
'</span>
</code></pre></div></div>

<h2 id="tests">Tests</h2>
<h3 id="test-0---no-cloaking">Test #0 - No cloaking</h3>
<p>To start, I made a simple program named <code class="language-plaintext highlighter-rouge">basic</code> that just prints out the arguments passed into it:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">pid</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getpid</span><span class="p">()</span>
  <span class="n">ppid</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getppid</span><span class="p">()</span>

  <span class="n">argc</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">)</span>
  <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"  PID     %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pid</span><span class="p">)</span>
  <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"  PPID    %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ppid</span><span class="p">)</span>
  <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"  argc    %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">argc</span><span class="p">)</span>
  <span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
    <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"  argv[%d] %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
  <span class="p">}</span>

  <span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"  Sleeping for 60 seconds so you can lookup the PID"</span><span class="p">)</span>
  <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">60</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When run with a single argument, <code class="language-plaintext highlighter-rouge">basic</code> prints out:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> /path/to/basic AAAA
  PID     984173
  PPID    1054354
  argc    2
  argv[0] /path/to/basic
  argv[1] AAAA
  Sleeping <span class="k">for </span>60 seconds so you can lookup the PID
</code></pre></div></div>

<p>This generates the following Sysmon Event as expected:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-11-30 23:51:08.592
  ProcessGuid: {35dd0383-b8ec-61a6-b8d0-480000000000}
  ProcessId: 984173
  Image: /path/to/basic
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: /path/to/basic AAAA
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-9d97-61a4-e803-000002000000}
  LogonId: 1000
  TerminalSessionId: 347
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-9a98-61a4-fddb-e43082550000}
  ParentProcessId: 1054354
  ParentImage: /usr/bin/bash
  ParentCommandLine: -bash
  ParentUser: path
</code></pre></div></div>

<p>We can see <code class="language-plaintext highlighter-rouge">basic</code> was launched by <code class="language-plaintext highlighter-rouge">bash</code>, the process IDs match up, and the commandline is the same.
All as expected.</p>

<p>A really neat aspect of Sysmon for Linux is how it extracts the commandline arguments from a process. Other tools use the
arguments passed into the <a href="https://www.man7.org/linux/man-pages/man2/execve.2.html">execve</a> syscall, but these arguments
can be vulnerable to a race condition, such as the one detailed by <a href="https://defcon.org/html/defcon-29/dc-29-speakers.html#guo">Rex Guo and Junyuan Zeng at DEF CON 29</a>, due to the fact they exist in the parent process’ userspace memory. Sysmon for Linux however extracts the arguments from the
task struct within the kernel’s memory, which (at the time Sysmon reads it) is inaccessible to the parent process, and will always be
the real arguments used to start the process.</p>

<p>We can also compare Sysmon’s output to other sysadmin tools, proving the commandline, process ID, and filename match up:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Use ps to look for 'basic' process</span>
<span class="nv">$&gt;</span> ps u <span class="nt">-C</span> <span class="s2">"basic"</span>
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
path     1066830  0.0  0.1 702896  8968 pts/2    Sl+  11:23   0:00 /path/to/basic AAAA
</code></pre></div></div>

<p>Under the hood, <code class="language-plaintext highlighter-rouge">ps</code> parses data from the <code class="language-plaintext highlighter-rouge">/proc/</code> pseudo-filesystem, so we can also do that:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Look up the commandline using the PID and the proc psudo filesystem</span>
<span class="nv">$&gt;</span> xxd <span class="nt">-g</span> 1 <span class="nt">-c</span> 6 /proc/1066830/cmdline
00000000: 2e 2f 62 69 6e 2f  /path/to/
00000006: 62 61 73 69 63 00  basic.
0000000c: 41 41 41 41 00     AAAA.
</code></pre></div></div>

<p>By reading <code class="language-plaintext highlighter-rouge">/proc/&lt;pid&gt;/cmdline</code> we can see the strings in <code class="language-plaintext highlighter-rouge">argv</code>, each ending in a <code class="language-plaintext highlighter-rouge">00</code> NULL character.
This data exists within <code class="language-plaintext highlighter-rouge">basic</code>’s memory, which means that a dodgy program could alter this data.</p>

<h3 id="test-1---post-execution-cloaking">Test #1 - Post-execution cloaking</h3>
<p>Next, I wrote a program named <code class="language-plaintext highlighter-rouge">dodgy</code> to alter the contents of it’s argv after it has started to run.
While this is easy to do in either C or Go, the Go code is harder to read, so I created <code class="language-plaintext highlighter-rouge">dodgy</code> in C, which looks like this:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
  <span class="c1">// Overwrite address of argv to fool 'ps'</span>
  <span class="n">memset</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="sc">'F'</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">memset</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="sc">'B'</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span>
  <span class="p">}</span>

  <span class="c1">// Print arguments just like 'basic' did</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"  PID     %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">getpid</span><span class="p">());</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"  PPID    %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">getppid</span><span class="p">());</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"  argc    %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">argc</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"  argv[%d] %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
  <span class="p">}</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"Sleeping for 60 seconds so you can lookup the PID</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now when we run the program we get:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> /path/to/dodgy AAAA
  PID     1067696
  PPID    1054354
  argc    2
  argv[0] FFFFFFFFFFF
  argv[1] BBBB
Sleeping <span class="k">for </span>60 seconds so you can lookup the PID

<span class="c"># Then in another tab look for all 'dodgy' processes</span>
<span class="nv">$&gt;</span> ps u <span class="nt">-C</span> <span class="s2">"dodgy"</span>
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
path     1067696  0.0  0.0   2304   556 pts/2    S+   12:06   0:00 FFFFFFFFFFF BBBB

<span class="c"># Look up the commandline using the PID and the proc psudo filesystem</span>
<span class="nv">$&gt;</span> xxd <span class="nt">-g</span> 1 <span class="nt">-c</span> 6 /proc/1067696/cmdline
00000000: 46 46 46 46 46 46  FFFFFF
00000006: 46 46 46 46 46 00  FFFFF.
0000000c: 42 42 42 42 00     BBBB.
</code></pre></div></div>

<p>There are similar techniques programs can use to fake information once it has executed,
including:</p>
<ul>
  <li>Using <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl</a> and <code class="language-plaintext highlighter-rouge">PR_SET_NAME</code> to change the process name in <code class="language-plaintext highlighter-rouge">/proc/pid/comm</code></li>
  <li>Fork twice and kill parents, which sets the process’ Parent PID to PID 1</li>
</ul>

<p>But to Sysmon all these efforts are in vain - As it logs the process information before it runs, 
it sees the original commandline that the process was started with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-11-30 23:51:08.592
  ProcessGuid: {35dd0383-b8ec-61a6-b8d0-480000000000}
  ProcessId: 1067696
  Image: /path/to/dodgy
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: /path/to/dodgy AAAA
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-9d97-61a4-e803-000002000000}
  LogonId: 1000
  TerminalSessionId: 347
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-9a98-61a4-fddb-e43082550000}
  ParentProcessId: 1054354
  ParentImage: /usr/bin/bash
  ParentCommandLine: -bash
  ParentUser: path
</code></pre></div></div>

<h3 id="test-2---piping-input">Test #2 - Piping input</h3>
<p>Some programs support reading data in from standard input, which means it can either be <code class="language-plaintext highlighter-rouge">|</code> piped in
or read in using <code class="language-plaintext highlighter-rouge">&lt;</code> file redirection. One example is <code class="language-plaintext highlighter-rouge">Python</code>, where the following examples are all equivalent:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Run the file</span>
python3 quietquokka.py
python3 <span class="si">$(</span>bash <span class="nt">-c</span> <span class="s2">"echo cXVpZXRxdW9ra2EucHkK | base64 -d"</span><span class="si">)</span>
<span class="c"># Pipe file in</span>
<span class="nb">cat </span>quietquokka.py | python3
<span class="c"># File redirection</span>
python3 &lt; quietquokka.py
</code></pre></div></div>

<p>The first two generate Syslog events matching what you expect (the <code class="language-plaintext highlighter-rouge">$(bash -c ...</code> part gets evaluated prior to the syscall):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-12-01 02:57:11.135
  ProcessGuid: {35dd0383-e487-61a6-fdbb-b3691d560000}
  ProcessId: 1068450
  Image: /usr/bin/bash
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: bash -c echo cXVpZXRxdW9ra2EucHkK | base64 -d
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-9d97-61a4-e803-000002000000}
  LogonId: 1000
  TerminalSessionId: 347
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-9a98-61a4-fddb-e43082550000}
  ParentProcessId: 1054354
  ParentImage: /usr/bin/bash
  ParentCommandLine: -bash
  ParentUser: path
Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-12-01 02:57:11.137
  ProcessGuid: {35dd0383-e487-61a6-69ee-60001a560000}
  ProcessId: 1068452
  Image: /usr/bin/base64
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: base64 -d
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-9d97-61a4-e803-000002000000}
  LogonId: 1000
  TerminalSessionId: 347
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-e487-61a6-fdbb-b3691d560000}
  ParentProcessId: 1068450
  ParentImage: /usr/bin/bash
  ParentCommandLine: bash
  ParentUser: path
Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-12-01 02:57:11.137
  ProcessGuid: {35dd0383-e487-61a6-ed63-6a0000000000}
  ProcessId: 1068453
  Image: /usr/bin/python3.9
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: python3 quietquokka.py
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-9d97-61a4-e803-000002000000}
  LogonId: 1000
  TerminalSessionId: 347
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-9a98-61a4-fddb-e43082550000}
  ParentProcessId: 1054354
  ParentImage: /usr/bin/bash
  ParentCommandLine: -bash
  ParentUser: path
</code></pre></div></div>

<p>However, in the other two examples (piping in data and file redirection), the Python commandline is empty:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-12-01 02:58:58.656
  ProcessGuid: {35dd0383-e4f2-61a6-ed63-6a0000000000}
  ProcessId: 1068470
  Image: /usr/bin/python3.9
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: python3
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-9d97-61a4-e803-000002000000}
  LogonId: 1000
  TerminalSessionId: 347
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-9a98-61a4-fddb-e43082550000}
  ParentProcessId: 1054354
  ParentImage: /usr/bin/bash
  ParentCommandLine: -bash
  ParentUser: path
</code></pre></div></div>

<p>I’ve <a href="https://blog.tofile.dev/2021/02/15/ebpf-01.html">covered this technique before</a>, including thoughts on how to
make use of eBPF to detect it. And to be clear Sysmon isn’t wrong - the Python was started with just the commandline “python3”.</p>

<p>You can’t pipe in all arguments to a program, however, only in specific cases where the program supports it.
But this does include a number of tools like <code class="language-plaintext highlighter-rouge">python</code>, <code class="language-plaintext highlighter-rouge">curl</code>, and <code class="language-plaintext highlighter-rouge">bash</code>, and is important to think about when
looking to make commandline-focussed detection.</p>

<h3 id="test-3---in-memory-loader">Test #3 - In-memory loader</h3>
<p>Another common technique used by malware is to execute a program
from memory. This can be easily done by using
<a href="https://man7.org/linux/man-pages/man2/memfd_create.2.html">memfd_create</a>:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c">// Print PID of loader program</span>
  <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"  Ldr PID %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getpid</span><span class="p">())</span>

  <span class="c">// Use memfd to create in-memory file</span>
  <span class="n">fd</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">unix</span><span class="o">.</span><span class="n">MemfdCreate</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>

  <span class="c">// open created in-memory file</span>
  <span class="n">fp</span> <span class="o">:=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"/proc/self/fd/%d"</span><span class="p">,</span> <span class="n">fd</span><span class="p">)</span>
  <span class="n">memfd_file</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">NewFile</span><span class="p">(</span><span class="kt">uintptr</span><span class="p">(</span><span class="n">fd</span><span class="p">),</span> <span class="n">fp</span><span class="p">)</span>
  <span class="k">defer</span> <span class="n">memfd_file</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

  <span class="c">// Read ELF from disk and write into in-memory file</span>
  <span class="c">// ELF could also be read from a C2 server, encrypted</span>
  <span class="c">// on disk, hardcoded, piped in from stdin, etc.</span>
  <span class="n">data</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">ioutil</span><span class="o">.</span><span class="n">ReadFile</span><span class="p">(</span><span class="s">"/path/to/basic"</span><span class="p">)</span>
  <span class="n">memfd_file</span><span class="o">.</span><span class="n">Write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>

  <span class="c">// Execute in-memory binary, fake argv[0] filename</span>
  <span class="n">argv</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"from_loader"</span><span class="p">,</span> <span class="s">"AAAA"</span><span class="p">}</span>
  <span class="n">unix</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="n">argv</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Environ</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This <code class="language-plaintext highlighter-rouge">loader</code> program can be then be run to launch <code class="language-plaintext highlighter-rouge">basic</code> from in-memory:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> /path/to/loader
  Ldr PID 17436
  PID     17436
  PPID    15924
  argc    2
  argv[0] from_loader
  argv[1] AAAA
  Sleeping <span class="k">for </span>60 seconds so you can lookup the PID

<span class="c"># Look up pid</span>
<span class="nv">$&gt;</span> ps u | <span class="nb">grep </span>17436
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
path       17436  0.0  0.0 703064  2872 pts/0    Sl+  15:13   0:00 from_loader AAAA
</code></pre></div></div>

<p>Due to how the <a href="https://www.man7.org/linux/man-pages/man2/execve.2.html">execve</a> syscall works, <code class="language-plaintext highlighter-rouge">basic</code>’s PID is the same as loader’s. This is because <code class="language-plaintext highlighter-rouge">execve</code> doesn’t really create a new process per sey,
but instead replaces the program running from <code class="language-plaintext highlighter-rouge">loader</code> to <code class="language-plaintext highlighter-rouge">basic</code>.
But to Sysmon, both programs are recorded:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2022-01-02 04:19:04.901
  ProcessGuid: {35dd0383-27b8-61d1-8cdf-480000000000}
  ProcessId: 17436
  Image: /path/to//bin/loader
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: /path/to/loader /path/to/basic AAAA
  CurrentDirectory: /path/to/
  User: path
  LogonGuid: {35dd0383-21ff-61d1-e803-000000000000}
  LogonId: 1000
  TerminalSessionId: 177
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {00000000-0000-0000-0000-000000000000}
  ParentProcessId: 15924
  ParentImage: -
  ParentCommandLine: -
  ParentUser: -
Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2022-01-02 04:19:04.901
  ProcessGuid: {35dd0383-27b8-61d1-1000-480000000000}
  ProcessId: 17436
  Image: /memfd:
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: from_loader AAAA
  CurrentDirectory: /path/to/
  User: path
  LogonGuid: {35dd0383-21ff-61d1-e803-000000000000}
  LogonId: 1000
  TerminalSessionId: 177
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {00000000-0000-0000-0000-000000000000}
  ParentProcessId: 15924
  ParentImage: -
  ParentCommandLine: -
  ParentUser: -
</code></pre></div></div>

<p>Sysmon can only tell the second ELF executed was from in-memory (marking it as <code class="language-plaintext highlighter-rouge">/memfd</code>),
and it records the ‘fake’ <code class="language-plaintext highlighter-rouge">from_loader</code> commandline. It cannot tell that the binary run was actually <code class="language-plaintext highlighter-rouge">/path/to/basic</code>.</p>

<p>But depending on the machine, user, or parent process, it is probably highly suspicious to see a <code class="language-plaintext highlighter-rouge">/memfd</code> in-memory ELF being run.
So while you cannot tell what binary it was, this alone might be enough to investigate.</p>

<h3 id="test-4---ld_preload">Test #4 - LD_PRELOAD</h3>
<p>One common technique used by malware to hide is to make use of <a href="https://www.man7.org/linux/man-pages/man8/ld.so.8.html">LD_PRELOAD</a> and similar
environment variables. By setting <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> to the path to a library on disk, the Linux dynamic linker will inject this library into
all new programs, and will look in that library first for any exported functions that the program may have instead wanted to use
from other libraries. This means a malicious library and can hook and alter the program, and is commonly used as a backdoor to alter
functions that relate to file reading or network connections, so the malware can hide data on disk, or allow a normally blocked connection through.</p>

<p>But another thing a malicious <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> library can do is hook the program just as it starts, and alter the <code class="language-plaintext highlighter-rouge">argv</code> arguments before they
are seen and used. For programs written in C using libc, this means hooking the <code class="language-plaintext highlighter-rouge">__libc_start_main</code> function.</p>

<p>In C, this type of hooking is simple:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">__libc_start_main</span><span class="p">(</span>
  <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">main</span><span class="p">)(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">),</span>
  <span class="kt">int</span> <span class="n">argc</span><span class="p">,</span>
  <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span>
  <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">),</span>
  <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fini</span><span class="p">)(</span><span class="kt">void</span><span class="p">),</span>
  <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">rtld_fini</span><span class="p">)(</span><span class="kt">void</span><span class="p">),</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">stack_end</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// Overwrite args like in 'dodgy'</span>
  <span class="n">memset</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="sc">'F'</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">memset</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="sc">'B'</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span>
  <span class="p">}</span>

  <span class="c1">// Lookup the real libc function</span>
  <span class="n">typeof</span><span class="p">(</span><span class="o">&amp;</span><span class="n">__libc_start_main</span><span class="p">)</span> <span class="n">orig_start_main</span> <span class="o">=</span> <span class="n">dlsym</span><span class="p">(</span><span class="n">RTLD_NEXT</span><span class="p">,</span> <span class="s">"__libc_start_main"</span><span class="p">);</span>

  <span class="c1">// Call real __libc_start_main</span>
  <span class="k">return</span> <span class="n">orig_start_main</span><span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">argc</span><span class="p">,</span> <span class="n">argv</span><span class="p">,</span> <span class="n">init</span><span class="p">,</span> <span class="n">fini</span><span class="p">,</span> <span class="n">rtld_fini</span><span class="p">,</span> <span class="n">stack_end</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After writing a C version of the <code class="language-plaintext highlighter-rouge">basic</code> program to be hooked, I ran with and set the <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> variable to be my malicious library:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Set the LD_PRELOAD variable to path to preload</span>
<span class="c"># NOTE: this won't work on Go programs, so use the C version of Basic</span>
<span class="nv">LD_PRELOAD</span><span class="o">=</span>/path/to/preload.so /path/to/basic_c AAAA
  PID     26687
  PPID    16961
  argc    2
  argv[0] FFFFFFFFFFFFF
  argv[1] BBBB
  Sleeping <span class="k">for </span>60 seconds so you can lookup the PID

<span class="c"># Check ps output for pid</span>
<span class="nv">$&gt;</span> ps aux | <span class="nb">grep </span>26687
path       26687  0.0  0.0   2360   532 pts/1    S+   15:47   0:00 FFFFFFFFFFFFF BBBB

<span class="c"># Extract LD_PRELOAD env variable from ps output</span>
<span class="nv">$&gt;</span> ps ue | <span class="nb">grep </span>26687 | <span class="nb">grep</span> <span class="nt">-o</span> <span class="s2">"LD_PRELOAD=.*"</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span>
<span class="nv">LD_PRELOAD</span><span class="o">=</span>/path/to/preload.so
</code></pre></div></div>

<p>As preload’s shenanigans run after the process starts, Sysmon only see’s the original arguments, and
not what the effective arguments to <code class="language-plaintext highlighter-rouge">basic_c</code> are:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
  RuleName: -
  UtcTime: 2021-12-02 03:29:01.320
  ProcessGuid: {35dd0383-3d7d-61a8-9d12-65fb78550000}
  ProcessId: 26687
  Image: /path/to/basic_c
  FileVersion: -
  Description: -
  Product: -
  Company: -
  OriginalFileName: -
  CommandLine: /path/to/basic_c AAAA
  CurrentDirectory: /path/to
  User: path
  LogonGuid: {35dd0383-4d8c-61a4-e803-000000000000}
  LogonId: 1000
  TerminalSessionId: 334
  IntegrityLevel: no level
  Hashes: -
  ParentProcessGuid: {35dd0383-4d8c-61a4-fd3b-d4af46560000}
  ParentProcessId: 1052430
  ParentImage: /usr/bin/bash
  ParentCommandLine: -bash
  ParentUser: path
</code></pre></div></div>

<p>This is accurate, <em>technically</em> <code class="language-plaintext highlighter-rouge">basic_c</code> was launched with <code class="language-plaintext highlighter-rouge">AAAA</code>, and preload runs after the process
has launched. But the <code class="language-plaintext highlighter-rouge">basic_c</code> program will act as if the fake <code class="language-plaintext highlighter-rouge">BBBB</code> argument was passed into it.</p>

<p>To add to the confusion, instead of overwriting <code class="language-plaintext highlighter-rouge">argv</code>, the preload library could instead
point <code class="language-plaintext highlighter-rouge">__libc_start_main</code> to a <em>new</em> argv array, which will fool <code class="language-plaintext highlighter-rouge">ps</code> as well:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Create our own argv and argc</span>
<span class="k">static</span> <span class="kt">char</span><span class="o">*</span> <span class="n">new_argv</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"from_preload"</span><span class="p">,</span>
    <span class="s">"BBBB"</span>
<span class="p">};</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">new_argc</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>

<span class="kt">int</span> <span class="n">__libc_start_main</span><span class="p">(</span>
  <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">main</span><span class="p">)(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">),</span>
  <span class="kt">int</span> <span class="n">argc</span><span class="p">,</span>
  <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span>
  <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="p">),</span>
  <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fini</span><span class="p">)(</span><span class="kt">void</span><span class="p">),</span>
  <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">rtld_fini</span><span class="p">)(</span><span class="kt">void</span><span class="p">),</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">stack_end</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// Lookup the real libc function</span>
  <span class="n">typeof</span><span class="p">(</span><span class="o">&amp;</span><span class="n">__libc_start_main</span><span class="p">)</span> <span class="n">orig_start_main</span> <span class="o">=</span> <span class="n">dlsym</span><span class="p">(</span><span class="n">RTLD_NEXT</span><span class="p">,</span> <span class="s">"__libc_start_main"</span><span class="p">);</span>

  <span class="c1">// Call real __libc_start_main, note we instead pass it our new argc and argv</span>
  <span class="c1">// instead of the original ones</span>
  <span class="k">return</span> <span class="n">orig_start_main</span><span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">new_argc</span><span class="p">,</span> <span class="n">new_argv</span><span class="p">,</span> <span class="n">init</span><span class="p">,</span> <span class="n">fini</span><span class="p">,</span> <span class="n">rtld_fini</span><span class="p">,</span> <span class="n">stack_end</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When run this looks like:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Set the LD_PRELOAD variable to path to preload</span>
<span class="c"># NOTE: this won't work on Go programs, so use the C version of Basic</span>
<span class="nv">LD_PRELOAD</span><span class="o">=</span>/path/to/preload.so /path/to/basic_c AAAA
  PID     28152
  PPID    16961
  argc    2
  argv[0] from_preload
  argv[1] BBBB
  Sleeping <span class="k">for </span>60 seconds so you can lookup the PID

<span class="c"># Check ps output for pid, note the original arguments and not what basic effectivly ran with</span>
<span class="nv">$&gt;</span> ps aux | <span class="nb">grep </span>28152
path       28152  0.0  0.0   2360   536 pts/1    S+   15:57   0:00 /path/to/basic_c AAAA
</code></pre></div></div>

<p>This blog was also meant to be about how to do things in Go and not C, so thanks to
<a href="https://github.com/awgh/">Awgh</a> on the <a href="http://bloodhoundgang.herokuapp.com/">Bloodhound Slack’s Go channel</a>, I was able to create a similar library in Go:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//export __libc_start_main</span>
<span class="k">func</span> <span class="n">__libc_start_main</span><span class="p">(</span><span class="n">main</span> <span class="o">*</span><span class="n">C</span><span class="o">.</span><span class="n">void</span><span class="p">,</span> <span class="n">argc</span> <span class="n">C</span><span class="o">.</span><span class="kt">int</span><span class="p">,</span> <span class="n">argv</span> <span class="o">**</span><span class="n">C</span><span class="o">.</span><span class="n">char</span><span class="p">,</span>    <span class="n">init</span> <span class="o">*</span><span class="n">C</span><span class="o">.</span><span class="n">void</span><span class="p">,</span>    <span class="n">fini</span> <span class="o">*</span><span class="n">C</span><span class="o">.</span><span class="n">void</span><span class="p">,</span>    <span class="n">rtld_fini</span> <span class="o">*</span><span class="n">C</span><span class="o">.</span><span class="n">void</span><span class="p">,)</span> <span class="n">C</span><span class="o">.</span><span class="kt">int</span> <span class="p">{</span>
  <span class="c">// Create and use new argv[0] and argv[1]</span>
  <span class="n">offset</span> <span class="o">:=</span> <span class="n">unsafe</span><span class="o">.</span><span class="n">Sizeof</span><span class="p">(</span><span class="kt">uintptr</span><span class="p">(</span><span class="m">0</span><span class="p">))</span>
  <span class="n">argv_copy</span> <span class="o">:=</span> <span class="n">argv</span>
  <span class="n">new_argv_0</span> <span class="o">:=</span> <span class="n">C</span><span class="o">.</span><span class="n">CString</span><span class="p">(</span><span class="s">"from_preload"</span><span class="p">)</span>
  <span class="o">*</span><span class="n">argv_copy</span> <span class="o">=</span> <span class="n">new_argv_0</span>
  <span class="k">if</span> <span class="n">argc</span> <span class="o">&gt;</span> <span class="m">1</span> <span class="p">{</span>
    <span class="n">new_argv_1</span> <span class="o">:=</span> <span class="n">C</span><span class="o">.</span><span class="n">CString</span><span class="p">(</span><span class="s">"BBBB"</span><span class="p">)</span>
    <span class="n">argv_copy</span> <span class="o">=</span> <span class="p">(</span><span class="o">**</span><span class="n">C</span><span class="o">.</span><span class="n">char</span><span class="p">)(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="kt">uintptr</span><span class="p">(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">argv_copy</span><span class="p">))</span> <span class="o">+</span> <span class="n">offset</span><span class="p">))</span>
    <span class="o">*</span><span class="n">argv_copy</span> <span class="o">=</span> <span class="n">new_argv_1</span>
  <span class="p">}</span>

  <span class="c">// Find and call real __libc_start_main function</span>
  <span class="n">fp</span> <span class="o">:=</span> <span class="n">C</span><span class="o">.</span><span class="n">dlopen</span><span class="p">(</span><span class="n">C</span><span class="o">.</span><span class="n">CString</span><span class="p">(</span><span class="s">"libc.so.6"</span><span class="p">),</span> <span class="n">C</span><span class="o">.</span><span class="n">RTLD_LAZY</span><span class="p">)</span>
  <span class="n">orig_func</span> <span class="o">:=</span> <span class="n">C</span><span class="o">.</span><span class="n">dlsym</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="n">C</span><span class="o">.</span><span class="n">CString</span><span class="p">(</span><span class="s">"__libc_start_main"</span><span class="p">))</span>
  <span class="n">val</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cdecl</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">orig_func</span><span class="p">),</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">main</span><span class="p">)),</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">argc</span><span class="p">),</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">argv</span><span class="p">)),</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">init</span><span class="p">)),</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">fini</span><span class="p">)),</span>
    <span class="kt">uintptr</span><span class="p">(</span><span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">rtld_fini</span><span class="p">)),</span>
  <span class="p">)</span>

  <span class="k">return</span> <span class="p">(</span><span class="n">C</span><span class="o">.</span><span class="kt">int</span><span class="p">)(</span><span class="n">val</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>(see the full code <a href="https://github.com/pathtofile/commandline_cloaking/blob/main/preload/go/preload.go">here</a>)</p>

<p>Unfortunately, Sysmon doesn’t log the environment variables a program was launched with, most likely
due to size constraints, but if it did so you should be able to see the <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>, which is not commonly used in production
environments, and will be pointing to the malicious library.</p>

<h3 id="test-5---in-memory-patching">Test #5 - In-memory patching</h3>
<p>The final technique to discuss is making use of in-memory patching, to achieve the same goal as <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>, but without
using the environment variable.</p>

<p>Back in 1998, <a href="https://twitter.com/silviocesare">Silvio Cesare</a> wrote a paper on
<a href="https://vxug.fakedoma.in/archive/VxHeaven/lib/vsc01.html">Unix ELF parasites and viruses</a>.
In the paper he wrote about how to patch ELF binaries to inject code at the program’s entrypoint, running custom
shellcode in between when the program has been <code class="language-plaintext highlighter-rouge">execve</code>‘d, and the program’s “main” function.</p>

<p>The awesome <a href="https://github.com/Binject/binjection">Binjection</a> library has implemented Silvio’s technique in Go,
so I was able to use their library and my in-memory loader code to:</p>
<ol>
  <li>Read a binary to run from disk</li>
  <li>Patch the entrypoint with custom shellcode that alters <code class="language-plaintext highlighter-rouge">argv</code> before returning to the real entrypoint</li>
  <li>Run the patched binary from memory using <code class="language-plaintext highlighter-rouge">memfd</code>.</li>
</ol>

<p>The full code I called <code class="language-plaintext highlighter-rouge">injector</code>, and is available <a href="https://github.com/pathtofile/commandline_cloaking/tree/main/injector">here</a>.
The shellcode to alter the arguments at the ELF entrypoint is actually very simple, as at the start of the ELF execution the kernel
will place the memory addresses to the arguments on the top of the stack, so as long as you don’t care about memory corruption, to overwrite the first
argument from <code class="language-plaintext highlighter-rouge">AAAA</code> to <code class="language-plaintext highlighter-rouge">BBBB</code> the shellcode simply looks like:</p>

<pre><code class="language-asm">; Get address of to argv[1] string
mov  rax, [rsp+0x10]

; overwrite argv[1] string to "BBBB\0"
; which in hex is: 42 42 42 42 00
mov [rax],   dword 0x42424242
mov [rax+4], byte 0x00

; Reset rax to 0
xor rax, rax
</code></pre>

<p>The end result looks like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> /path/to/injector /path/to/shellcode.bin /path/to/basic AAAA
  PID     28638
  PPID    16961
  argc    2
  argv[0] from_injector
  argv[1] BBBB
  Sleeping <span class="k">for </span>60 seconds so you can lookup the PID

<span class="c"># Check ps output for pid, note the original arguments and not what `basic` saw</span>
<span class="nv">$&gt;</span> ps aux | <span class="nb">grep </span>28152
path       28638  0.0  0.0 703064  2876 pts/1    Sl+  16:34   0:00 from_injector BBBB
</code></pre></div></div>

<p>Sysmon for Linux will log the loader fully, but again will have some trouble with the loaded binary, only recording <code class="language-plaintext highlighter-rouge">/memfd</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Event SYSMONEVENT_CREATE_PROCESS
 RuleName: -
 UtcTime: 2022-01-02 05:36:48.387
 ProcessGuid: {35dd0383-39f0-61d1-a01b-4b0000000000}
 ProcessId: 28638
 Image: /path/to/injector
 FileVersion: -
 Description: -
 Product: -
 Company: -
 OriginalFileName: -
 CommandLine: /path/to/injector /path/to/shellcode.bin /path/to/basic AAAA
 CurrentDirectory: /path/to/
 User: path
 LogonGuid: {35dd0383-25d1-61d1-e803-000001000000}
 LogonId: 1000
 TerminalSessionId: 178
 IntegrityLevel: no level
 Hashes: -
 ParentProcessGuid: {00000000-0000-0000-0000-000000000000}
 ParentProcessId: 16961
 ParentImage: -
 ParentCommandLine: -
 ParentUser: -
Event SYSMONEVENT_CREATE_PROCESS
 RuleName: -
 UtcTime: 2022-01-02 05:36:48.387
 ProcessGuid: {35dd0383-39f0-61d1-2700-480000000000}
 ProcessId: 28638
 Image: /memfd:
 FileVersion: -
 Description: -
 Product: -
 Company: -
 OriginalFileName: -
 CommandLine: from_injector AAAA
 CurrentDirectory: /path/to/
 User: path
 LogonGuid: {35dd0383-25d1-61d1-e803-000001000000}
 LogonId: 1000
 TerminalSessionId: 178
 IntegrityLevel: no level
 Hashes: -
 ParentProcessGuid: {00000000-0000-0000-0000-000000000000}
 ParentProcessId: 16961
 ParentImage: -
 ParentCommandLine: -
 ParentUser: -
</code></pre></div></div>

<p>Sysmon for Linux doesn’t currently log the hash of the image being executed. If it did, the Sha256 hash
of the binary launched would be the hash for the original binary+shellcode:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">sha256sum</span> /path/to/basic
9cadc2121a28ffe4c9c30afe093794206fdc429b8d56b4b0ddae70ac9dc993cd  /path/to/basic
<span class="c"># the patched binary can be read from the proc filesystem:</span>
<span class="nv">$&gt;</span> <span class="nb">sha256sum</span> /proc/28152/exe
0a21c7e1bb346db045e2d182ec2678f13d431689ff2d0a8ab83e641bd94a5b4f  /proc/28152/exe
</code></pre></div></div>

<p>The patched binary hash would be extremely uncommon, possibly unique, and doesn’t match the original binary on disk.
Depending on the environment, the uncommonness of the hash may be enough to investigate.</p>

<h2 id="conclusions-and-final-thoughts">Conclusions and final thoughts</h2>
<h3 id="sysmon-for-linux-1">Sysmon for Linux</h3>
<p>Sysmon for Linux is looking like a great free monitoring tool for Linux. For people familiar with the Windows version of Sysmon,
it provides a common configuration language that will definitely be useful for admin to maintain, and it’s process creation events
would provide excellent insight into a lot of standard Linux malware attacks.</p>

<p>It is missing a few features that might catch more advanced attacks, such a file hashing and environment variable logging, and I
didn’t explore its performance on a production-sized machine, but it’s a solid monitor to rival using AuditD, or other
free monitors such as <a href="https://github.com/aquasecurity/tracee">Tracee</a>.</p>

<p>One thing not covered in this blog is the ability for an attacker to tamper with Sysmon’s configuration. Sysmon uses eBPF under the hood,
and uses eBPF Maps to store the configuration (What event types to record, processes to ignore, etc.), which <a href="https://www.crowdstrike.com/blog/analyzing-the-security-of-ebpf-maps/">as I’ve written about in the past</a> can be susceptible to tampering by an attacker with admin/root level privileges.
While it is difficult for a program to protect itself for such a highly privileged attacker, it’s worth noting that the attack I described in my previous blog does work on Sysmon without generating any tamper-detection events. This is something the Sysmon team might consider detecting in the future,
but it’s not an easy thing to prevent, and they may rightfully consider it out of scope for a free monitoring tool.</p>

<h3 id="writing-low-level-go">Writing low-level Go</h3>
<p>Thanks to the <a href="https://github.com/awgh/">Awgh</a>, the <a href="http://bloodhoundgang.herokuapp.com/">Bloodhound Slack</a>, and the
<a href="https://github.com/Binject/binjection">Binjection</a> project, I was able to pretty easily get up to speed and write Go code
to do raw syscalls, <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>ing, and other low-level tricks pretty easily. Raw pointer manipulation looks pretty ugly in Go compared
to C, but doing things like argument parsing and safe string manipulation was much nicer in Go.</p>

<h2 id="example-code">Example Code</h2>
<p>I’ve uploaded the source for all the different programs mentioned in this blog here: <a href="https://github.com/pathtofile/commandline_cloaking">https://github.com/pathtofile/commandline_cloaking</a>. With examples in both C and Go, it has these programs:</p>
<ul>
  <li>Basic’s argument printing</li>
  <li>Dodgy’s <code class="language-plaintext highlighter-rouge">argv</code> manipulation</li>
  <li><code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> libraries</li>
  <li>In-memory loading to alter <code class="language-plaintext highlighter-rouge">argv</code> after execution</li>
  <li>ELF Patching to alter <code class="language-plaintext highlighter-rouge">argv</code> after execution</li>
  <li>Sample Sysmon for Linux configuration and setup</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[Commandline Cloaking and Sysmon for Linux]]></summary></entry></feed>